📜 ⬆️ ⬇️

The transition from AngularJS to Angular: life after AngularJS (3/3)


In the final part of the story about the migration to Angular, we will share the selected places of our internal documentation, helping our developers to get used to the new framework. It will be about the features of the new logic of compiling components, Change Detection and the concept of transkluda. These are actual conventions used right now when working with Angular. Well, in the end - a few links to English-language articles and videos that we recommend to colleagues.


The previous series: first, about the preparation of migration and the second, about the features of work in a hybrid mode .


Compiling an application and AoT


One of the major changes in Angular is the logic of compiling components.


In AngularJS, we took the HTML of the first component (the highest one), stuffed it into the DOM, waited for the browser to either sparce it, or build a DOM fragment, or paste it into the DOM, get a link to the resulting DOM piece, parsili in it Angular things (directives , components), repeated for each component found.


In Angular, a compilation of templates in js / ts is used in 2 versions - JiT and AoT. At the start of the application (in JiT mode), the frame takes and compiles all (in general, all) the templates of all components from the string from HTML to JS code, which will create the desired piece of DOM. Those. Parsing and building a DOM fragment is completely independent of the browser, Angular does it itself. After that, starting from the top component, it simply starts to execute the corresponding ready-made JS code, creating DOM pieces and immediately inserting them into the right places of the tree. After insertion, additional parsing is not required, since the compiler has already disassembled everything.


AoT compilation is performed on the server / locally during the assembly, takes some time (not very much) and on output gives a heap of data for all components in the form of TS files in a separate directory. They cling to the main application, and TypeScript compilation is already taking into account the copied templates.


In AoT mode, templates are compiled into TypeScript, not JS. This gives the static typing of all templates. You skipped a variable, indicated it as private, or you don't manage it in an inappropriate way (you try to push an object into an input of a component that is waiting for a string) - you get an error when you build it. But this is all only in build mode for production, since because of the ton of added typed code, the build (compiling ts -> js) slows down a bit, and AoT mode is turned off during development (waiting for happy times of multi-threaded compilation or some magic to use AoT in developer mode without suffering).


Links to the topic: the first , second and third .


Change detection (CD)


In Angular, there is no longer a global digest cycle (as well as a list of parameters that may change, and they need to be checked), native binding for events ( addEventListener and the like) is used to the maximum, but the change detection itself has not gone anywhere.


How does Angular know that something has happened, without separate directives for events and special services for timeouts / intervals?


From the Dart language, which has a separate version of Angular, borrowed a useful concept of zones, which allows you to be aware of when the execution of some code began in a particular zone, and when it ended taking into account asynchronous calls. This all took shape in the zone.js libu, which allows you to create / operate with such zones and is actively used inside the Angular engine. To keep abreast of asynchronous events, the zone.js monkey patch (replaces with its version) the corresponding methods — addEventListener , setTimeout , setInterval , Promise methods, and the like. This also eliminated the need for separate directives for binding on events / additional services. Accordingly, at the end of any event that is caught in zone.js, Angular pulls the change detection to synchronize the model with the DOM. In dev mode, the twitch twitches 2 times - the second to make sure that nothing has changed after the first one, or give a damn (error in the console) if it has changed; in production always exactly once (no more than ten digest calls of the cycle and the corresponding error).


How does change detection pass if there is no global pool of parameters to be checked?


This is now the work of the components. Each of them may have a set of parameters that are somehow used in DOM or special binding ( HostBinding , for example). Such parameters are stored in a separate list within the component (and are not known anywhere else).


When Angular pulls the global change detection, then each component (from top to bottom on the tree) itself checks its parameters. In this case, the change detection mechanism may be uncoupled from the component altogether, then its parameters will not be checked in principle. Or it can be configured to check only changes to its inputs ( ChangeDetectionStrategy.OnPush ), then not only will it not check anything, but it will also completely cut off the launch of checks of the components / directives nested in it. Thus, correctly scattering OnPush , we can exclude entire branches of components from checking for each tick.


It is also possible to execute a piece of code outside or (obviously) inside the Angular zone (in which it catches events and all that). This is useful if we are clinging to a third-party libu, which chchakh all these troubles, it works on its own; and we do not want every event or timeout inside our system to detect all over the component tree. In order to tell the Angulyar later that we have updated the data (obtained from this, which was launched outside the zone he was listening to), we explicitly prescribe the execution of some action (for example, change the local property of the component) in the angular zone.


