Angular Data Binding
Every component has a template. The ability to communicate between the information in the component and the view is called data binding.
The syntax is HTML with some additional Angular features like loops, conditions and event bindings.
Angular has three main categories of data binding divided in from the component to the view, the way back and two directional.
We can further extract it to this:
In the template to bind data we can use the Template syntax. The template Angular uses to display the view is valid HTML, but with added functionalities.
Template syntax ( or expressions ) are limited JavaScript functions that:
It has its own expression context and therefore it can't access the global namespace.
Since Angular stores the current value and each time change detection runs, it checks the value with the changed data.
If the expression doesn't generate a value, it can't be changed. That's why Template expressions must always return a value. Angular executes the expression and assigns it to a property of a target.
Template expressions cannot refer to anything in the global namespace, ( except undefined ). They can't refer to window or document. That is the reason we can't call console.log().
It's important to point out the following
Template expressions are evaluated every time a change detection cycle is executed.
Because of that, they have to be simple logic and have a fast execution rate.
If a template expression returns a primitive type ( string, number, boolean ) , it should return the same value when called twice in a row.
if a template expression returns an object ( this could be an Array or Date, etc. ) it should return the same object reference ( reference value )
For security reasons, Angular does not support the <script></script> tag.
Template statements are part of the event binding communication between the HTML ( the view ) and the Angular component ( the TypeScript logic ).
Binding is only checked once in production mode!
It is important to point out here the unidirectional data flow in Angular. The component tree is always checked for changes from parent to child.
Interpolation
Changes to title's value are automatically propagated to the DOM ( textContent property ) by Angular change detection.
Interpolation is a one way data binding expression toString() to textContent DOM property.
Interpolation always converts the data to a string.
It binds a template expression to textContent DOM property.
The textContent property accepts only strings ( texts )
Interpolation can also be used as property interpolation.
Property binding
Property data binding is one way data binding.
Parent expression to child component's Input property.
The @Input() is a special Angular decorator that allows us to pass data between parent and a child component. The value is updated every time Angular detects a change in the parent component.
We can also use an alias to connect the HTML target property and use it as simply title property in the .ts component logic.
Input properties are a great way to make components more reusable.
The target name is always the name of a property. The current target property is in valid square brackets and the componentTitle is the parent template expression.
Element properties may be the more common targets, but Angular looks first to see if the name is a property of a known directive!
The binding is not limited only to parent properties, but also includes any valid template expression!
Attributes, Class and Style binding
Attribute binding is similar to property binding, but with a specific syntax. The brackets are still there, but there is a prefix attr. and the name of the attribute.
We can use the attribute interpolation to assign data to HTML elements.
The attribute name has to be valid
Class attributes are useful in certain situations. One such situation could be conditional styling.
The above example works with Truthy expressions.
It could also be a function call. A perfect way to toggle a single class name, but for multiple names it is better to do class binding with [ngClass] - A built- in Angular directive.
It is a perfect way to control CSS classes dynamically.
Even better way to do it is:
Style binding is similar to [ngClass]. It is also an attribute directive built in in Angular.
Event binding
Events are a powerful way to bind data and information. It lets us listen for user interaction. The data in this situation flows from the child component to the parent one.
The brackets again are valid HTML. Every time an event is fired, the method update_data is called.
The update_data is a template statement.
As a difference to our previous examples, this one is binding from the DOM event to the Component. As a result, we are not writing any template expressions. This method could be void and we may not return any value. Template statements have side effects and are not following the Idempotence rule.
These methods are executed only when an event is fired, and not every time change detection runs.
Every time an event is fired and executed, Angular runs a change detection!
Any valid event is a valid Angular event.
If we console.log the $event that is passed with the event we can see all the information that is passed with the Event. That is a lot of information that we may not be interested in. In order to handle this issue we have the template variable.
Angular checks if the name of the event matches an event property of a known directive. If it doesn't it binds to the event of the underlying DOM element. If that fails too, it throws an error "unknown directive".
Unlike Template expressions, template statements and methods can support assignment and can be chained.
Template statements don't have to do anything when an event is fired and this could be used as a way to trigger change detection.
Another way of binding is the custom events.
If we want the child to send information to the parent, Angular provide us with the:
@Output()
The child component uses the @Output() property to raise an event to notify the parent of the change. To raise an event, an @Output() must have the type of EventEmitter, which is a class in @angular/core that you use to emit custom events.
This alone won't do anything, until we actually send the data.
To be used, we have to handle the event in the parent component.
The $event contains the value we are sending with the emit method.
We can use the @Output() and the @Input() together.
Two way data binding
There is a built- in directive in Angular for two way data binding. A syntactic sugar for a property binding and an event binding.
It could be used only with primitive types of data. The direction is from the Component to the Form Element/DOM and back.
Changes to the name property are automatically propagated to the DOM ( value property ) by Angular change detection.
Changes to the input's value are automatically propagated to the component property and that triggers change detection.
The [(ngModel)] could be said as an Input and Output, but shorthand for it. What is happening under the hood is this
When used on input, NgModel binds to the DOM property value.
Keep in mind you may receive an error with the following: $event.target.value. Property ‘value’ does not exist on type ‘EventTarget’ - you may see a solution in the demo project bellow in the two-way-binding.component
In the (ngModelChange) event, the $event is not a DOM event object, but the emitted value of the input. This is what the ngModel directive emits.
In order to get access to the whole $event object, use (change)="onChange($event)"
This two way data binding could be used also between components.
The structure is the same, but it is important to point out that the output event has to be fired by emit().
We don't need to call the emit() when we change the input property in the child when the property is a reference type.
One difference is that with two way binding between components we can bind reference type and not only primitives.
Since change detection runs from top to bottom, we have to emit the child changes to the parent. Changes from parent to child are automated, because when Angular detects a change in the parent component, it updates the value and this forces an update on the child component.
Messing the unidirectional data flow could lead to pretty common mistake and error:
ExpressionChangedAfterItHasBeenCheckedError
When using two-way data binding between components, output property must be named a certain way. If the input property name is name, what must the output property be named nameChange
Host binding
Host binding is a little bit unexplained and usually people don't mention it.
Angular automatically checks host property bindings during change detection, and if a binding changes it updates the host element of the directive.
With the code above we can bind to an element and set the attribute with a value of "true".
We can bind any kind of attribute to the host.
In order to update the state back we have to set a listener.
Demo
Every example could be seen here:
https://github.com/dimitar-stoev/angular-data-binding/
https://stackblitz.com/edit/angular-ivy-sn81eh?file=README.md
https://angular-ivy-sn81eh.stackblitz.io/