如何选择 *ngIf 呈现的动态元素

Posted

技术标签:

【中文标题】如何选择 *ngIf 呈现的动态元素【英文标题】:How can I select dynamic elements rendered by *ngIf 【发布时间】:2017-03-25 10:01:30 【问题描述】:

正如下面提供的代码。我尝试选择由 ngIf 生成的动态元素,但失败了。

我一共用了两种方式。

    ElementRef 和 querySelector

组件模板:

    `<div class="test" *ngIf="expr">
      <a id="button">Value 1</a>
    </div>
    <div class="test" *ngIf="!expr">
      <a id="button">Value 2</a>
    </div>`

组件类:

    expr: boolean;

    constructor(
      private elementRef: ElementRef,
    ) 
    

    ngOnInit(): void
    //Call Ajax and set the value of this.expr based on the callback;
    //if expr == true, then show text Value 1; 
    //if expr == false, then show text Value 2;
    
    ngAfterViewInit(): void
      console.log(this.elementRef.nativeElement.querySelector('#button'));
    

输出结果为null

    @ViewChild

组件模板:

    `<div class="test" *ngIf="expr">
      <a #button>Value 1</a>
    </div>
    <div class="test" *ngIf="!expr">
      <a #button>Value 2</a>
    </div>`

组件类:

    @ViewChild('button') button: elementRef;

    expr: boolean;

    ngOnInit(): void
    //Call Ajax and set the value of this.expr based on the callback;
    //if expr == true, then show text Value 1; 
    //if expr == false, then show text Value 2;
    

    ngAfterViewInit(): void
      console.log(this.button);
    

输出结果为undefined;

有没有办法获取 *ngIf 生成的动态 dom?


终于通过@ViewChildren 解决了这个问题。

并且要记录更新的结果,需要使用单独的函数。

例如:

错误代码:

    @ViewChildren('button') buttons: ElementRef;

    function(): void
      this.expr = true; // Change expression then the DOM will be changed;
      console.log(this.buttons.toArray()); // This is wrong because you will still get the old result;
    

正确的代码:

    @ViewChildren('button') buttons: ElementRef;

    function(): void
      this.expr = true; // Change expression then the DOM will be changed;
    
    ngAfterViewInit(): void
      this.buttons.changes.subscribe( e => console.log(this.buttons.toArray()) ); // This is right and you will get the latest result;
    

【问题讨论】:

如何更新expr 值?是@Input() 吗? expr 的值由 AJAX 回调决定。 @Ghetolay 好的,但是怎么做?您是使用 observable 还是通过 @Input() 注册回调函数?我认为无论你如何做,甘特的答案都会奏效,所以如果他的解决方案对你有用,你就不用费心回答我了。否则,您可以将该部分添加到代码 sn-p。 【参考方案1】:

*ngIf="expr" 表达式为 false 时无法获取该元素,因为该元素不存在。

ngOnInit() 中的值尚未设置,仅在调用 ngAfterViewInit() 时。

Plunker example

@Component(
  selector: 'my-app',
  template: `
    <div class="test" *ngIf="prop">
      <a #button id="button1">button1</a>
    </div>
    <div class="test" *ngIf="!boolean">
      <a id="button2">button2</a>
    </div>`
 ,
)
export class App 
  @ViewChild('button') button: ElementRef;

  prop:boolean = true;

  ngAfterViewInit() 
    console.log(this.button);
  

Plunker example with ViewChildren

@Component(
  selector: 'my-app',
  template: `
    <button (click)="prop = !prop">toggle</button>
    <div class="test" *ngIf="prop">
      <a #button id="button1">button1</a>
    </div>
    <div class="test" *ngIf="!boolean">
      <a #button id="button2">button2</a>
    </div>`
 ,
)
export class App 
  @ViewChildren('button') button: QueryList<ElementRef>;

  prop:boolean = true;

  ngAfterViewInit() 
    console.log(this.button.toArray());
    this.button.changes.subscribe(val => 
      console.log(this.button.toArray());
    );

  