detectChanges and markForCheck


If we have a component with a detached CD or on a component (on the current or higher in the tree), ChangeDetectionStrategy.OnPush written, and we have some changes that are not picked up automatically, that is, 2 methods of the local ChangeDetectorRef service - detectChanges and markForCheck :



 if (!this.changeDetectorRef["destroyed"]) { this.changeDetectorRef.detectChanges(); } 

When what to use:



ng-content is NOT the same as ng-transclude


The concept of transcular in Angular has changed a bit (influenced by the way it is done in web components and the features of the angular engine / compiler). Externally, ng-content looks and has the same capabilities as ng-transclude : throw something into the component, throw it into a certain place ( select attribute).


But there is one minor difference - there is no possibility to specify the default content inside ng-content - and one very important one: the content thrower is responsible for initializing the content that is being pushed to ng-content , and not the component that contains the ng-content tag .


Explanation of an example:


 @Component({ ... template: ` Visible: {{ visible }} <ng-content *ngIf="visible"></ng-content> `, }) class ProjectionComponent implements OnInit { public visible = false; public ngOnInit() { window.setTimeout(() => this.visible = true, 1000); window.setTimeout(() => this.visible = false, 2000); } } @Component({...}) class ChildComponent implements OnInit, AfterViewInit, OnDestroy { public ngOnInit() { console.log("Child component ngOnInit"); } public ngAfterViewInit() { console.log("Child component ngAfterViewInit"); } public ngOnDestroy() { console.log("Child component ngOnDestroy"); } } @Component({ ... template: ` <vim-projection> <vim-child-component></vim-child-component> </vim-projection> `, }) class ParentComponent {} 

Main focus on ngIf in ProjectionComponent and console log:


 //    `Visible`   `ProjectionComponent` Child component ngOnInit //   Child component ngAfterViewInit //  2  // … 

In the first second, despite *ngIf="false" , the ChildComponent was initialized (the OnInit hook worked), and OnDestroy did not work after 2 seconds, although it OnDestroy like the components were hidden.


Those. regardless of what happens in the component containing ng-content , everything that is thrown there will be initialized (called OnInit hooks). This may be important if there are any queries in the component initialization, heavy processing and the like.


If you need to hide such content, then the one who pushes it should do:


 <vim-projection> <vim-child-component *ngIf="visibleInParent"></vim-child-component> </vim-projection> 

or use an interesting way to get around this restriction using a TemplateRef:


 <div *ngIf="condition" class="smth1"> <ng-container *ngTemplateOutlet="contentTpl"></ng-container> </div> <div *ngIf="!condition" class="smth2"> <ng-container *ngTemplateOutlet="contentTpl"></ng-container> </div> <div *ngIf="condition2" class="smth1"> <ng-container *ngTemplateOutlet="contentWithSelectorTpl"></ng-container> </div> <div *ngIf="!condition2" class="smth2"> <ng-container *ngTemplateOutlet="contentWithSelectorTpl"></ng-container> </div> <ng-template #contentTpl><ng-content></ng-content></ng-template> <ng-template #contentWithSelectorTpl><ng-content select="[some-attribute]"></ng-content></ng-template> 

Of the restrictions - to insert such ng-content can only be in one place at a time.


Also, ng-content does not allow to specify several identical slots in the template, over which the condition hangs when to output them.


 <!--   --> <div *ngIf="smth" class="smth1"> <ng-content></ng-content> </div> <div *ngIf="!smth" class="smth2"> <ng-content></ng-content> </div> <!--    --> <div *ngIf="smth" class="smth1"> <ng-content select="[some-selector]"></ng-content> </div> <div *ngIf="!smth" class="smth2"> <ng-content select="[some-selector]"></ng-content> </div> 

Links to useful materials


In Google, a lot of things on angular 2+, you can safely search for any topics that will be immediately / on the docks are not clear. The people are very active from the release (and even before it) took the frame apart and figured out how it works.


Here are some useful links:


ElementRef, TemplateRef, ViewContainerRef


Structural Directives 1


Structural Directives 2


ViewChildren, ContentChildren, QueryList


Dynamic components 1


Dynamic components 2


Change detection


ng-template and microsyntax of structural directives


And finally, as always, we remind that we are always looking for cool developers (not just Angular ).


')

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


All Articles