Angular 2 - 组件内的 formControlName [英] Angular 2 - formControlName inside component

查看:28
本文介绍了Angular 2 - 组件内的 formControlName的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

模板:

组件:

import {成分,输入,视图封装来自'@angular/core';@成分({模块 ID:module.id,选择器:'自定义输入',主持人: {'[class.custom-input]': '真'},templateUrl: 'input.component.html',styleUrls: ['input.component.css'],封装:ViewEncapsulation.None,})导出类 InputComponent {@Input() 标签:字符串;@Input() 消息:字符串;@Input() 占位符:字符串;}

用法:

</自定义输入>

解决方案

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

在这里,您可以在自定义输入组件中使用 controlValueAccessor 接口,以便在自定义输入模板中发生输入字段事件时更新自定义输入的值改变或模糊.

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

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

import { Component, Input, forwardRef, AfterViewInit, trigger, state, animate, transition, style, HostListener, OnChanges, ViewEncapsulation, ViewChild, ElementRef } from '@angular/核';从@angular/forms"导入 { NG_VALUE_ACCESSOR, ControlValueAccessor, FormControl };导出 const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = {提供:NG_VALUE_ACCESSOR,useExisting: forwardRef(() => InputComponent),多:真};@成分({选择器:'inv-input',templateUrl:'./input-text.component.html',styleUrls: ['./input-text.component.css'],封装:ViewEncapsulation.None,提供者:[CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR],动画:[触发器('可见性改变',[state('true',style({'height':'*','padding-top':'4px'})),state('false',style({height:'0px','padding-top':'0px'})),过渡('*=>*',animate('200ms'))])]})导出类 InputComponent 实现 ControlValueAccessor、AfterViewInit、OnChanges {//输入字段类型 eg:text,password@Input() type = "text";//字段的 ID 属性和标签的属性@Input() idd = "";//字段名称 text .如果没有给出 pH(占位符)输入,也用于设置占位符@Input() text = "";//占位符输入@Input() pH 值:字符串;//当前表单控件输入.有助于验证和访问表单控件@Input() c:FormControl = new FormControl();//如果我们不需要以红色显示星号,则设置为 true@Input() 可选:boolean = false;//@Input() v:boolean = true;//验证输入.如果为 false,我们将不会显示错误消息.//表单控件的错误将存储在此数组中错误:数组<任何>= ['此字段为必填'];//获取对输入元素的引用@ViewChild('input') inputRef:ElementRef;构造函数(){}ngOnChanges(){}//生命周期钩子.angular.io 了解更多信息ngAfterViewInit(){//当没有给 pH 属性输入时设置占位符默认值if(this.pH === 未定义){this.pH = "输入"+this.text;}//当表单控件RESET时RESET自定义输入表单控件UIthis.c.valueChanges.subscribe(() =>{//检查表单控件是否为 RESET 的条件if (this.c.value == "" || this.c.value == null || this.c.value == undefined) {this.innerValue = "";this.inputRef.nativeElement.value = "";}});}//表单控件值访问的内部数据模型私人内部价值:任何 = '';//当输入值改变时触发的事件.后来使用自定义值访问器接口传播到表单控件onChange(e:Event, value:any){//设置改变的值this.innerValue = 值;//使用控件值访问器接口将值传播到表单控件中this.propagateChange(this.innerValue);//重置错误this.errors = [];//设置,将错误消息重置为数组(循环)并添加验证消息以显示在字段区域下方for(this.c.errors 中的 var 键){如果 (this.c.errors.hasOwnProperty(key)) {if(key === "必填"){this.errors.push("此字段为必填项");}别的{this.errors.push(this.c.errors[key]);}}}}//获取访问器获取值():任何{返回 this.innerValue;};//设置访问器包括调用onchange回调设置值(v:任何){如果 (v !== this.innerValue) {this.innerValue = v;}}//将更改传播到自定义表单控件中传播变化 = (_: 任何) =>{ }//来自ControlValueAccessor接口写值(值:任何){this.innerValue = 值;}//来自ControlValueAccessor接口registerOnChange(fn: 任何) {this.propagateChange = fn;}//来自ControlValueAccessor接口registerOnTouched(fn: any) {}}

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

<!--标签文本--><label [attr.for]="idd">{{text}}<sup *ngIf="!optional">*</sup></label><!--带有on change事件监听器的输入表单控件元素有助于传播更改--><input type="{{type}}" #input id="{{idd}}" placeholder="{{pH}}" (blur)="onChange($event, input.value)"><!--循环错误--><div style="height:0px;"[@visibilityChanged]="!c.pristine && !c.valid" class="error"><p *ngFor="let error of errors">{{error}}</p>

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

<inv-input formControlName="title" [c]="newQueryForm.controls.title" [optional]="true" idd="title" 占位符="键入要搜索的标题"text="标题"></inv-input>

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

可以模仿相同的样式,按照表单控件的行为要求,以上述方式开发自定义选择组件、单选按钮组、复选框、文本区域、文件上传等.

I want to create a custom input component that I can use with the FormBuilder API. How do I add formControlName inside a component?

Template:

<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>

Component:

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;
}

Usage:

<custom-input label="Title" 
           formControlName="title" // Pass this to input inside the component>
</custom-input>

解决方案

You should not be adding formControlName attribute to the input field in the template of your custom component. You should be adding the formControlName on the custom-input element itself as per the best practice.

Here what you can use in your custom-input component is the controlValueAccessor interface to make your custom-input have the value updated whenever there is an event of input field in the template of your custom input changed or blurred.

It provides a connection (to update values or other needs) between the form control behavior of your custom input and the UI you are providing for that custom form control.

Below is the code of a custom input component in 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) {

    }
}

Below is the template HTML for the custom input component

<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>

Below is custom input component which can be used in a fromGroup or individually

<inv-input formControlName="title" [c]="newQueryForm.controls.title" [optional]="true" idd="title" placeholder="Type Title to search"
          text="Title"></inv-input>

In this fashion if you implement your custom form controls you can apply your custom validator directives easily and accumulate the errors on that form control to display your errors.

One can imitate the same style to develop custom select component, radio button group, checkbox, textarea, fileupload etc in the above fashion with minor changes as per what the form control's behavior demands.

这篇关于Angular 2 - 组件内的 formControlName的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