Angular 结构指令和 Renderer2
Angular structural directive and Renderer2
我有一个自定义复选框指令,它添加了传统 label/span 样式中的样式以及一些其他功能。它在自身周围注入包装器,并在旁边注入跨度。我刚刚意识到,当放置在结构指令中时,它无法操纵 DOM。大多数设置是在构造函数中完成的,但我认为这可能需要更多 Angular life-cycle-aware 才能与结构 parents.
示例问题 DOM:
<ng-container *ngIf="test">
<!-- <div class="row align-middle"> -->
<input type="text" alloy placeholder="you should see a checkbox">
<input type="checkbox" alloy alloyLabel="default">
<!-- </div> -->
</ng-container>
有了评论 div 就可以了。但是,由于 ng-container 是直接的 parent,因此渲染器无法注入 DOM。这是构造函数:
constructor(
protected el: ElementRef,
protected renderer: Renderer2,
protected focusMonitor: FocusMonitor,
@Host() @Optional() protected identityDirective: AlloyIdentityDirective) {
super();
// If we don't have a label wrapper, create one
this.labelElement = this.renderer.parentNode(el.nativeElement);
if (!(this.labelElement instanceof HTMLLabelElement)) {
const label = this.renderer.createElement('label');
// Inject wrapper then move native element (input) within it.
this.renderer.insertBefore(this.labelElement, label, this.el.nativeElement);
this.renderer.removeChild(this.labelElement, this.el.nativeElement);
this.renderer.appendChild(label, this.el.nativeElement);
this.labelElement = label;
// We must add the span because that's what actually gets the check styling
this.styledElement = this.renderer.createElement('span');
this.renderer.appendChild(this.labelElement, this.styledElement);
}
编辑:添加了结果 DOM
没有实际错误。在有缺陷的情况下(直接 ng-container
的 parent)我最终得到了初始元素,但没有注入:
<input type="checkbox" alloy alloyLabel="default">
使用包装器 div
我得到了预期的注入(_ngcontent* 已删除):
<label class="alloy-check-wrapper">
<input alloy="" alloylabel="default" type="checkbox" class="alloy-check">
<span></span>
<span class="alloyLabel">default</span>
</label>
这很可能是因为当一个元素位于 ng-container 的顶层时,它的构造早于 ngIf 变为 true 的时间,即在它被添加到 DOM.
要解决此问题,您需要将修改 DOM 的逻辑从构造函数移至 ngOnInit,例如:
constructor(
protected el: ElementRef,
protected renderer: Renderer2,
protected focusMonitor: FocusMonitor,
@Host() @Optional() protected identityDirective: AlloyIdentityDirective) {
super();
}
ngOnInit() {
// If we don't have a label wrapper, create one
this.labelElement = this.renderer.parentNode(el.nativeElement);
if (!(this.labelElement instanceof HTMLLabelElement)) {
const label = this.renderer.createElement('label');
// Inject wrapper then move native element (input) within it.
this.renderer.insertBefore(this.labelElement, label, this.el.nativeElement);
this.renderer.removeChild(this.labelElement, this.el.nativeElement);
this.renderer.appendChild(label, this.el.nativeElement);
this.labelElement = label;
}
// We must add the span because that's what actually gets the check styling
this.styledElement = this.renderer.createElement('span');
this.renderer.appendChild(this.labelElement, this.styledElement);
}
对于修改 DOM,这几乎肯定是一个更好的做法,因为在构造函数执行的那一刻,您永远无法确定它是否正确地存在于 DOM 中.
我有一个自定义复选框指令,它添加了传统 label/span 样式中的样式以及一些其他功能。它在自身周围注入包装器,并在旁边注入跨度。我刚刚意识到,当放置在结构指令中时,它无法操纵 DOM。大多数设置是在构造函数中完成的,但我认为这可能需要更多 Angular life-cycle-aware 才能与结构 parents.
示例问题 DOM:
<ng-container *ngIf="test">
<!-- <div class="row align-middle"> -->
<input type="text" alloy placeholder="you should see a checkbox">
<input type="checkbox" alloy alloyLabel="default">
<!-- </div> -->
</ng-container>
有了评论 div 就可以了。但是,由于 ng-container 是直接的 parent,因此渲染器无法注入 DOM。这是构造函数:
constructor(
protected el: ElementRef,
protected renderer: Renderer2,
protected focusMonitor: FocusMonitor,
@Host() @Optional() protected identityDirective: AlloyIdentityDirective) {
super();
// If we don't have a label wrapper, create one
this.labelElement = this.renderer.parentNode(el.nativeElement);
if (!(this.labelElement instanceof HTMLLabelElement)) {
const label = this.renderer.createElement('label');
// Inject wrapper then move native element (input) within it.
this.renderer.insertBefore(this.labelElement, label, this.el.nativeElement);
this.renderer.removeChild(this.labelElement, this.el.nativeElement);
this.renderer.appendChild(label, this.el.nativeElement);
this.labelElement = label;
// We must add the span because that's what actually gets the check styling
this.styledElement = this.renderer.createElement('span');
this.renderer.appendChild(this.labelElement, this.styledElement);
}
编辑:添加了结果 DOM
没有实际错误。在有缺陷的情况下(直接 ng-container
的 parent)我最终得到了初始元素,但没有注入:
<input type="checkbox" alloy alloyLabel="default">
使用包装器 div
我得到了预期的注入(_ngcontent* 已删除):
<label class="alloy-check-wrapper">
<input alloy="" alloylabel="default" type="checkbox" class="alloy-check">
<span></span>
<span class="alloyLabel">default</span>
</label>
这很可能是因为当一个元素位于 ng-container 的顶层时,它的构造早于 ngIf 变为 true 的时间,即在它被添加到 DOM.
要解决此问题,您需要将修改 DOM 的逻辑从构造函数移至 ngOnInit,例如:
constructor(
protected el: ElementRef,
protected renderer: Renderer2,
protected focusMonitor: FocusMonitor,
@Host() @Optional() protected identityDirective: AlloyIdentityDirective) {
super();
}
ngOnInit() {
// If we don't have a label wrapper, create one
this.labelElement = this.renderer.parentNode(el.nativeElement);
if (!(this.labelElement instanceof HTMLLabelElement)) {
const label = this.renderer.createElement('label');
// Inject wrapper then move native element (input) within it.
this.renderer.insertBefore(this.labelElement, label, this.el.nativeElement);
this.renderer.removeChild(this.labelElement, this.el.nativeElement);
this.renderer.appendChild(label, this.el.nativeElement);
this.labelElement = label;
}
// We must add the span because that's what actually gets the check styling
this.styledElement = this.renderer.createElement('span');
this.renderer.appendChild(this.labelElement, this.styledElement);
}
对于修改 DOM,这几乎肯定是一个更好的做法,因为在构造函数执行的那一刻,您永远无法确定它是否正确地存在于 DOM 中.