【讨论】:

即使满足了表达式,渲染了dom,还是拿不到元素。有什么办法吗?谢谢。 constructor( private elementRef: ElementRef ) 只能获取init DOM。例如,在 ajaxcall 之后,DOM 会发生变化,但 elementRef 不会。所以我试图找到一种在ajax调用后声明elementRef的方法。 它是如何变化的?传递给构造函数的ElementRef 是组件或指令的宿主元素,没有办法将组件移动到不同的元素——至少我从未听说过这样的事情。 AJAX调用后如何修改DOM? 例如,AJAX 回调给了我一个布尔值'expr'。并且会根据expr的值改变不同的模板。【参考方案2】:
<div class="test" *ngIf="boolean">
  <a #button id="button1">button1</a>
</div>

@ViewChild('button') button: elementRef;

console.log(this.button);

ID = 'button1',而不是 'button'?

var target = document.getElementById('Button1');

【讨论】:

对不起,示例代码不清楚,在此示例中,我尝试使用 ViewChild 标签#button not ID button1【参考方案3】:

如果您仅在有人与它交互(例如点击它)或与它的兄弟或子元素交互的那一刻才需要该元素,您可以将它的引用传递给事件。

模板:

<div class="test" *ngIf="expr">
  <a #b id="button1" (click)="onButton1(b)">button1</a>
</div>

代码:

onButton1(button: htmlButtonElement) 

如果您需要不与它交互的元素,您也可以查看ViewChildren 查询而不是ViewChild

你可以设置它

@ViewChildren('button') buttons: QueryList<ElementRef>;    

然后您可以通过this.buttons.changes.subscribe(...) 订阅匹配选择器的元素更改。如果元素被创建或删除,您将通过订阅收到通知。但是我的第一种方法涉及的样板代码要少得多。

或者,您也只能在您确定元素存在的时刻(通过一些先决条件)同步访问QueryList。在您的示例中,您应该能够使用

检索按钮
let button: ElementRef = this.buttons.first;

在这些情况下。

【讨论】:

好主意。但我想获得这个动态 DOM 的原因是创建 observable 不仅包括点击,还包括其他事件。还是谢谢。 您也可以对其他事件执行此操作。 没错。这就是我写“与它交互”的原因。或者甚至在 if 块中使用另一个元素。但是点击的例子是最容易理解的。 我理解并会尝试一下。谢谢。 @Matthias247 谢谢。我会试试这个。但仍然想看看你的答案中提到的其他可能的方式。【参考方案4】:

并不是最好的,但我使用 [ngClass] 而不是 *ngIf 来处理类似的情况。 只需使用“display:none”创建一个类并在需要时隐藏元素。可以毫无问题地访问使用 @ViewChild 选择的元素。 现在,您可以在寻找更好或更优雅的解决方案时使用这个小“hack”。 例如。

.hide-element
    display: none;


<div class="test" [ngClass]="expr === 'yourTest' ? 'hide-element' : null">
  <a #b id="button1" (click)="onButton1(b)">button1</a>
</div>

如果你正在处理一些异步返回,你可以使用异步管道

<div class="test" [ngClass]="(expr | async) === 'yourTest' ? 'hide-element' : null">
      <a #b id="button1" (click)="onButton1(b)">button1</a>
</div>

希望对你有帮助。

【讨论】:

以上是关于如何选择 *ngIf 呈现的动态元素的主要内容,如果未能解决你的问题,请参考以下文章

如何从动态构建的表格中选择标题元素?

如何使用Vue.js获取呈现dom元素的组件

如何使用 jQuery 创建元素并动态选择它们

jQuery 不选择具有动态 ID 的元素,如何做到这一点?

使用 Angular2 DevExtreme 显示和隐藏表单元素

如何通过jQuery选择使用javascript动态生成的innerHTML元素[重复]