如何使用角度为6的ngModel创建自定义输入组件?

Ars*_*min 30 angular

由于我使用了大量相同指令和.css类的输入,我想将重复的代码提取到某个组件,如下所示:

  @Component({
  selector: "app-input",
  template: `
    <div class="...">
      <input type="..." name="..." class="..." [(ngModel)]="value" someDirectives...>
      <label for="...">...</label>
    </div>
  `,
  ...
  })
  export class InputComponent implements OnInit {
    // some implementation connecting external ngModel with internal "value" one
  }
Run Code Online (Sandbox Code Playgroud)

这里的问题是以这样的方式创建一个组件,它可以与ngModel一起用作普通输入:

<app-input [(ngModel)]="externalValue" ... ></app-input>
Run Code Online (Sandbox Code Playgroud)

我在互联网上找到了几个可以部分或完全过时的解决方案,如: Angular 2自定义表单输入 这可以在角度6中以更好的方式完成吗?

and*_*eas 57

我前段时间遇到了同样的问题,想要分享一个与Angular 2+一起使用的最小例子.

假设您想在应用中的任何位置使用以下代码:

<app-input-slider [(ngModel)]="inputSliderValue"></app-input-slider>
Run Code Online (Sandbox Code Playgroud)

1)现在创建一个名为的组件InputSlider.

2)在input-slider.component.html,添加以下内容:

<input type="range" [(ngModel)]="value" (ngModelChange)="updateChanges()">
Run Code Online (Sandbox Code Playgroud)

3)现在我们必须在input-slider.component.ts文件中做一些工作:

import {Component, forwardRef, OnInit} from "@angular/core";
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from "@angular/forms";

@Component({
    selector: "app-input-slider",
    templateUrl: "./input-slider.component.html",
    styleUrls: ["./input-slider.component.scss"],
    providers: [{
        provide: NG_VALUE_ACCESSOR,
        useExisting: forwardRef(() => InputSliderComponent),
        multi: true
    }]

})
export class InputSliderComponent implements ControlValueAccessor {

    /**
     * Holds the current value of the slider
     */
    value: number = 0;

    /**
     * Invoked when the model has been changed
     */
    onChange: (_: any) => void = (_: any) => {};

    /**
     * Invoked when the model has been touched
     */
    onTouched: () => void = () => {};

    constructor() {}

    /**
     * Method that is invoked on an update of a model.
     */
    updateChanges() {
        this.onChange(this.value);
    }

    ///////////////
    // OVERRIDES //
    ///////////////

    /**
     * Writes a new item to the element.
     * @param value the value
     */
    writeValue(value: number): void {
        this.value = value;
        this.updateChanges();
    }

    /**
     * Registers a callback function that should be called when the control's value changes in the UI.
     * @param fn
     */
    registerOnChange(fn: any): void {
        this.onChange = fn;
    }

    /**
     * Registers a callback function that should be called when the control receives a blur event.
     * @param fn
     */
    registerOnTouched(fn: any): void {
        this.onTouched = fn;
    }

}
Run Code Online (Sandbox Code Playgroud)

当然,您可以使用此类添加更多功能和值检查,但我希望它能为您提供一些想法.

快速解释:

诀窍是添加提供程序NG_VALUE_ACCESSOR on the decorator of the class and implementControlValueAccessor`.

然后我们需要定义函数writeValue,registerOnChangeregisterOnTouched.后两者直接调用组件的创建.这就是为什么我们需要变量(例如onChangeonTouched- 但你可以根据自己的喜好命名它们.

最后,我们需要定义一个函数,让组件知道更新底层的ngModel.我用这个功能做到了updateChanges.每当值发生变化时,都需要调用它,无论是从外部(这就是调用它的原因writeValue),还是从内部(这就是从html调用它的原因ngModelChange).


ome*_*mer 23

这也可以这样做,当你创建一个双向绑定[()]时,你可以使用相同的名称+'change'(在我们的例子中是inputModel和inputModelChange)将它绑定到一个函数,这样ngModel会在你更新时更新触发inputModelChange.emit('updatedValue').并且您只需要在组件内声明一次.

import { Component, OnInit, Output, Input, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-input',
  template: `  <input type="text" [(ngModel)]="inputModel" (ngModelChange)="inputModelChange.emit(inputModel)"/>`,
  styleUrls: ['./app-input.component.scss']
})
export class AppInputComponent {
  @Input() inputModel: string;
  @Output() inputModelChange = new EventEmitter<string>();
}
Run Code Online (Sandbox Code Playgroud)

  • 目前,我正使用此解决方案。唯一的问题是它没有在接口中使用ngModel,而是在使用其他特殊字段(在您的情况下为“ inputModel”)。但是我发现,与为任何新的shell输入或我创建的其他组件正确实现整个ngModel相比,此缺点要麻烦得多。 (2认同)
  • 这是很棒的东西@omer ...一千个谢谢! (2认同)

Ped*_*ani 12

如果您不关心[ngModel]以模板模型或[formControl]反应形式绑定变量,则可以使用omer answer

除此以外:

  1. NG_VALUE_ACCESSOR注入令牌添加到您的组件定义中:

    import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
    
    @Component({
       ...,
       providers: [
         {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => AppInputComponent),
            multi: true
         }
       ]
    })
    
    Run Code Online (Sandbox Code Playgroud)
  2. 实施ControlValueAccessor接口:

    export class AppInputComponent implements ControlValueAccessor {
    
      writeValue(obj: any): void {
        // Step 3
      }
    
      registerOnChange(fn: any): void {
        this.onChange = fn;
      }
    
      registerOnTouched(fn: any): void {
        this.onTouched = fn;
      }
    
      setDisabledState?(isDisabled: boolean): void {
      }
    
      onChange: any = () => { };
    
      onTouched: any = () => { };
    
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 管理value何时更改:

    private _value;
    
    public get value(){
      return this._value;
    }
    
    public set value(v){
      this._value = v;
      this.onChange(this._value);
      this.onTouched();
    }
    
    writeValue(obj: any): void {
      this._value = obj;
    }
    
    // Optional
    onSomeEventOccured(newValue){
      this.value = newValue;
    }
    
    Run Code Online (Sandbox Code Playgroud)

现在您可以使用 <app-input [(ngModel)]="externalValue" ... ></app-input>

  • 这是正确的答案 (2认同)