Property and Attribute Binding in Angular
In all the previous articles we’ve been using interpolation to put data inside a template. We even provided values to href with the help of interpolation. Interpolation is a very good syntax to put values. But inside the start tag when we add an attribute or property, we can use the property binding syntax of Angular. Under the hood Angular converts interpolation to property binding.
The syntax of property binding is *[property_name]="component_variable"*. Inside the square bracket we put the name of the property. But what is a property? Let's explain it with how a browser works.
When a browser gets HTML it parses it and creates a tree of native browser DOM elements. Each of these DOM elements are objects. These objects can have many properties. What we write as pure HTML attributes in an HTML source file does not always have one-to-one mapping with the properties of these DOM element objects. Some attributes from the HTML source are stored as properties of the corresponding DOM element objects and some end up getting stored inside another DOM property called attributes that we retrieve by calling the getAttribute() method on the elements. There are some HTML attributes that are stored as a DOM property with a different name.
When we are talking in terms of HTML source we only see attributes inside the start tags. But remember these are the attributes of HTML. When we talk in terms of DOM objects then there are properties and attributes which may or may not have a one-to-one relation with the attributes in the HTML source. Again, the value you provide in HTML sometimes gets converted to other native types.
But, why are we talking about attributes and properties? What good is in there to know the distinctions? If I answer from the perspective of Angular only then the answer is simple: if you use property binding syntax for what are attributes for the DOM, you will get errors and thus your application will not run. Now, let's talk from the perspective of DOM element objects. DOM element attributes and properties are taken from the HTML source attributes and if they have a one-to-one relation with DOM element properties, then if you change the property value or it is changed, the corresponding DOM attribute is not changed. This is important because the changing attribute value at runtime that you set in HTML also changes the value of the DOM element property. But things get funny when you change the value of a DOM property that had a default value in the HTML source code that will not reflect this change in the DOM attribute (and thus breaking the views). So, it is better to stick with properties instead of attributes.
Let's get back to code. We know that href is a property and we can convert our HTML template from interpolation to property binding. Let's try:
<h1>My profile</h1>
<b> Hello World </b> <br/>
My name is Md. {{ name }} <br/>
I am a {{ profession }}. <br/>
Do not forget to visit me at: {{ website }} <br/>
My Social Links:
<ol>
<li *ngFor="let link_obj of social_links">
<a *ngIf="link_obj.is_active; else elseTemplate" [href]="link_obj.link"> {{ link_obj.title }} </a>
<ng-template #elseTemplate>
<s> This link is not active </s>
</ng-template>
</li>
</ol>
Go to the browser to see that everything is as before. So, it's time to do the same to the HTML attributes that do not have one-to-one relation with a DOM property or are always attributes.
Let's convert our list to table so that we can experiment with attributes and properties.
<h1>My profile</h1>
<b> Hello World </b> <br/>
My name is Md. {{ name }} <br/>
I am a {{ profession }}. <br/>
Do not forget to visit me at: {{ website }} <br/>
My Social Links:
<table border="1">
<tr *ngFor="let link_obj of social_links">
<td>
<a *ngIf="link_obj.is_active; else elseTemplate" [href]="link_obj.link"> {{ link_obj.title }} </a>
<ng-template #elseTemplate>
<s> This link is not active </s>
</ng-template>
</td>
<td>
{{ link_obj.link }}
</td>
<tr>
</table>
colspan of the td tag is an attribute—not a property. Let's try to do property binding with this.
<h1>My profile</h1>
<b> Hello World </b> <br/>
My name is Md. {{ name }} <br/>
I am a {{ profession }}. <br/>
Do not forget to visit me at: {{ website }} <br/>
My Social Links:
<table border="1">
<tr *ngFor="let link_obj of social_links">
<td [colspan]="2">
<a *ngIf="link_obj.is_active; else elseTemplate" [href]="link_obj.link"> {{ link_obj.title }} </a>
<ng-template #elseTemplate>
<s> This link is not active </s>
</ng-template>
</td>
<td>
{{ link_obj.link }}
</td>
<tr>
</table>
Go to the browser and you will be welcomed with an empty screen. When you see such behavior you be sure that something broke in your Angular application due to some error or exception. Press F12 to go to the browser console. You see an error message like below:
Error: Template parse errors:
Can't bind to 'colspan' since it isn't a known property of 'td'. ("
<table border="1">
<tr *ngFor="let link_obj of social_links">
<td [ERROR ->][colspan]="2">
<a *ngIf="link_obj.is_active; else elseTemplate" [href]="link_obj.link"> "): ng:///AppModule/AppComponent.html@8:12
Binding to colspan is not possible as it is not a known property for td to Angular. So, how do we solve this problem? What happens if we fall back to interpolation? Let's try:
<td colspan="{{ 2 }}">
Same error. I told you that Angular converts interpolation of attribute values inside the HTML start tag to property binding. Then how on earth do we set attributes dynamically in Angular?
It's not necessary in the Angular world that every property has to be a property of a DOM element. It can also be associated with a component or directive along with DOM elements.
Angular provides few exceptions to the rule of always binding to properties. You can add attributes to elements with the help of attribute binding with [attr.attribute_name]. Notice that attribute binding is also done with property binding syntax. attr is special for angular property binding. Let's try it with colspan.
<td [attr.colspan]="2">
Go to browser to see that errors are gone and your application is working again. But there is not much visual indication that we got two columns. Let's modify our code.
<table border="1">
<tr *ngFor="let link_obj of social_links">
<td [attr.colspan]="2">
<a *ngIf="link_obj.is_active; else elseTemplate" [href]="link_obj.link"> {{ link_obj.title }} </a>
<ng-template #elseTemplate>
<s> This link is not active </s>
</ng-template>
</td>
<td>
{{ link_obj.link }}
</td>
<tr>
<tr>
<td>col 1</td>
<td>col 2</td>
<td>col 3</td>
</tr>
</table>
It looks good now. Let's put colspan to 3 to make is more visible.
<td [attr.colspan]="4">
It's more apparent now. Go to the browser to see what is happening.
Conclusion
Be careful about what are properties and what are attributes. Also keep in mind that not only DOM element can have properties—directives and components can also have them.
Previous article: Working With Objects, ngFor and ngIf in Angular |
About the Author
My name is Md. Sabuj Sarker. I am a Software Engineer, Trainer and Writer. I have over 10 years of experience in software development, web design and development, training, writing and some other cool stuff including few years of experience in mobile application development. I am also an open source contributor. Visit my github repository with username SabujXi.
Recent Stories
Top DiscoverSDK Experts
Compare Products
Select up to three two products to compare by clicking on the compare icon () of each product.
{{compareToolModel.Error}}
{{CommentsModel.TotalCount}} Comments
Your Comment