📜 ⬆️ ⬇️

Understanding the syntax of templates in Angular2


Many of the first time they saw the syntax of the Angular2 templates begin to lament, saying that they did some horror, it really wasn’t like in Angular1 at least. Why it was necessary to introduce this variety of brackets, stars and other nonsense! However, upon closer inspection, everything becomes much easier, the main thing is not to be afraid.

Since the templates in AngularJS are an integral part of it, it is important to understand them at the very beginning of the acquaintance with the new version of this framework. At the same time we will discuss what advantages this syntax gives us in comparison with angular 1.x. And it would be best to consider this with small examples.

This article is largely based on the materials of these two articles:
')


In order to simplify the flow of material, let's see. By AngularJS I will mean the whole Angular 1.x branch, while by Angular2 - the 2.x branch.

Also, thanks to Mr. Bronx for the valuable addition that I have included in the text of the article.

Note: Weekend evening, because of typos, etc. inform in lichku. Much grateful and pleasant reading.


Binding properties of elements


In the case of simple data output, you will not feel the difference, however, if you decide to transfer part of the state as the value of any attribute of elements, you can already observe interesting solutions.

Let's refresh in memory how the binding on the attributes in AngularJS. To do this, we usually directly forward expressions (interpolated when compiling a template) directly into an element attribute, or use one of a number of directives. For example, ngValue to set the value of the value the input parameter.

Here is an example of how this works in AngularJS:

 <input ng-value="expression" /> 

Just do not forget that we can simply interpolate the result of the expression directly as the value of the argument:

 <input value="{{expression}}" /> 

Note the interesting feature. The second option is avoided by many, as you can see the intermediate state before the angular interpolates the values. However, the first option uses directives. That is, in order for everything to be all right, nice and comfortable, we need to follow the directive for each property of all elements. Agree, not very convenient. Why not add some notation for an attribute that would tell the angular substitute a value on it. And it would be nice that the syntax was valid. And they added, now for this you just need to wrap the attribute of interest (any attribute) in square brackets - [] .

 <input [value]="valueExpression" [placeholder]="placeholderExpression" /> 


In essence, the [] syntax is nothing more than a shorthand notation for bind-prop . If you remove the sugar, the example above will be written as:

 <input bind-value="valueExpression" bind-placeholder="placeholderExpression" /> 


However, remember how you can change the attributes in Javascript:

 element[prop] = 'value'; 


it is from here that these square brackets are taken. We explain that the values ​​will be mapped directly on the properties of the elements. That is, instead of the ng-bind-html directive, we can simply bandage the result of an expression in [inner-html] . Or instead of ng-hide we can use the [hidden] property of the element itself. This significantly reduces the number of angular-specific things that you need to know and brings us closer to the DOM, while we still have an abstraction from it.

Well, to close this question is to indicate that we also have the opportunity to map the interpolated value, just as it was in AngularJS:

 <input value="{{ valueExpression }}" placeholder="{{ placeholderExpression }}" /> 



Developments


In AngularJS we could subscribe for events of elements using special directives. Just as in the case of properties, we have to deal with a whole bunch of possible events. And for each event I had to make a directive. Perhaps the most popular of these directives is ngClick :

 <button ng-click="doSomething($event)"> 

Considering that we have already solved such a problem for the properties of the elements, it is probably also worth solving this problem? That is exactly what they did! In order to subscribe to an event, it is enough to register an attribute using the following syntax: (eventName) . Thus, we have the opportunity to subscribe to any event generated by our element, without needing to write a separate directive:

 <button (click)="doSomething($event)"> 


As with the binding properties, it is also sugar for on-writing. That is, the example above could be written as:

 <button on-click="doSomething($event)"> 


And the look of the parentheses is obtained from the associations with the installation of event handlers in javascript:

 element.addEventListener('click', ($event) => doSomething($event)); 


It is also worth noting an important difference in behavior when compared with the binding of events in AngularJS. Now Angular2 will throw us errors in case of a call to a nonexistent method. As if we were calling the code directly in javascript. I think many of those who have been stupid bugs because of typos in this context will be happy. In some cases, this adds certain inconveniences and therefore the Elvis operator was added, which we will talk about later.


Bilateral binding


There is a widespread belief that double-sided binding is bad, and that this is the main sin of the angular. This is not entirely true. The problem with bilateral binding in AnugularJS was that it is used everywhere, not giving developers an alternative (perhaps the situation with this will soon change).

Yet sometimes there are cases when bilateral binding makes the development great, especially in the case of forms. So how is this implemented in Angular2? Let's think about how to organize bilateral binding with one-sided binding of the property of elements and binding of events:

 <input type="text" [value]="firstName" (input)="firstName=$event.target.value" /> 

Again, not very convenient. Therefore, in Angular2 there is also syntactic sugar using ngModel . The result will be identical to what we quoted above:

 <input type="text" [(ngModel)]="firstName" /> 

Local variables


Local variables are used to transfer data between elements within the same template. The closest analogy in AngularJS, perhaps, is the access to form elements by name via ngForm. Of course, this is not an entirely correct comparison, since it works only at the expense of the ngForm directive. In Angular2, you can use a reference to any object or DOM element within the template element and its descendants using local variables # .

 <video #movieplayer ...> <button (click)="movieplayer.play()"> </video> 

