使用指令显示 <ng-template> 它在 angular 模板中的位置

Show <ng-template> where it is located in angular template using a directive

提问人:Felice Lombardi 提问时间:11/17/2023 更新时间:11/17/2023 访问量:19

问:

我正在创建一个 Angular 指令,用于元素以显示 .<input type='text' formControlName="queryInput">formControl

这个想法是:

  1. 如果将 an 传递给指令,则它应使用提供的模板来显示错误消息,并将模板定位在声明它的位置[errorTemplateRef]="errorTemplate"

  2. 如果提供了 no,则指令应在输入元素后添加一个简单的右键。errorTemplateRef<div>Validation error</div>

我在实现第 2 点时没有问题。但是,我遇到了第 1 点的问题。 我无法弄清楚如何在通过指令向其传递上下文(错误消息)时“呈现”模板。

你能帮我解决这个问题吗?

Angular 模板:


<form
    [formGroup]="formGroup"
    (ngSubmit)="onSubmitSearch()"
    class="flex w-full shadow-default"
>
    <input
        formControlName="queryInput"
        type="text"
        class="w-full h-full border-0 bg-white rounded-l-md"
        appInput
        [showError]="showError"
        [errorTemplateRef]="errorTemplate"
    />
    <button
        type="submit"
        class="flex justify-center items-center w-29 ml-1 p-3 bg-white rounded-r-md"
    >
        <span class="w-3/6 text-blue font-semibold text-sm tracking-wide uppercase">Cerca</span>

        <app-icon
            class="w-3/6 text-indaco"
            name="mdi:magnify"
            size="16"
        ></app-icon>
    </button>
</form>

<ng-template
    #errorTemplate
    let-message
    ><p class="text-crimson-red">Error: {{ message }}</p>
</ng-template>

appInput 指令 - 此时它用一个简单的 ,但我想使用它


@Directive({
    selector: '[appInput]',
    standalone: true,
})
export class AppInputDirective implements OnInit, OnChanges, OnDestroy {
    @Input() showError = false;
    @Input() errorTemplateRef: TemplateRef<unknown> | undefined;

    private _errorElement: HTMLElement | undefined;
    private _errorPlaceholderElement: HTMLElement | undefined;
    private _errorSubscription: Subscription | undefined;

    constructor(
        private _el: ElementRef<HTMLInputElement>,
        private _renderer: Renderer2,
        private _ngControl: NgControl,
    ) {}

    ngOnInit(): void {
        // If there's a placeholder to display the error, use it;
        // otherwise, create a div element below the input.
        this._errorPlaceholderElement = <HTMLElement>this.errorTemplateRef?.elementRef.nativeElement;

        const inputParent = <HTMLElement>this._renderer.parentNode(this._el.nativeElement);

        // Create a div element for the error message.
        this._errorElement = <HTMLElement>this._renderer.createElement('div');
        this._renderer.addClass(this._errorElement, 'text-coral-red');

        const wrapperElement = <HTMLElement>this._renderer.createElement('div');

        if (!this._errorPlaceholderElement && inputParent) {
            const wrapperClasses = ['flex', 'flex-col', 'gap-1'];
            inputParent.classList.contains('w-full') && wrapperClasses.push('w-full');
            inputParent.classList.contains('h-full') && wrapperClasses.push('h-full');
            wrapperElement.classList.add(...wrapperClasses);

            this._renderer.insertBefore(inputParent, wrapperElement, this._el.nativeElement);
            this._renderer.appendChild(wrapperElement, this._el.nativeElement);

            // Insert the error element after the target element.
            this._renderer.insertBefore(inputParent, this._errorElement, this._el.nativeElement.nextSibling);
        }

        // If there's a placeholder, position the error in its place.
        if (this._errorPlaceholderElement) {
            // Replace the original element with the new one.
            const parent = <HTMLElement>this._renderer.parentNode(this._errorPlaceholderElement);
            this._renderer.insertBefore(parent, this._errorElement, this._errorPlaceholderElement);
            this._renderer.removeChild(parent, this._errorPlaceholderElement);
        }
    }

    ngOnChanges(changes: SimpleChanges): void {
        if (changes['showError']?.currentValue) {
            this._errorSubscription = this._ngControl.statusChanges
                ?.pipe(startWith(1))
                .subscribe(() => this._updateErrorDisplay());
        } else {
            this._errorSubscription?.unsubscribe();
        }
    }

    ngOnDestroy(): void {
        this._errorSubscription?.unsubscribe();
    }

    private _updateErrorDisplay(): void {
        this._renderer.setProperty(this._errorElement, 'innerText', null);
        if (this._ngControl.errors) {
            const firstErrorKey = Object.keys(this._ngControl.errors)[0];
            const errorValue = <{ [key: string]: unknown }>this._ngControl.errors[firstErrorKey];

            const errorMessage = fillInPlaceholders(validationErrorString[firstErrorKey], errorValue);
            if (errorMessage) {
                this._renderer.setProperty(this._errorElement, 'innerText', errorMessage);
            }
        }
    }
}

在这一刻,它用一个简单的代替了 ng-tamplate,但我想使用它

角度 验证 输入 angular-reactive-forms 指令

评论


答: 暂无答案