Contribute to help us improve!

Are there edge cases or problems that we didn't consider? Is there a technical pitfall that we should add? Did we miss a comma in a sentence?

If you have any input for us, we would love to hear from you and appreciate every contribution. Our goal is to learn from projects for projects such that nobody has to reinvent the wheel.

Let's collect our experiences together to make room to explore the novel!

To contribute click on Contribute to this page on the toolbar.

Components Layer

The components layer encapsulates all components presenting the current application view state, which means data to be shown to the user. The term component refers to a component described by the standard Web Components. So this layer has all Angular components, directives and pipes defined for an application. The main challenges are:

  • how to structure the components layer (see File Structure Guide)

  • decompose components into maintainable chunks (see Component Decomposition Guide)

  • handle component interaction

  • manage calls to the services layer

  • apply a maintainable data and event flow throughout the component tree

Smart and Dumb Components

The architecture applies the concept of Smart and Dumb Components (syn. Containers and Presenters). The concept means that components are divided into Smart and Dumb Components.

A Smart Component typically is a top-level dialog inside the component tree.

  • a component, that can be routed to

  • a modal dialog

  • a component, which is placed inside AppComponent

A Dumb Component can be used by one to many Smart Components. Inside the component tree a Dumb Component is a child of a Smart Component.

Component Tree
Figure 1. Component tree example

As shown the topmost component is always the AppComponent in Angular applications. The component tree describes the hierarchy of components starting from AppComponent. The figure shows Smart Components in blue and Dumb Components in green. AppComponent is a Smart Component by definition. Inside the template of AppComponent placed components are static components inside the component tree. So they are always displayed. In the example OverviewComponent and DetailsComponent are rendered by Angular compiler depending on current URL the application displays. So OverviewComponents sub-tree is displayed if the URL is /overview and DetailsComponents sub-tree is displayed if the URL is /details. To clarify this distinction further the following table shows the main differences.

Smart vs Dumb Components
Smart Components Dumb Components

contain the current view state

show data via binding (@Input) and contain no view state

handle events emitted by Dumb Components

pass events up the component tree to be handled by Smart Components (@Output)

call the services layer

never call the services layer

use services

do not use services

consists of n Dumb Components

is independent of Smart Components

Interaction of Smart and Dumb Components

With the usage of the Smart and Dumb Components pattern one of the most important part is component interaction. Angular comes with built in support for component interaction with @Input() and @Output() Decorators. The following figure illustrates an unidirectional data flow.

  • Data always goes down the component tree - from a Smart Component down its children.

  • Events bubble up, to be handled by a Smart Component.

Smart and Dumb Components Interaction
Figure 2. Smart and Dumb Component Interaction

As shown a Dumb Components role is to define a signature by declaring Input and Output Bindings.

  • @Input() defines what data is necessary for that component to work

  • @Output() defines which events can be listened on by the parent component

Dumb Components define a signature
export class ValuePickerComponent {

  @Input() columns: string[];
  @Input() items: {}[];
  @Input() selected: {};
  @Input() filter: string;
  @Input() isChunked = false;
  @Input() showInput = true;
  @Input() showDropdownHeader = true;

  @Output() elementSelected = new EventEmitter<{}>();
  @Output() filterChanged = new EventEmitter<string>();
  @Output() loadNextChunk = new EventEmitter();
  @Output() escapeKeyPressed = new EventEmitter();

}

The example shows the Dumb Component ValuePickerComponent. It describes seven input bindings with isChunked, showHeader and showDropdownHeader being non mandatory as they have a default value. Four output bindings are present. Typically, a Dumb Component has very little code to no code inside the TypeScript class.

Smart Components use the Dumb Components signature inside the template
<div>

  <value-input
    ...>
  </value-input>

  <value-picker
    *ngIf="isValuePickerOpen"
    [columns]="columns"
    [items]="filteredItems"
    [isChunked]="isChunked"
    [filter]="filter"
    [selected]="selectedItem"
    [showDropdownHeader]="showDropdownHeader"
    (loadNextChunk)="onLoadNextChunk()"
    (elementSelected)="onElementSelected($event)"
    (filterChanged)="onFilterChanged($event)"
    (escapeKeyPressed)="onEscapePressedInsideChildTable()">
  </value-picker>

</div>

Inside the Smart Components template the events emitted by Dumb Components are handled. It is a good practice to name the handlers with the prefix on* (e.g. onInputChanged()).