如何在自定义元素上实现ngModel?


Answers:


43

我认为此链接将回答您的问题:

我们需要实现两件事来实现这一目标:

  • 提供表单组件逻辑的组件。它不需要输入,因为它将由ngModel本身提供
  • 一个ControlValueAccessor将实现此组件和ngModel/之间的桥梁的自定义ngControl

上一个链接为您提供了完整的示例...



34

[(ngModel)]="item" 是的简写 [ngModel]="item" (ngModelChange)="item = $event"

这意味着,例如,如果要向组件添加2向绑定属性

<app-my-control [(myProp)]="value"></app-my-control>

您需要在组件中做的就是添加

@Input()
myProp: string;

// Output prop name must be Input prop name + 'Change'
// Use in your component to write an updated value back out to the parent
@Output()
myPropChange = new EventEmitter<string>();

Input将处理写入操作,并将新值写回父级,只需调用即可this.myPropChange.emit("Awesome")(如果您只想确保每次值更改时都将其更新,则可以将属性放到属性的setter中。)

您可以在此处阅读有关其工作方式/原因的详细说明。


如果您想使用名称ngModel(因为有额外的指令绑定到ngModel的元素),或者这是针对Form Control元素而不是组件(也就是ngForm中使用的AKA),那么您将需要玩与ControlValueAccessor。可以在此处阅读有关制作自己的表单控件及其工作原理的详细说明。


1
大!保存了一天!
钢琴家

5

我在共享组件中一次实现了ngModel的输入,从那时起我可以非常简单地扩展它。

只有两行代码:

  1. 提供者:[createCustomInputControlValueAccessor(MyInputComponent)]

  2. 扩展InputComponent

my-input.component.ts

import { Component, Input } from '@angular/core';
import { InputComponent, createCustomInputControlValueAccessor } from '../../../shared/components/input.component';
@Component({
   selector: 'my-input',
   templateUrl: './my-input-component.component.html',
   styleUrls: ['./my-input-component.scss'],
   providers: [createCustomInputControlValueAccessor(MyInputComponent)]
})
export class MyInputComponent extends InputComponent {
    @Input() model: string;
}

my-input.component.html

<div class="my-input">
    <input [(ngModel)]="model">
</div>

input.component.ts

import { Component, forwardRef, ViewChild, ElementRef, OnInit } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
export function createCustomInputControlValueAccessor(extendedInputComponent: any) {
    return {
        provide: NG_VALUE_ACCESSOR,
        useExisting: forwardRef(() => extendedInputComponent),
        multi: true
    };
}

@Component({
    template: ''
})
export class InputComponent implements ControlValueAccessor, OnInit {
    @ViewChild('input') inputRef: ElementRef;

    // The internal data model
    public innerValue: any = '';

    // Placeholders for the callbacks which are later provided
    // by the Control Value Accessor
    private onChangeCallback: any;

    // implements ControlValueAccessor interface
    writeValue(value: any) {
        if (value !== this.innerValue) {
            this.innerValue = value;
        }
    }
    // implements ControlValueAccessor interface
    registerOnChange(fn: any) {
        this.onChangeCallback = fn;
    }

    // implements ControlValueAccessor interface - not used, used for touch input
    registerOnTouched() { }

    // change events from the textarea
    private onChange() {
        const input = <HTMLInputElement>this.inputRef.nativeElement;
        // get value from text area
        const newValue = input.value;

        // update the form
        this.onChangeCallback(newValue);
    }
    ngOnInit() {
        const inputElement = <HTMLInputElement>this.inputRef.nativeElement;
        inputElement.onchange = () => this.onChange();
        inputElement.onkeyup = () => this.onChange();
    }
}

5

步骤1:添加以下providers属性:

@Component({
    selector: 'my-cool-element',
    templateUrl: './MyCool.component.html',
    styleUrls: ['./MyCool.component.css'],
    providers: [{   // <================================================ ADD THIS
        provide: NG_VALUE_ACCESSOR,
        useExisting: forwardRef(() => MyCoolComponent),
        multi: true
    }]
})

步骤2:实施ControlValueAccessor

    export class MyCoolComponent implements ControlValueAccessor {

      private _value: string;
      // Whatever name for this (myValue) you choose here, use it in the .html file.
      public get myValue(): string { return this._value }
      public set myValue(v: string) {
        if (v !== this._value) {     
          this._value = v;
          this.onChange(v);
        }
      }

      constructor() {}

      onChange = (_) => { };
      onTouched = () => { };

      writeValue(value: any): void {    
        this.myValue = value;
      }
      registerOnChange(fn: any): void {
        this.onChange = fn;
      }
      registerOnTouched(fn: any): void {
        this.onTouched = fn;
      }
      setDisabledState?(isDisabled: boolean): void {
        throw new Error("Method not implemented.");
      }

    }

步骤3:在html中,绑定您想要的任何控件myValue


    <mat-select [(value)]="myValue">
                <mat-option *ngFor="let item of warehouses" [value]="item.id">
                    {{item.name}}
                </mat-option>
     </mat-select>

[(ngModel)]自定义组件中实现的零件在哪里?<cutom-component model =“ something”>然后在自定义组件中,您接受某些东西并将其绑定为[(ngModel)]否?
mcha
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.