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.
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 Components | Dumb Components |
---|---|
contain the current view state |
show data via binding ( |
handle events emitted by Dumb Components |
pass events up the component tree to be handled by Smart Components ( |
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.
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
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.
<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()
).