如何动态地将NG_VALUE_ACCESSOR组件添加到被动形式?

Jul*_*ien 6 typescript angular

我们希望NG_VALUE_ACCESSOR使用带有ComponentFactoryResolver和的自定义指令动态地将组件添加到被动表单ViewContainerRef.问题是我们无法将formControlName分配给动态添加的组件,我们无法从组件中获取访问者值.

我们尝试了几种不同的选项,但它们都没有为我们工作(直接将formControlName添加到ngContainer会引发错误,也是ngComponentOutlet的一个选项,但是我们无法为组件提供参数).

我们在plunker中创建了一个静态测试用例(我们想要达到的结果)和一个使用指令的动态测试用例,我们无法将formControlName分配给组件.我们将在下面的评论中提供链接.

yur*_*zui 5

您可以尝试扩展NgControl。这是简单的实现。但这可能更复杂。

动态面板指令

@Directive({
    selector: '[dynamic-panel]'
})
export class DynamicPanelDirective extends NgControl implements OnInit  {

    name: string;

    component: ComponentRef<any>;

    @Output('ngModelChange') update = new EventEmitter();

    _control: FormControl;

    constructor(
        @Optional() @Host() @SkipSelf() private parent: ControlContainer,
        private resolver: ComponentFactoryResolver,
        private container: ViewContainerRef) {
        super();
    }

    ngOnInit() {
        let component = this.resolver.resolveComponentFactory<GeneralPanelComponent>(GeneralPanelComponent);
        this.name = 'general';
        this.component = this.container.createComponent(component);
        this.valueAccessor = this.component.instance;
        this._control = this.formDirective.addControl(this);
    }

    get path(): string[] {
        return [...this.parent.path !, this.name];
    }

    get formDirective(): any { return this.parent ? this.parent.formDirective : null; }

    get control(): FormControl { return this._control; }

    get validator(): ValidatorFn|null { return null; }

    get asyncValidator(): AsyncValidatorFn { return null; }

    viewToModelUpdate(newValue: any): void {
        this.update.emit(newValue);
    }

    ngOnDestroy(): void {
        if (this.formDirective) {
            this.formDirective.removeControl(this);
        }
        if(this.component) {
            this.component.destroy();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

改装柱塞

所以

如何动态地将NG_VALUE_ACCESSOR组件添加到反应形式?

this.valueAccessor = this.component.instance;
Run Code Online (Sandbox Code Playgroud)

就我而言

如果要使用验证器,请参阅此柱塞

  • 为什么这篇文章和其他帖子中的大多数 1 年前或以上的 plunker 示例不再起作用?有什么方法可以快速修复或将它们移至 stackblitz?谢谢 (2认同)

Nat*_*han 5

当前接受的答案适用于原始帖子中的确切场景,但稍微不同的场景让我看到了这篇文章。感谢@yurzui,我能够根据他的回答找到解决方案。

我自己的解决方案允许使用模板中常用的声明性绑定将(包括 ngModel、反应式表单和验证器)完全集成到 Angular 表单生态系统中。所以我把它贴在这里,以防其他人来这里找这个。

您可以在StackBlitz查看

import {
    Component,
    ComponentFactoryResolver,
    forwardRef,
    Host,
    Injector,
    SkipSelf,
    ViewContainerRef,
} from '@angular/core';
import { ControlContainer, NgControl, NG_VALUE_ACCESSOR } from '@angular/forms';

import { CustomInputComponent } from './custom-input.component';

@Component({
    selector: 'app-form-control-outlet',
    template: ``,
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => FormControlOutletComponent),
            multi: true,
        },
    ],
})
export class FormControlOutletComponent {
    constructor(
        public injector: Injector,
        private componentFactoryResolver: ComponentFactoryResolver,
        private viewContainerRef: ViewContainerRef,
    ) {}

    public ngOnInit(): void {
        const ngControl = this.injector.get(NgControl);
        const componentFactory = this.componentFactoryResolver.resolveComponentFactory(
            /**
              * Retrieve this component in whatever way you would like,
              * it could be based on an @Input or from a service etc...
              */
            CustomInputComponent,
        );

        const componentRef = this.viewContainerRef.createComponent(
            componentFactory,
        );

        ngControl.valueAccessor = componentRef.instance;
    }
}
Run Code Online (Sandbox Code Playgroud)