preface

I am a person who writes articles or experiences. I don’t like to copy directly from the official website, which is really not interesting. I prefer to write in my vernacular. Today this about the template input variable this I chewed the official website for a long time today, finally is a preliminary understanding of what is something.

So what exactly is a template input variable

The reason I wanted to look at this is because when I was using Ng-Zorro, I used its Pagination component (link to website). One of these features is the ability to customize the previous page next page template. The code is as follows:

@Component({
  selector: 'nz-demo-pagination-item-render'.template: ` 
      {{ page }} 
        Previous 
        Next 
        << 
        >> 
        `
})
export class NzDemoPaginationItemRenderComponent {}
Copy the code

After I read this, I was like, what is this let, why does a let- have a value after the last variable? Then I started to look for what the let was in the official website. Finally, I found a description of lets in the main concept-instruction-Structural Instruction microsyntax section. Website description: micro syntax.

There is a short explanation below:

Template input variable

A template input variable is a variable whose value you can refer to in the template of a single instance. This example has several template input variables: hero, I, and odd. They all use let as the lead keyword.

.

You declare a template input variable in the template using the let keyword (such as let Hero). The scope of this variable is limited to a single instance of the repeating template. In fact, you can use the same variable name in other structural directives.

.

It doesn’t tell you how it works, nor does it tell you where the values of the variables in the let declaration come from. I more see more gas, get, official website you don’t say, I find myself.

A rough conclusion: Variables declared by let are variables in the context object of the template template. That’s why they call it template input variables. In the *ngFor Inside section, we see that the struct directive is to wrap the host element in a
, and then parse the expression in *ngFor into the let template input variables and the values that the directive needs to pass in. Since the code in the template is not directly rendered into a view, there must be some way to make the template into a view. That’s what our structure directive does, which is turn our template into a view.

The official ngFor example code is as follows:

// The template before parsing<div *ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd] ="odd">
  ({{i}}) {{hero.name}}
</div>// Angular parsed template<ng-template ngFor let-hero [ngForOf] ="heroes" let-i="index" let-odd="odd" [ngForTrackBy] ="trackById">
  <div [class.odd] ="odd">({{i}}) {{hero.name}}</div>
</ng-template>
Copy the code
  • Here’s a hint, so calledThe host elementIt’s the element where the instruction is, and like in the example above,divis*ngForThe host element of the instruction.
  • trackByThis is similar to the key in vue and react. You can give the template an identifier to reduce the performance overhead when it is rerendered.

And then we look at the website:

  • let-ilet-oddThe variable is bylet i=indexlet odd=oddIs defined by. Angular sets them tocontextThe object of theindexoddProperty.
  • It’s not specified herelet-heroContext properties of. Its origin is implicit. Presents thelet-heroSet for this context$implicitProperty, which is made up ofNgForInitialized with the hero in the current iteration.

Angular sets a context object for this template. But we can’t see this process because it’s implemented inside the source code for ngFor. And the context object has the index and odd properties, and contains a $implicit. The property of (not explicitly stated). So we infer that this context object has at least the following properties:

{
    $implicit:null.index:null.odd:null,}Copy the code

So what we’re essentially declaring a let variable is declaring a variable that gets the value of a property of the same name in the context object. Without any let-hero assignment, hero defaults to $implicit. It doesn’t matter how many let-a’s, let-B’s, let-c’s or let-Me’s. The declared value of this variable is equal to $implicit’s value. If we assign, for example, let-i = “index”, the value of I is equal to the value of the index property in the context object.

How is the context object set

Once we know what the context object is, we need to think about how the context object is set.

In the Structure directives section, we follow the official example to do this custom structure directive (if you haven’t done it already, we recommend doing it first). In UnlessDirective, its constructor declares two injectable variables, TemplateRef and ViewContainerRef. TemplateRef is a reference to the template formed by wrapping the host element. ViewContainerRef represents a reference to the view container. So the question is, where is this view container? Let’s print out a ViewContainerRef in our constructor. The print result is as shown in the figure:

And then we click on the comment element. It turns out to be a comment element. As shown in the figure:

I’m not really sure if the view container is the comment element. But there is no doubt that the view container and the host element are brothers, right next to the host element. We can use the createEmbeddedView() method in ViewContainerRef to create a real view by passing in the templateRef template reference. Because this view is inserted into the view container ViewContainerRef, it is also called an embedded view. So what does this have to do with our context object? The createEmbeddedView method has more than one parameter, and the second parameter is the context object for our template. See the createEmbeddedView API for more details.

That’s all. We can then insert the context object into the template, and we can use the context object directly using the let variable declaration method.

Customize a simple class *ngFor directiveappRepeat

So we know how it’s set up, so let’s verify that it’s right. Next, let’s write a simple rendering directive that mimics ngfor’s functionality.

First we define a directive: RepeatDirective. The code is as follows:

@Directive({
  selector: '[appRepeat]',})export class RepeatDirective {
  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainer: ViewContainerRef,
  ){}@Input(a)set appRepeatOf(heroesList: string[]) {
    heroesList.forEach((item, index, arr) = > {
      this.viewContainer.createEmbeddedView(this.templateRef, {
        // The default value for the current entry
        $implicit: item,
        // Total number of iterable objects
        count: arr.length,
        // The index value of the current entry
        index: index,
        // True if the index of the current entry in the iterable is an even number.
        even: index % 2= = =0.// True if the index number of the current entry in the iterable is odd.
        odd: index % 2= = =1}); }); }}Copy the code

We then import it into the NgModule and omit the process. Then we use this directive in the component:

@Component({
  selector: 'app-structural-likeNgFor-demo'.template: '

Custom ngFor (appRepeat)

  • index: {{I}} -- {{h}} -- index: {{even.tostring ()}}

  • index: {{I}} - {{h}} - index value is an even number: {{even. The toString ()}} < / li > < / ul > `
,})export class StructuralLikeNgForDemoComponent { public heroesList: string[] = ['clock from'.'smoke Fei'.'Noelle'.'Diona']; } Copy the code

The important thing to note here is that the appRepeatOf directive is not scribbled. The “of” in the let h of heroesList will be capitalized first during microsyntax parsing, becoming “of”. Then I prefix it with this directive, appRepeat. The combination is appRepeatOf. It receives an iterable object.

The final renderings are shown:

The result is the same as *ngFor. But the functionality is certainly lacking, if you have the ability to read the source code of *ngFor: *ngFor source code.

conclusion

From this article, we learned that let-variables, a template input variable, are defined and retrieved from the context object of the template. Then setting the context object is done with the second parameter of the createEmbeddedView method.

conclusion

But I’ve always felt like I didn’t know enough. I’ve always felt like there might be more than just createEmbeddedView to set the context object for the template, but I haven’t found any other way. If you know other ways can leave a message to tell me.

References:

The inspiration for this article is that Angular implements a “repeat” directive