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 eventflow throughout the component tree
The architecture applies the concept of Smart and Dumb Components (syn. Containers and Presenters). The concept means that components are devided into Smart and Dumb Components.
A Smart Component typically is a toplevel 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
subtree is displayed if the URL is /overview
and DetailsComponents
subtree 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 emited 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 |
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()
).