【问题标题】:How do I combine a template reference variable with ngIf?如何将模板引用变量与 ngIf 结合起来?
【发布时间】:2017-12-02 07:53:01
【问题描述】:
<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

我做错了什么?

【问题讨论】:

  • 您可以简单地使用[ngStyle]="{'display': showElement ? 'block' : 'none'}" 或使用任何其他默认显示选项(flex、inline ...)

标签: angular angular2-template


【解决方案1】:

如果您使用的是 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 将是默认值。

【讨论】:

    【解决方案2】:

    正如托拜厄斯·博斯所说

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

    这里没有嵌入视图。多合一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);
          });
    }
    

    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

    【讨论】:

    • 为什么您建议在解决方案#2 中使用ng-container 而不是ng-template 和ngIf?如果两者都可能,那么在这种情况下如何考虑在它们之间进行选择?
    • @AlexanderAbakumov 我在那里展示了 ngIf 的糖化版本,因为每个人都很容易理解这种众所周知的语法。您可以改用&lt;ng-template [ngIf],它会更好地处理事件,因为它不会创建额外的评论节点
    • 知道了,谢谢,特别提到评论节点!由于我在这里缺乏经验,只需检查两个选项之间的区别。
    【解决方案3】:

    您可以使用例如 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

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

    希望我能帮上忙。


    回答您评论中的问题“不应该在第一次打勾后更新 tRefVar 吗?”:

    不,因为它是未定义的。如果您在组件中声明一个对象(但未定义)并向其添加一个属性,您将获得相同的结果。 elvis 操作员对此无济于事。

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

    这不起作用,但这会起作用:

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

    再次编辑我的答案并删除了完全错误的令人困惑的解释。谢谢yurzui,终于明白你的意思了。需要睡一晚。

    【讨论】:

    【解决方案4】:

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


    最终代码:

    @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

    【讨论】:

      【解决方案5】:

      &lt;div *ngIf="true" myHighlight #tRefVar="myHighlight"&gt;&lt;/div&gt;
      在这里你应该注意到*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 可由 Child(div 此处)及其自身(ng-template 此处)访问。
      第二个&lt;div&gt; 不是存在模板引用变量的&lt;div&gt; 的兄弟。
      更多解释here


      这种行为是预期的,因为模板引用变量可以被子/兄弟元素引用。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-30
      • 2021-06-10
      相关资源
      最近更新 更多