如何将模板引用变量与ngIf组合?

时间:2017-06-28 19:34:33

标签: angular angular2-template

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

即使*ngIf成立,我也会获得Cannot read property 'foo' of undefined。如果我删除*ngIf,它就可以了!

我尝试使用Elvis运算符tRefVar?.foo来解决错误,但之后它永远不会使用值进行更新。

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

我做错了什么?

5 个答案:

答案 0 :(得分:25)

正如Tobias Bosch所说

  

在* ngIf内声明的变量不能在...之外使用   * ngIf

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

  

只有相反的方式(即声明* ngIf中的变量并使用   它在* ngIf之外是不起作用的,将无法按设计工作。

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

为什么会这样?

1)没有* ngIf

让我们看看这个模板

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

angular将为此创建以下viewDefinition

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

此处没有嵌入视图。一体View_App_0。我们可以在这里看到我们的表达式{{tRefVar?.foo}}

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

它从索引为2的节点获取值

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

在同一视图中声明

2)使用* ngIf

然后让我们更改模板如下

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

输出将是以下

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创建了嵌入式视图View_App_1,除了View_App_0。我们的表达式{{tRefVar?.foo}}已变为

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

它只是成为组件属性,因为没有节点会在View_App_0中引用此模板变量。它已进入嵌入式视图View_App_1

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

因此我们不能引用嵌入视图外嵌入视图中声明的模板变量。

如何解决?

1)使用可见性标记,如[hidden]或css类,而不是*ngIf

2)在嵌入视图中移动表达式,其中tRefVar被声明为

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

3)使用@ViewChild因为它将代表组件属性。或者使用@ViewChildren

答案 1 :(得分:1)

<div *ngIf="true" myHighlight #tRefVar="myHighlight"></div>
在这里你应该注意*ngIf是一个语法糖(快捷方式)来定义ng-template,所以实际上评估为

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

请注意,#tRefVar可由儿童(div此处)及其自身(ng-template此处)访问。
第二个<div>不是存在模板引用变量的<div>的兄弟 更多解释here

由于Child / Sibling元素可以引用模板引用变量,因此可以预期该行为。

答案 2 :(得分:0)

您可以使用例如ng-container并在那里设置ngIf条件,这使tRevVar可以像这样访问:

<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

可能有更多方法可以让它发挥作用,但你必须更具体,然后你想用它做什么。

希望我能提供帮助。

要回答评论中的问题&#34; 不应该在第一次打勾后更新tRefVar吗?&#34;

不,因为它未定义。如果在组件中声明对象(但保留未定义)并向其添加属性,则会得到相同的结果。 elvis运营商在那里没有帮助。

myObj: any;

ngOnInit() {
    myObj.text = 'my Text';
}

<div>{{ myObj?.text }}</div>

这不会奏效,但这可行:

myObj: any = {};

ngOnInit() {
    myObj.text = 'my Text';
}

<div>{{ myObj?.text }}</div>

再次编辑我的答案并删除了一个简单错误的令人困惑的解释。谢谢yurzui,终于得到了你的意思。需要一夜的睡眠。

答案 3 :(得分:0)

由@lexith在above answer中给出,因为您正在使用*ngIf,所以当时没有定义相应的模板变量。我们可以通过修改代码来避免这种混淆 <div *ngIf="true" myHighlight #tRefVar="myHighlight"></div>
<div [hidden]=false myHighlight #tRefVar="myHighlight"></div>这将有效。

最终代码:

@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

答案 4 :(得分:0)

如果您使用的是Angular 8,则可以通过添加视图子引用并将静态值设置为false来解决此问题。

示例模板代码:

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

组件代码:

export class EventsComponent {
    @ViewChild('eventsTable', {static: false}) eventsTable: Table;

    constructor() {
        console.log(this.eventsTable)
    }
}

在Angular 9中,false将是默认值。