如何选择 *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 呈现的动态元素的主要内容,如果未能解决你的问题,请参考以下文章
jQuery 不选择具有动态 ID 的元素,如何做到这一点?