ng new ivy-internals
tsconfig.json
file located in the new project folder: "angularCompilerOptions": { "enableIvy": true }
ngc
command in the newly created project folder: node_modules/.bin/ngc
dist/out-tsc
. For example, take a look at the following fragment of the AppComponent
template: <div style="text-align:center"> <h1> Welcome to {{ title }}! </h1> <img width="300" alt="Angular Logo" src="…"> </div>
dist/out-tsc/src/app/app.component.js
: i0.ɵE(0, "div", _c0); i0.ɵE(1, "h1"); i0.ɵT(2); i0.ɵe(); i0.ɵE(3, "img", _c1); i0.ɵe(); i0.ɵe(); i0.ɵE(4, "h2"); i0.ɵT(5, "Here are some links to help you start: "); i0.ɵe();
src/app/app.component.html
), compile it again and see how the changes made to it affect the generated code.i0.ɵE
and i0.ɵT
var i0 = require("@angular/core");
i0
is just an Angular kernel module, and all of these are functions exported by Angular. The letter ɵ
used by the Angular development team to indicate that some methods are intended solely to support the internal mechanisms of the framework, that is, users should not call them directly, since the API’s invariance of these methods is not guaranteed when new versions of Angular are released (in fact, I would say that their API is almost guaranteed to change).elementStart
.ɵT
is text
, the name of the method ɵe
is elementEnd
. Armed with this knowledge, we can "translate" the generated code, turning it into something that will be easier to read. Here is a small piece of this "translation": var core = require("angular/core"); //... core.elementStart(0, "div", _c0); core.elementStart(1, "h1"); core.text(2); core. (); core.elementStart(3, "img", _c1); core.elementEnd(); core.elementEnd(); core.elementStart(4, "h2"); core.text(5, "Here are some links to help you start: "); core.elementEnd();
<div style="text-align:center"> <h1> Welcome to {{ title }}! </h1> <img width="300" alt="Angular Logo" src="…"> </div>
core.elementStart()
.core.elementEnd()
calls.core.text()
calls.elementStart
and text
methods is a number, the value of which increases with each call. It is probably an index in some kind of array in which Angular stores references to the created elements.elementStart
method. After studying the above materials, we can conclude that the argument is optional and contains a list of attributes for the DOM node. You can check this by looking at the _c0
value and finding out that it contains a list of attributes and their values for the div
element: var _c0 = ["style", "text-align:center"];
AppComponent.ngComponentDef
, a static property that contains all the metadata about the component, such as CSS selectors, its change detection strategy (if specified), and the template. If you feel adventurous, you can now figure out how it works, although we'll talk about it below. import { Component } from '@angular/core'; @Component({ selector: 'manual-component', template: '<h2><font color="#3AC1EF">Hello, Component</font></h2>', }) export class ManualComponent { }
@component
decorator, creates instructions, and then draws it all in the form of a static property of the component class. Therefore, in order to imitate the activity of Ivy, we remove the @component
decorator and replace it with the ngComponent
static property: import * as core from '@angular/core'; export class ManualComponent { static ngComponentDef = core.ɵdefineComponent({ type: ManualComponent, selectors: [['manual-component']], factory: () => new ManualComponent(), template: (rf: core.ɵRenderFlags, ctx: ManualComponent) => { // }, }); }
ɵdefineComponent
. Metadata includes the type of component (used earlier for dependency injection), the CSS selector (or selectors) that will call this component (in our case, manual-component
is the name of the component in the HTML template), a factory that returns a new instance component, and then the function that defines the pattern for the component. This template displays a visual representation of the component and updates it when the component properties change. In order to create this template, we will use the methods that we found above: ɵE
, ɵe
and ɵT
. template: (rf: core.ɵRenderFlags, ctx: ManualComponent) => { core.ɵE(0, 'h2'); // h2 core.ɵT(1, 'Hello, Component'); // core.ɵe(); // h2 },
rf
or ctf
parameters provided by our template function. We will come back to them. But first, let's look at how to bring our first homemade component to the screen.ɵrenderComponent
. All that needs to be done is to check that the index.html
file has an HTML tag that corresponds to the element selector, <manual-component>
, and then add the following to the end of the file: core.ɵrenderComponent(ManualComponent);
Hello, Component
, we are going to allow the user to change the part of the text that comes after Hello
.name
property and the method to update the value of this property to the component class: export class ManualComponent { name = 'Component'; updateName(newName: string) { this.name = newName; } // ... }
name
property: template: (rf: core.ɵRenderFlags, ctx: ManualComponent) => { if (rf & 1) { // : core.ɵE(0, 'h2'); core.ɵT(1, 'Hello, '); core.ɵT(2); // <-- name core.ɵe(); } if (rf & 2) { // : core.ɵt(2, ctx.name); // ctx - } },
if
expressions that check the rf
values. This parameter is used by Angular to indicate whether the component is being created for the first time (the least significant bit will be set ), or we just need to update the dynamic content during the change detection process (this is the second if
expression that is directed).ɵt
responsible for this (note the lower-case letter t
), which corresponds to the textBinding
function exported by Angular:core.ɵT(2);
command core.ɵT(2);
. It plays the role of placeholder for name
. We update it with the core.ɵt(2, ctx.name);
command core.ɵt(2, ctx.name);
when a change in the corresponding variable is detectedHello, Component
will still appear, although we can change the value of the name
property, which will change the text on the screen.updateName()
component method: template: (rf: core.ɵRenderFlags, ctx: ManualComponent) => { if (rf & 1) { core.ɵE(0, 'h2'); core.ɵT(1, 'Hello, '); core.ɵT(2); core.ɵe(); core.ɵT(3, 'Your name: '); core.ɵE(4, 'input'); core.ɵL('input', $event => ctx.updateName($event.target.value)); core.ɵe(); } // ... },
core.ɵL('input', $event => ctx.updateName($event.target.value));
line core.ɵL('input', $event => ctx.updateName($event.target.value));
. Namely, the ɵL
method ɵL
responsible for setting the event listener for the most recent of the declared elements. The first argument is the name of the event (in this case, input
is the event that is called when the content of the <input>
element changes), the second argument is the callback. This callback accepts event data as an argument. Then we retrieve the current value from the target element of the event, that is, from the <input>
element, and pass it to the function in the component. Your name: <input (input)="updateName($event.target.value)" />
<input>
element and observe how the text in the component changes. However, the input field is not filled when the component is loaded. In order for everything to work that way, you need to add another instruction to the template function code that is executed when a change is detected: template: (rf: core.ɵRenderFlags, ctx: ManualComponent) => { if (rf & 1) { ... } if (rf & 2) { core.ɵt(2, ctx.name); core.ɵp(4, 'value', ctx.name); } }
ɵp
, which updates the property of an element with a given index. In this case, the method is passed an index of 4, which is the index assigned to the input
element, and instructs the method that it should put the value of ctx.name
in the value
property of this element.*ngIf
or *ngFor
. They are processed in a special way. Let's look at how to use *ngIf
in the code of our self-made template.@angular/common
npm-package - it is here that *ngIf
. Next, you need to import the directive from this package: import { NgIf } from '@angular/common';
NgIf
in a template, you need to provide it with some metadata, since the @angular/common
module was not compiled using Ivy (at least during the writing of the material, and in the future this will probably change from introduction ngcc ).ɵdefineDirective
method, which is related to the already familiar ɵdefineComponent
method. It defines metadata for directives: (NgIf as any).ngDirectiveDef = core.ɵdefineDirective({ type: NgIf, selectors: [['', 'ngIf', '']], factory: () => new NgIf(core.ɵinjectViewContainerRef(), core.ɵinjectTemplateRef()), inputs: {ngIf: 'ngIf', ngIfThen: 'ngIfThen', ngIfElse: 'ngIfElse'} });
ngFor
. Now that we have prepared NgIf
for use in Ivy, we can add the following to the list of directives for the component: static ngComponentDef = core.ɵdefineComponent({ directives: [NgIf], // ... });
*ngIf
. function ifTemplate(rf: core.ɵRenderFlags, ctx: ManualComponent) { if (rf & 1) { core.ɵE(0, 'div'); core.ɵE(1, 'img', ['src', 'https://pbs.twimg.com/tweet_video_thumb/C80o289UQAAKIqp.jpg']); core.ɵe(); } }
img
element inside the div
element.ngIf
directive to the component template: template: (rf: core.ɵRenderFlags, ctx: ManualComponent) => { if (rf & 1) { // ... core.ɵC(5, ifTemplate, null, ['ngIf']); } if (rf & 2) { // ... core.ɵp(5, 'ngIf', (ctx.name === 'Igor')); } function ifTemplate(rf: core.ɵRenderFlags, ctx: ManualComponent) { // ... } },
core.ɵC(5, ifTemplate, null, ['ngIf']);
). It declares a new container element, that is, an element that has a template. The first argument is the element index; we have already seen such indexes. The second argument is the sub-template function that we have just defined. It will be used as a template for the container element. The third parameter is the tag name for the element, which does not make sense here, and finally there is a list of directives and attributes associated with this element. This is where ngIf
comes into ngIf
.core.ɵp(5, 'ngIf', (ctx.name === 'Igor'));
the element state is updated by binding the ngIf
attribute to the value of the logical expression ctx.name === 'Igor'
. Here we check whether the name
property of the component is equal to the string Igor
. <div *ngIf="name === 'Igor'"> <img align="center" src="..."> </div>
NgIf
section in action, enter the name Igor
in the field Your name
.Source: https://habr.com/ru/post/419995/
All Articles