Угловая структурная директива и Renderer2 - PullRequest
0 голосов
/ 11 сентября 2018

У меня есть специальная директива checkbox, которая добавляет стили в традиционный стиль label / span, а также некоторые другие функции.Он вводит обертку вокруг себя и пролет рядом с ним.Я просто понял, что когда он помещен в структурную директиву, он не может манипулировать DOM.Большая часть настройки выполняется в конструкторе, но я думаю, что, возможно, это должно быть более ориентированным на жизненный цикл Angular, чтобы хорошо играть со структурными родителями.

Пример выпуска 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-контейнера, поскольку он является прямым родителем, рендереру не удается внедрить 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);
}

Редактировать: Добавлено Resultant DOM

Нет фактической ошибки.В некорректном случае (прямой родитель ng-container) я получаю начальный элемент, но без инъекций: <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>

1 Ответ

0 голосов
/ 11 сентября 2018

Вероятно, это связано с тем, что, когда элемент находится на верхнем уровне внутри ng-контейнера, он создается раньше, чем когда ваш ngIf становится истинным, то есть до того, как он будет добавлен в 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.

...