Angular 2 - 组件内部的formControlName

Rit*_*XPD 29 angular2-forms angular

我想创建一个可以与FormBuilder API一起使用的自定义输入组件.如何formControlName在组件内添加?

模板:

<label class="custom-input__label"
          *ngIf="label">
        {{ label }}
</label>
<input class="custom-input__input" 
       placeholder="{{ placeholder }}"
       name="title" />
<span class="custom-input__message" 
      *ngIf="message">
        {{ message }}
</span>
Run Code Online (Sandbox Code Playgroud)

零件:

import {
    Component,
    Input,
    ViewEncapsulation
} from '@angular/core';

@Component({
    moduleId: module.id,
    selector: 'custom-input',
    host: {
        '[class.custom-input]': 'true'
    },
    templateUrl: 'input.component.html',
    styleUrls: ['input.component.css'],
    encapsulation: ViewEncapsulation.None,
})
export class InputComponent {
    @Input() label: string;
    @Input() message: string;
    @Input() placeholder: string;
}
Run Code Online (Sandbox Code Playgroud)

用法:

<custom-input label="Title" 
           formControlName="title" // Pass this to input inside the component>
</custom-input>
Run Code Online (Sandbox Code Playgroud)

web*_*now 32

您不应该将formControlName属性添加到自定义组件的模板中的输入字段.您应该根据formControlName最佳实践添加自定义输入元素本身.

您可以在自定义输入组件中使用的controlValueAccessor界面是,只要自定义输入的模板中的输入字段事件发生更改或模糊,您的自定义输入就会更新值.

它提供了自定义输入的表单控件行为与您为该自定义表单控件提供的UI之间的连接(以更新值或其他需求).

下面是TypeScript中自定义输入组件的代码.

import { Component, Input, forwardRef, AfterViewInit, trigger, state, animate, transition, style, HostListener, OnChanges, ViewEncapsulation, ViewChild, ElementRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor, FormControl } from '@angular/forms';

export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => InputComponent),
    multi: true
};

@Component({
  selector: 'inv-input',
  templateUrl:'./input-text.component.html',
    styleUrls: ['./input-text.component.css'],
    encapsulation: ViewEncapsulation.None,
  providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR],
    animations:[trigger(
        'visibilityChanged',[
            state('true',style({'height':'*','padding-top':'4px'})),
            state('false',style({height:'0px','padding-top':'0px'})),
            transition('*=>*',animate('200ms'))
        ]
    )]
})

export class InputComponent implements ControlValueAccessor, AfterViewInit, OnChanges {

    // Input field type eg:text,password
    @Input()  type = "text"; 

    // ID attribute for the field and for attribute for the label
    @Input()  idd = ""; 

    // The field name text . used to set placeholder also if no pH (placeholder) input is given
    @Input()  text = ""; 

    // placeholder input
    @Input()  pH:string; 

    //current form control input. helpful in validating and accessing form control
    @Input() c:FormControl = new FormControl(); 

    // set true if we need not show the asterisk in red color
    @Input() optional : boolean = false;

    //@Input() v:boolean = true; // validation input. if false we will not show error message.

    // errors for the form control will be stored in this array
    errors:Array<any> = ['This field is required']; 

    // get reference to the input element
    @ViewChild('input')  inputRef:ElementRef; 


    constructor() {

    }

    ngOnChanges(){

    }

    //Lifecycle hook. angular.io for more info
    ngAfterViewInit(){ 
        // set placeholder default value when no input given to pH property      
        if(this.pH === undefined){
            this.pH = "Enter "+this.text; 
        }

        // RESET the custom input form control UI when the form control is RESET
        this.c.valueChanges.subscribe(
            () => {
                // check condition if the form control is RESET
                if (this.c.value == "" || this.c.value == null || this.c.value == undefined) {
                    this.innerValue = "";      
                    this.inputRef.nativeElement.value = "";                 
                }
            }
        );
    }

   //The internal data model for form control value access
    private innerValue: any = '';

    // event fired when input value is changed . later propagated up to the form control using the custom value accessor interface
    onChange(e:Event, value:any){
        //set changed value
        this.innerValue = value;
        // propagate value into form control using control value accessor interface
        this.propagateChange(this.innerValue);

        //reset errors 
        this.errors = [];
        //setting, resetting error messages into an array (to loop) and adding the validation messages to show below the field area
        for (var key in this.c.errors) {
            if (this.c.errors.hasOwnProperty(key)) {
                if(key === "required"){
                    this.errors.push("This field is required");
                }else{
                    this.errors.push(this.c.errors[key]);
                }              
            }
        }
    }



