我们希望使用NG_VALUE_ACCESSOR
和ComponentFactoryResolver
的自定义指令动态地将ViewContainerRef
组件添加到被动表单中。问题是我们无法将formControlName分配给动态添加的组件,我们无法从组件中获取访问者值。
我们尝试了几种不同的选项,但它们都没有为我们工作(直接将formControlName添加到ngContainer会引发错误,也是ngComponentOutlet的一个选项,但是我们无法为组件提供参数)。
我们在plunker中创建了一个静态测试用例(我们想要达到的结果)和一个使用指令的动态测试用例,我们无法将formControlName分配给组件。我们将在下面的评论中提供链接。
答案 0 :(得分:4)
您可以尝试扩展NgControl
。这是简单的实现。但它可能更复杂。
<强>动态panel.directive.ts 强>
@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();
}
}
}
<强> Modified Plunker 强>
所以
如何动态地将NG_VALUE_ACCESSOR组件添加到被动表单?
this.valueAccessor = this.component.instance;
在我的情况下
如果您想使用验证器,请参阅 Plunker
答案 1 :(得分:3)
当前接受的答案适用于原始帖子中的确切场景,但是略有不同的场景将我带到了该帖子中。感谢@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;
}
}