Angular Data Flow

This post lists the ways you can send data around in a angular application which will be referred to as data flow.

Using interpolation, data in a component’s properties can be output to the HTML template. But then there is also property-, class- and other bindings such as two-way binding (Banana in a Box). Data can be exchanged between child (@ViewChild, @ViewChildren, @ContentChild, @ContentChildren decorators) and parent components. Events can be sent (EventEmitter). Forms can be used to submit data with validation. But why are forms needed in the first place, when we have data binding?

To a beginner all these concepts are confusing, this post lists all interactions and explains their major benefit and when to use them.

Interpolation

The value stored in a component’s property can be output on a template using the interpolation syntax.

import { Component } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'cockpit';
}
{{title}}

Interpolation also allows you to perform calculations and function calls.

{{10 + 20}}
{{functionCall()}}

The value can be placed anywhere in the template and is rendered as is. If you want to put a value from a component into an attribute of a DOM element or child component, do not use interpolation but use property binding.

Interpolation using getters

If your component contains a getter

import { Component } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'cockpit';
username = 'username';

get user(): string {
return this.username;
}
}

interpolation also works with the getter:

{{user}}

Getters and Change Detection

As angular uses an elaborate change detection mechanism, the getter should not return a new object each time it is called but instead should return an immutable object which remains identical unless the data actually did change.

For an object to be immutable, the object is never modified but recreated from scratch each time data changes. If the data stays the same, the getter will return the same immutable object instance instead of returning a new string or a new object every time it is called even if the data is the same.

That way, using immutable objects, change detection can compare objects returned by getters correctly an only redraw that part of the page, when a new object is present, which means that data did actually change.

Refrain from constructing new objects on the fly and returning them from a getter as this throws change detection into a loop and causes angular to perform unnecessary work when rendering the page.

Naming fields and their get and set methods

Coming from a Java background, where getters and setters are also available, the expectation is the following syntax:

public class MyClass {
private String fieldname;

public getFiendname() {
return this.fieldname;
}

public setFieldname(const String fieldname) {
this.fieldname = fieldname;
}
}

This means, getters and setters are in essence normal member functions or methods. They have the conventional set and get prefixes to make them immediately identifiable as getters and setters. This convention is also mandated by the definition of a Java bean.

In Angular/Typescript, the keywords get and set exist and they are special syntax for getters and setters. The downside of this explicit notation is that getters and setters basically use the identifier that would be used for the field itself! There is a naming conflict here! How to resolve that conflict? This depends on the conventions you agree upon in your project. There is an underscore convention which is outdated apparently. Then you can choose a different name for the field and use the compact identifier for the getters and setters. Ultimately there is no definite solution to the name conflict issue.

Event Binding – Calling methods in components from the UI

Event Binding uses braces around attributes like this: (attr)=”eventHandlerMethod()”

You can bind to the click event of a button and call a function in your component.

login() {
this.authService.login();
}

logout() {
this.authService.logout();
}

In the template:

<button (click)="login()">Login</button>
<button (click)="logout()">Logout</button>

Another example of EventBinding is to bind a method of a component to the submit event of an Angular template-driven form (https://angular.io/guide/forms).

<form (ngSubmit)="onSubmit()" #heroForm="ngForm"></form>

When the form is submitted using the submit button, the onSubmit() method in the component is called.

@Input – Property Binding – Passing data from a parent to a DOM element or child component

Property Binding uses square brackets like this: [attr]=”value”.

As the template of a component can contain other components, a parent child hierarchy is formed. The parent, the component owning the template can put a value into an attribute of a child component. The child component can make use of the attribute by mapping that attribute to a field in it’s own class.

The mapping is created by annotating a field using the @Input() annotation. Now that field is mapped to an attribute of the same name in the template. To pass a value to the child, the attribute has to be surrounded by square brackets (= the binding operator).  The binding operator takes the value inside the square brackets and tries to find a matching field in the component. It will then set the value to that field. 

Imagine a DetailsComponent that is supposed to display a model object. An example could be a UserDetailsComponent displaying a user domain model.

@Input()
user: User;

The template of the parent component contains:

<user-details [user]="user"></user-details>

In this example, the user variable used as value can be created by a *ngFor directive or can come from somewhere else. It will be supplied by the parent component in most cases.

The NgClass Directive

The NgClass directive can add or remove CSS classes to a DOM element. It expects an object as a parameter. The object contains the intividual CSS classes as properties and boolean values that either add that particular class when the boolean value is true or remove that class when the value is false. Instead of hardcoded booleans, the boolean value can be returned from a method in the component.

<div [ngClass]="{selected: selected(), alarm: alarm()}"></div>

Two-way Data Binding

Two-way Binding uses square brackets containing braces, containing the value like this: [(attr)]=”value”

The banana-in-a-box syntax is used to send data from the frontend to the component and also from the component to the ui.

<input type="text" class="form-control" id="name" required       [(ngModel)]="model.name" name="name">

The code snippet above contains an input field from a template-driven form. The two-way binding is applied to the ngModel directive which is defined in the FormsModule. ngModel connects input from the template to the component.

The assignment to the Two-way data bound ngModel is using model.name as a value. model.name refers to the name field in the model field in the component that houses the template form.

This is a sentence that nobody will understand ever, so let me rephrase it. model.name refers to application defined fields. model is not a special keyword. It use a convention to name the target object that the form input is stored into, model. model.name is an example for a field of the model, which is called name. The two-way binding to model.name will store the user input into the model field of the component and inside the model it will store the input into the name property of the model object. If the object you store your data in is not called model, that is fine too, just specify the correct names in the two-way binding value.

Sending Events from a child to a Parent

Difference between a Content Child and a View Child

Angular ultimately renders a tree of components. A tree data structure defines nodes and their children. Children are nested into their parent nodes. In Angular components are nested into components.

There are two ways to nest components in Angular:

  1. View Children
  2. Content Children

Nesting a child component into a parent component is done by using the child components selector / tag in a template.

The distinction between view and component child is made by where, in which template the child component is used.

If the child component is used directly in the parent’s template, then the nesting is view nesting and the child is a ViewChild.

If the parent component is used in some “third-party” template and child components are used inside the opening and closing tag of the parent in the same arbitrary “third-party” template, then this is referred to as content nesting and the children are content children.

An example for view nesting is a fixed combination of components. A ColorPicker component might have a nested view child that draws the color space into a square or circle for the user to click into to select color in a explorative manner. Lets call this component ColorField. The ColorPicker component might have another nested view that represents the current color using sliders for the color’s red, green, blue and alpha values. Let’s call this component ColorCoordinates. In ColorPicker’s own template, the ColorField and ColorCoordinates components are  used, which by definition makes them view children.

An example for content nesting is a Tab component which is used in the template of a Dashboard component. As content children, the Tab component will have several TabPane components, that the user can switch between. Instead of inserting TabPane components directly into the Tab component’s template as view children, the TabPane components are added as nested tags to the Tab-tag in the Dashboard component’s template. This allows the user of the Dashboard component to add as many TabPanes to the Tab component as they want or need. Applying this idea further, the content on the TabPane components again is added directly in the Dashboard component’s template which makes it another example for content nesting.

A component can have View and Content children at the same time. A component can also have neither View nor Component children at all.

The interesting question is, when a component has ViewChild or Content components, what can it do with those? The answer is that the component class will have references to those children and call call methods on those children components inside it’s own code.

Sometimes, classes annotated with the @Component decorator are refered to as the component’s controllers. Having View- or Content-Children is a way for the parent component’s controller to access the controllers of the component’s children.

Leave a Reply