    //get accessor
    get value(): any {
        return this.innerValue;
    };

    //set accessor including call the onchange callback
    set value(v: any) {
        if (v !== this.innerValue) {
            this.innerValue = v;
        }
    }

    //propagate changes into the custom form control
    propagateChange = (_: any) => { }

    //From ControlValueAccessor interface
    writeValue(value: any) {
        this.innerValue = value;
    }

    //From ControlValueAccessor interface
    registerOnChange(fn: any) {
        this.propagateChange = fn;
    }

    //From ControlValueAccessor interface
    registerOnTouched(fn: any) {

    }
}
Run Code Online (Sandbox Code Playgroud)

下面是自定义输入组件的模板HTML

<div class="fg">
      <!--Label text-->
      <label [attr.for]="idd">{{text}}<sup *ngIf="!optional">*</sup></label>
      <!--Input form control element with on change event listener helpful to propagate changes -->
      <input type="{{type}}" #input id="{{idd}}" placeholder="{{pH}}" (blur)="onChange($event, input.value)">
      <!--Loop through errors-->
      <div style="height:0px;" [@visibilityChanged]="!c.pristine && !c.valid" class="error">
            <p *ngFor="let error of errors">{{error}}</p>
      </div>
</div>
Run Code Online (Sandbox Code Playgroud)

下面是自定义输入组件,可以在fromGroup或单独使用

<inv-input formControlName="title" [c]="newQueryForm.controls.title" [optional]="true" idd="title" placeholder="Type Title to search"
          text="Title"></inv-input>
Run Code Online (Sandbox Code Playgroud)

通过这种方式,如果您实现自定义表单控件,则可以轻松应用自定义验证程序指令,并在该表单控件上累积错误以显示错误.

可以模仿相同的样式以上述方式开发自定义选择组件,单选按钮组,复选框,文本区域,文件上载等,并根据表单控件的行为要求进行微小更改.


dar*_*o99 17

Angular 8 和 9:在自定义组件中使用 viewProvider。工作示例:

@Component({
    selector: 'app-input',
    templateUrl: './input.component.html',
    styleUrls: ['./input.component.scss'],
    viewProviders: [
        {
            provide: ControlContainer,
            useExisting: FormGroupDirective
        }
    ]
})
Run Code Online (Sandbox Code Playgroud)

现在,当您分配 formControlName 时,您的组件将自身附加到父表单。

<input matInput formControlName="{{name}}">
Run Code Online (Sandbox Code Playgroud)

或者

<input matInput [formControlName]='name'>
Run Code Online (Sandbox Code Playgroud)

  • 请您完成这个,如何输入和使用这个属性? (3认同)
  • 找到这个[stackoverflow答案](/sf/ask/3702516191/),值得尝试 (2认同)

P. *_*ney 9

这里的主要思想是你必须将FormControl链接到FormGroup,这可以通过将FormGroup传递给每个输入组件来完成......

因此,您的输入模板可能如下所示:

<div [formGroup]="form">
    <label *ngIf="label">{{ label }}</label>
    <input [formControlName]="inputName" />
    <span *ngIf="message">{{ message }}</span>
</div>
Run Code Online (Sandbox Code Playgroud)

其中@Input的用于输入组件会form,label,inputNamemessage.

它会像这样使用:

<form [FormGroup]="yourFormGroup">
    <custom-input
        [form]="yourFormGroup"
        [inputName]="thisFormControlName"
        [message]="yourMessage"
        [label]="yourLabel">
    </custom-input>
</form>
Run Code Online (Sandbox Code Playgroud)

有关自定义表单输入组件的更多信息,我建议您查看Angular的动态表单.此外,如果您想了解有关如何获取@Input@Output工作的更多信息,请查看Angular Docs Here

  • 这里的问题是每个输入都有一个表单包装器,这并不理想 (2认同)
  • 这个答案有点过时了,Angular现在有[`FormGroupName`](https://angular.io/docs/ts/latest/api/forms/index/FormGroupName-directive.html),其工作方式类似于`FormControlName`,也就是说,它不是包装每个输入,而是简单地在theroy中添加一个指向`FormGroup`的指针 (2认同)