Я создаю директиву для моего приложения, которая динамически создает компонент и внедряет его сразу после элемента, который он украшает.
Директива работает отлично, за исключением одной проблемы: когда компонент добавляется в представление с помощью директива, он содержит тег обтекания моего шаблона:
Есть ли способ аннулировать упаковку <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>