Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I combine a template reference variable with ngIf?

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

Even though the *ngIf is true, I get a Cannot read property 'foo' of undefined. If I remove the *ngIf, it works fine!

I tried using the Elvis operator tRefVar?.foo, which resolved the error, but then it never updates with the value.

https://plnkr.co/edit/5rsXygxK1sBbbkYdobjn?p=preview

What am I doing wrong?

like image 214
adamdport Avatar asked Jun 28 '17 19:06

adamdport


People also ask

How do I pass a template reference variable in component?

To get started using template reference variables, simply create a new Angular component or visit an existing one. To create a template reference variable, locate the HTML element that you want to reference and then tag it like so: #myVarName .

What is correct syntax for a template reference variable?

Syntaxlink In the template, you use the hash symbol, # , to declare a template variable. The following template variable, #phone , declares a phone variable with the <input> element as its value. Refer to a template variable anywhere in the component's template.

What is the use of template reference variable?

Template Reference Variable in angular is used to access all the properties of any element inside DOM. It can also be a reference to an Angular component or directive or a web component.

What does template reference variable contain?

A template reference variable is often a reference to a DOM element within a template. It can also refer to a directive (which contains a component), an element, TemplateRef, or a web component.


3 Answers

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);
      });
}

enter image description here

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);
      });
}

enter image description here

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

like image 198
yurzui Avatar answered Oct 09 '22 15:10

yurzui


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.

like image 17
Tom Benyon Avatar answered Oct 09 '22 17:10

Tom Benyon


<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.

like image 2
Manubhargav Avatar answered Oct 09 '22 15:10

Manubhargav