In this example, we can see how, through the movieplayer variable movieplayer we can access the media element's API directly in the template.

In addition to the # symbol, you can also declare variables using the var- prefix. In this case, instead of #movieplayer we could write var-movieplayer .

Thanks to local variables, we no longer need to make new directives whenever actions on some elements have to change something in others. For example, in the example above, we can quickly add a button that starts watching a video. This is actually the main conceptual difference between Angular2 and AngularJS, less useless micro-directives, a greater focus on components.


Asterisk (*)


The * symbol causes the most questions. Let's see why he needed. To understand the reasons for adding this symbol, we should remember about such an element as the template .

The template element allows us to declare a piece of DOM that we can initialize later, which gives us better performance and more efficient use of resources. Something like a documentFragment in the context of HTML.

Perhaps it will be easier to show why it is necessary with an example:

 <div style="display:none"> <img src="path/to/your/image.png" /> </div> 

In this small example, we can see that the block is hidden ( display:none ). However, the browser will still try to download the image, even if it is not needed. If there are a lot of such things on the page, this can adversely affect the overall performance of the page.

The solution to this problem is to use the template element.

 <template> <img src="path/to/your/image.png" /> </template> 

In this case, the browser will not load the image until we initialize the template.

But back to our sheep. Using the * character in front of an element directive will allow the angular to compile the element into a template when compiling. It's easier to look at an example:

 <hero-detail *ngIf="isActive" [hero]="currentHero"></hero-detail> 

This template will be transformed into:

 <template [ngIf]="isActive"> <hero-detail [hero]="currentHero"></hero-detail> </template> 

It should now become clear that this symbol provides syntactic sugar for higher performance when using conditional directives like ngFor , ngIf and ngSwitch . It is logical that there is no need to create an instance of the hero-detail component while isActive not true.

Pipe


Payp is a direct analogue of filters from AngularJS. In general, the syntax of their use has not changed much:

 <p>My birthday is {{ birthday | date:"MM/dd/yy" }} </p> 

Why did it take to change the name from the already familiar filters to the new pipes - a separate question. This was done to emphasize the new mechanics of the filters. Now these are not synchronous filters, but asynchronous pipes (by analogy with unix pipes).

In AngularJS, filters run synchronously on every $ digest cycle. This is required by the mechanism for tracking changes in AngularJs. In Angular2, however, tracking changes takes into account data dependencies, so it allows you to optimize a number of concepts. There was also a separation between stateful and stateless pipes (while AngularJS films were considered stateful).

Stateless pipes, as the name implies, do not have their own state. These are pure features. They are executed only once (or if the input data has changed). Most pipelines in Angular2 are stateless pipes. This can significantly increase productivity.

Stateful pipes, on the contrary, have their own state and they are often executed due to the fact that the internal state may change. An example of such a pipe is Async . It receives a promise at the input, subscribes to the changes, and returns the value returned.

 //   TypeScript,  babel  stage-1,  ,   @Component({ selector: 'my-hero', template: 'Message: {{delayedMessage | async}}', }) class MyHeroAsyncMessageComponent { delayedMessage = new Promise((resolve, reject) => { setTimeout(() => resolve('You are my Hero!'), 500); }); } // ? ,   TypeScript   . 

In this example, the my-hero component displays Message: You are my Hero! only after the delayedMessage is delayedMessage .

In order to make a stateful pipe, we must explicitly declare it in the metadata thereof. Otherwise, Angular2 will consider it stateless.

Elvis operator


In AngularJS, we could make calls to anything completely painless, which often resulted in very insidious bugs and made it difficult to debug. You probably had to deal with typos in ngClick , in which the code did not perform the required actions and the framework did not give us any hints about what went wrong. In Angular2 we will finally get errors! However, this solution may not appeal to everyone without additional sugar.

In Javascript, we often have to check for any properties. I think we all wrote something like this:

 if (cordova && cordova.plugins && cordova.plugins.notification){ // use cordova.plugins.notification } 

By doing such checks, we certainly want to avoid this:

 TypeError: Cannot read property 'notification' of undefined. 

To solve this problem, the Elvis operator was introduced as a shortened version of the ternary operator. You could see similar in coffeescript. Angular2 solved this problem using the same operator, but at the template level:

 <button (click)="employer?.goToWork()">Go To Work</button> 

This entry indicates that the employer property is optional, and if it is null, the rest of the expression is ignored. If you remove the sugar, then this entry will look like this:

 <button (click)="employer === undefined ? : employer.goToWork()">Go To Work</button> 

If we did not use this operator and at the same time would not do such a check, then in this situation we would get a TypeError .

As in coffescript, this operator can be used as many times as possible within a single expression, for example: a?.b?.c?.d

findings


The developers of Angular2 have done a great job in order to make the pattern syntax more flexible and powerful. Yes, some things take a little time to adapt, but in general, over time, it all seems more than natural. And most importantly, not so scary as it seems at first glance.

Source: https://habr.com/ru/post/274743/


All Articles