有没有一种方法可以在angular的<input />元素上动态设置formControlName?

时间:2019-02-20 16:18:11

标签: javascript angular typescript angular-components angular-forms

我正在编写一个旨在简化/同化表单外观和交互方式的组件。代码看起来像这样:

用法示例

http://ourserver/testsys/mynicepgm.aspx

您可以看到... <my-form-input labelKey = "email" controlName="emailAddress" [form]="userForm"> <input myInput class="form-control" type="email" formControlName="emailAddress" /> </my-form-input> ... "emailAddress" 的形式传递给MyFormInputComponent,并第二次传递给controlName上的 FormControlName指令。 <input>元素。我只想通过一次,这样我的最终用户就不必这样做。

是否有解决这个问题的好方法,或者这只是我应该接受的一个约束(如果是,欢迎解释为什么存在此约束)?代码如下所示。

我尝试了两种方法:

  1. @HostBinding("attr.formControlName")组件中设置MyInput批注。我可以通过这种方式在元素上操作名为formcontrolname的属性,但是它不会触发Angular Forms需要在组中正确注册控件的指令。
  2. 要求用户向formControlName元素提供<input>并从其余组件中读取该值。这可能有效,但是我必须直接通过ElementRefwhich is not recommended访问DOM。与DOM交互的推荐路线-Renderer-doesn't seem to expose any ability to read attributes either

my-form-input.component.ts

@Component({
    selector: 'my-form-input',
    templateUrl: './my-form-input.component.html',
    styleUrls: ['./my-form-input.component.scss']
})
export class MyFormInputComponent implements OnInit, AfterContentInit {
    @Input()
    labelKey: string;

    @Input()
    controlName: string;

    @Input()
    form: FormGroup;

    @ContentChild(MyInputDirective)
    input: MyInputDirective;

    ngAfterContentInit(): void {        
        this.initInput();
    }

    /**
     * Updates the input we project into the template
     */
    private initInput() {
        this.input.updatePlaceholder(this.labelKey);
        // I'd like to somehow edit the FormControlName directive applied to the input here
    }
}

my-form-input.component.html

<label>{{ labelKey | translate }}</label>
<ng-content></ng-content>
<my-input-error [control]="form.controls[controlName]" [name]="labelKey | translate" />

my-input.directive.ts

@Directive({
    selector: '[myInput]'
})
export class myInputDirective implements OnInit {
    private placeholderKey = ""; 

    @HostBinding("placeholder")
    private placeholder: string;

    updatePlaceholder(placeholderKey: string) {
        this.placeholderKey = placeholderKey;
        this.placeholder = this.translateService.instant(this.placeholderKey);
    }

    constructor(private translateService: TranslateService) {
    }
}

my-form-error.component.ts

// Not shown since not relevant.

1 个答案:

答案 0 :(得分:0)

我仍然不确定确切的解释,但是有些推理暗示了我可能会迷路的地方。

我假设我的组件拥有它要投影的元素,但实际上我认为这是不正确的。您可以在模板中设置属性/指令。这意味着您最好将想要拥有/控制的所有元素都包含在模板中,而不是仅对其进行投影。

在这种情况下,这将导致您为特定的表单控件(例如<input><textarea>等)制作单独的组件。这就是我最后要做的。无论如何,从设计端点来看这是更好的方法—永远不可能发生一个用于包装所有可能的表单控件的组件。像我在问题中那样,设计一个复制某些属性的表单控件,或者创建特定的组件。或两者兼有(只需将最常用的表单控件放入组件中,将一次性的问题包装在投影组件中即可)。

以下一些博客帮助我找到了自己的路:

  1. https://medium.com/@vadimkorr/implementing-nested-custom-controls-in-angular-5-c115c68e6b88
  2. https://blog.angularindepth.com/never-again-be-confused-when-implementing-controlvalueaccessor-in-angular-forms-93b9eee9ee83