Angular 8 - создание компонента Dynami c без тега ng-component - PullRequest
0 голосов
/ 29 апреля 2020

Я создаю директиву для моего приложения, которая динамически создает компонент и внедряет его сразу после элемента, который он украшает.

Директива работает отлично, за исключением одной проблемы: когда компонент добавляется в представление с помощью директива, он содержит тег обтекания моего шаблона:

  • Шаблон компонента: <mat-error>...</mat-error>

  • Но когда компонент добавлен в представление html - это: <ng-component><mat-error>...</mat-error></ng-component>

Есть ли способ аннулировать упаковку <ng-component></ng-component>?

Вот исходный код моей директивы:

import { Directive, Self, Inject, Optional, Host, ComponentRef, ViewContainerRef, ComponentFactoryResolver, OnDestroy } from '@angular/core';
import { NgControl } from '@angular/forms';
import { untilDestroyed } from 'ngx-take-until-destroy';
import { InjectionToken } from '@angular/core';
import { merge, Observable, EMPTY } from 'rxjs';
import { FormSubmitDirective } from './form-submit.directive';
import { ControlErrorComponent } from './control-error.component';
import { ControlErrorContainerDirective } from './control-error-container.directive';

export const defaultErrors = {
    required: (error) => `This field is required`,
    minlength: ({ requiredLength, actualLength }) => `Expect ${requiredLength} but got ${actualLength}`
}

export const FORM_ERRORS = new InjectionToken('FORM_ERRORS', {
    providedIn: 'root',
    factory: () => defaultErrors
});

@Directive({
    selector: '[formControl], [formControlName]'
})
export class ControlErrorsDirective implements OnDestroy{

    submit$: Observable<Event>;
    ref: ComponentRef<ControlErrorComponent>;
    container: ViewContainerRef;

    constructor(
        private vcr: ViewContainerRef,
        @Optional() controlErrorContainer: ControlErrorContainerDirective,
        @Self() private control: NgControl,
        @Optional() @Host() private form: FormSubmitDirective,
        @Inject(FORM_ERRORS) private errors,
        private resolver: ComponentFactoryResolver) {

        this.submit$ = this.form ? this.form.submit$ : EMPTY;
        console.log("Form: ", this.form);
        this.container = controlErrorContainer ? controlErrorContainer.vcr : vcr;
    }    

    ngOnInit() {
        merge(
            this.submit$,
            //this.control.valueChanges,
            this.control.statusChanges,                        
        ).pipe(
            untilDestroyed(this)).subscribe((v) => {                
                const controlErrors = this.control.errors;
                if (controlErrors) {
                    const firstKey = Object.keys(controlErrors)[0];
                    const getError = this.errors[firstKey];
                    const text = getError(controlErrors[firstKey]);
                    this.setError(text);
                } else if (this.ref) {
                    this.setError(null);
                }
            })
    }

    ngOnDestroy(): void {
        console.log("Destroy control-error directive");
    }

    setError(text: string) {

        console.log("set  error: ", text);
        if (!this.ref) {
            console.log("Create error control");

            //
            // Here I am injecting dynamically the control to the view
            //
            const factory = this.resolver.resolveComponentFactory(ControlErrorComponent);
            this.ref = this.vcr.createComponent(factory);
        }

        this.ref.instance.text = text;
    }
}

А вот исходный код компонента, созданного динамически:

import { Component, ChangeDetectionStrategy, Input, ChangeDetectorRef } from '@angular/core';

@Component({
    template: `<mat-error class="help is-danger" [class.hide]="_hide">{{_text}}</mat-error>`,
    changeDetection: ChangeDetectionStrategy.OnPush
   })
   export class ControlErrorComponent {
    _text: string;
    _hide = true;

    @Input() set text(value) {
      if (value !== this._text) {
        this._text = value;
        this._hide = !value;
        this.cdr.detectChanges();
      }
    };

    constructor(private cdr: ChangeDetectorRef) { }

   }

Это вывод HTML при создании элемента управления:

<ng-component class="ng-star-inserted"><mat-error class="help is-danger mat-error" role="alert" id="mat-error-2">This field is required</mat-error></ng-component>
...