【问题标题】:How to implement ngModel on custom elements?如何在自定义元素上实现 ngModel?
【发布时间】:2016-05-11 00:37:16
【问题描述】:

给定一个简单的input 元素,我可以这样做:

<input [(ngModel)]="name" /> {{ name }}

这不适用于我的自定义元素:

<my-selfmade-combobox [(ngModel)]="name" values="getValues()" required></my-selfmade-combobox>

我该如何实现它?

【问题讨论】:

标签: angular angular2-template angular2-directives


【解决方案1】:

如果你真的需要[(ngModel)](支持ngForm,不像[(myProp)]方法), 我认为这个链接会回答你的问题:

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

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

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

【讨论】:

【解决方案2】:

您可以自己实现自定义双向绑定。对于 angular 10,请参见官方示例 SizerComponent,这里 [(size)] 的行为就像 [(ngModel)]

<app-sizer [(size)]="fontSizePx"></app-sizer>

【讨论】:

    【解决方案3】:

    第 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

    
        <my-cool-element [(value)]="myValue">
                  <!-- ..... -->
         </my-cool-element>
    

    【讨论】:

    • 自定义组件中的[(ngModel)] 部分在哪里实现? 然后在自定义组件中,你接受一些东西并将它绑定到[(ngModel)] no?
    • @mcha,第 3 步是“MyCoolComponent”的 html。目标是使自定义组件与内置的 ngModel 功能兼容。所以在这种情况下,他们现在可以写了;
    【解决方案4】:

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

    只有两行代码:

    1. providers: [createCustomInputControlValueAccessor(MyInputComponent)]

    2. extends 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;
    }
    

    我的输入.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】:

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

      这意味着如果你想为你的组件添加一个 2-way 绑定属性,例如

      <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")(如果您只想确保它是每次值更改时都会更新。)

      您可以阅读有关其工作原理/原因的更详细说明here


      如果您想使用名称 ngModel(因为有额外的指令绑定到带有 ngModel 的元素),或者这是用于 FormControl 元素而不是组件(AKA,用于在 @ 987654332@),那么您将需要使用ControlValueAccessor。关于制作自己的FormControl 及其工作原理的详细说明可以阅读here

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-06-10
        • 1970-01-01
        • 2023-03-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多