How do I combine a template reference variable with ngIf?

后端 未结 5 823
抹茶落季
抹茶落季 2020-12-14 06:16
tRefVar is {{tRefVar.foo}}

Even though the

相关标签:
5条回答
  • 2020-12-14 06:39

    As Tobias Bosch said

    A variable declared inside of an *ngIf cannot be used outside of the *ngIf

    https://github.com/angular/angular/issues/6179#issuecomment-233374700

    Only the opposite way (i.e. declare a variable inside of *ngIf and use it outside of *ngIf) is not working, and won't work by design.

    https://github.com/angular/angular/issues/6179#issuecomment-233579605

    Why is it so?

    1) Without *ngIf

    Let's see at this template

    <h2 myHighlight #tRefVar="myHighlight">tRefVar is {{tRefVar.foo}}</h2>
    <div>tRefVar is {{tRefVar?.foo}}</div>
    

    angular will create the following viewDefinition for that:

    function View_App_0(_l) {
      return jit_viewDef1(0,[(_l()(),jit_textDef2(null,['\n      '])),(_l()(),jit_elementDef3(0,
          null,null,2,'h2',[['myHighlight','']],null,null,null,null,null)),jit_directiveDef4(16384,
          [['tRefVar',4]],0,jit_HighlightDirective5,[jit_ElementRef6],null,null),(_l()(),
          jit_textDef2(null,['tRefVar is ',''])),(_l()(),jit_textDef2(null,['\n      '])),
          (_l()(),jit_elementDef3(0,null,null,1,'div',[],null,null,null,null,null)),(_l()(),
              jit_textDef2(null,['tRefVar is ',''])),(_l()(),jit_textDef2(null,['\n  ']))],
          null,function(_ck,_v) {
            var currVal_0 = jit_nodeValue7(_v,2).foo;
            _ck(_v,3,0,currVal_0);
            var currVal_1 = ((jit_nodeValue7(_v,2) == null)? null: jit_nodeValue7(_v,2).foo);
            _ck(_v,6,0,currVal_1);
          });
    }
    

    there is no embedded view here. All in one View_App_0. And we can see here our expression {{tRefVar?.foo}}

    var currVal_1 = ((jit_nodeValue7(_v,2) == null)? null: jit_nodeValue7(_v,2).foo);
    

    it takes value from node with index 2

    jit_directiveDef4(16384,
      [['tRefVar',4]],0,jit_HighlightDirective5,[jit_ElementRef6],null,null),(_l()(),
      jit_textDef2(null,['tRefVar is ','']))
    

    that declared in the same view

    2) With *ngIf

    Then let's change template as follows

    <h2 *ngIf="true" myHighlight #tRefVar="myHighlight">tRefVar is {{tRefVar.foo}}</h2>
    <div>tRefVar is {{tRefVar?.foo}}</div>
    

    The output will be the following

    function View_App_1(_l) {
      return jit_viewDef1(0,[(_l()(),jit_elementDef2(0,null,null,2,'h2',[['myHighlight',
          '']],null,null,null,null,null)),jit_directiveDef3(16384,[['tRefVar',4]],0,jit_HighlightDirective4,
          [jit_ElementRef5],null,null),(_l()(),jit_textDef6(null,['tRefVar is ','']))],
          null,function(_ck,_v) {
            var currVal_0 = jit_nodeValue7(_v,1).foo;
            _ck(_v,2,0,currVal_0);
          });
    }
    function View_App_0(_l) {
      return jit_viewDef1(0,[(_l()(),jit_textDef6(null,['\n'])),(_l()(),jit_anchorDef8(16777216,
          null,null,1,null,View_App_1)),jit_directiveDef3(16384,null,0,jit_NgIf9,[jit_ViewContainerRef10,
          jit_TemplateRef11],{ngIf:[0,'ngIf']},null),(_l()(),jit_textDef6(null,['\n'])),
          (_l()(),jit_elementDef2(0,null,null,1,'div',[],null,null,null,null,null)),(_l()(),
              jit_textDef6(null,['tRefVar is ',''])),(_l()(),jit_textDef6(null,['\n  ']))],
          function(_ck,_v) {
            var currVal_0 = true;
            _ck(_v,2,0,currVal_0);
          },function(_ck,_v) {
            var _co = _v.component;
            var currVal_1 = ((_co.tRefVar == null)? null: _co.tRefVar.foo);
            _ck(_v,5,0,currVal_1);
          });
    }
    

    Angular created embedded view View_App_1 apart to View_App_0 . And our expression {{tRefVar?.foo}} has turned into

    var currVal_1 = ((_co.tRefVar == null)? null: _co.tRefVar.foo);
    

    it just becames component property because there is no node that will reference to this template variable in View_App_0. It's gone to embedded view View_App_1

    var currVal_0 = jit_nodeValue7(_v,1).foo;
    

    So we cannot refer to template variable that has been declared in embedded view outside of embedded view.

    How to solve it?

    1) Use visibility flag like [hidden] or css class instead of *ngIf

    2) Move your expression inside embedded view where tRefVar is declared

    <ng-container *ngIf="true">
      <h2 myHighlight #tRefVar="myHighlight">tRefVar is {{tRefVar.foo}}</h2>
      <div>tRefVar is {{tRefVar?.foo}}</div>
    </ng-container>
    

    3) Use @ViewChild because it will represent component property. Or use @ViewChildren

    0 讨论(0)
  • 2020-12-14 06:46

    If you are using Angular 8 you can solve this issue by adding a view child reference and setting the static value to false.

    Example template code:

    <button type="button" (click)="eventsTable.someMethod()">Click Me!</button>
    <div *ngIf="someValue" #eventsTable >
        SHIBAMBO!
    </div>
    

    Component Code:

    export class EventsComponent {
        @ViewChild('eventsTable', {static: false}) eventsTable: Table;
    
        constructor() {
            console.log(this.eventsTable)
        }
    }
    

    In Angular 9, false will be the default value.

    0 讨论(0)
  • 2020-12-14 06:49

    <div *ngIf="true" myHighlight #tRefVar="myHighlight"></div>
    Here you should note that *ngIf is a syntactic sugar(shortcut) to define a ng-template, So that actually evaluates to

    <ng-template [ngIf]="true">
      <h2 myHighlight #tRefVar="myHighlight">Hello {{name}}, tRefVar is {{tRefVar.foo}}</h2>
    </ng-template>
    <div>tRefVar is {{tRefVar?.foo}}</div>
    

    Note that #tRefVar is accessible by Child(div here) and itself(ng-template here).
    The second <div> is not a sibling to the <div> where Template reference variable is present.
    More explained here


    The behavior is expected as the Template reference variable can be referenced by Child/Sibling elements.

    0 讨论(0)
  • 2020-12-14 06:50

    As given in the above answer by @lexith, Because you're using *ngIf the corresponding template variable is not defined at the time. We can avoid this confusion by modifying the code
    <div *ngIf="true" myHighlight #tRefVar="myHighlight"></div> with
    <div [hidden]=false myHighlight #tRefVar="myHighlight"></div> and that will work.


    Final code:

    @Component({
      selector: 'my-app',
      template: `
        <div>
          <h2 [hidden]=false myHighlight #tRefVar="myHighlight">Hello {{name}}, tRefVar is {{tRefVar.foo}}</h2>
          <div>tRefVar is {{tRefVar?.foo}}</div>
        </div>
      `,
    })
    

    Modified Plunker

    0 讨论(0)
  • 2020-12-14 07:06

    You could use for example ng-container and set your ngIf conditional there, which makes tRevVar accessible like this:

    <ng-container *ngIf="true">
         <h2 myHighlight #tRefVar="myHighlight">Hello {{name}}, tRefVar is {{tRefVar.foo}}</h2>
         <div>tRefVar is {{tRefVar?.foo}}</div>
    </ng-container>
    

    Plunkr: https://plnkr.co/edit/cqrsDVGwa90o1FGWgE22?p=preview

    There are probably more ways to make it work, but you have to be more specific then, what you want to do with it.

    Hope i could help.


    To answer the question in your comment "Shouldn't it update tRefVar after that first tick though?":

    No, because it's undefined. You'll have the same outcome if you declare an object in your component (but leave it undefined) and add a property to it. The elvis operator doesn't help there.

    myObj: any;
    
    ngOnInit() {
        myObj.text = 'my Text';
    }
    
    <div>{{ myObj?.text }}</div>
    

    This won't work, but this would be working:

    myObj: any = {};
    
    ngOnInit() {
        myObj.text = 'my Text';
    }
    
    <div>{{ myObj?.text }}</div>
    

    Edited my answer again and removed the confusing explanation which was plain wrong. thanks yurzui, finally got what you meant. needed a night of sleep for it.

    0 讨论(0)
提交回复
热议问题