代码之家  ›  专栏  ›  技术社区  ›  Sinan Samet

无法通过两个级别的ngModel

  •  0
  • Sinan Samet  · 技术社区  · 5 年前

    我试图通过ngModel的第二个子组件,但一旦我这样做,它就不起作用了。 要传递ngModel,我使用这个抽象类:

    export abstract class AbstractValueAccessor implements ControlValueAccessor {
      _value: any = '';
      get value(): any { return this._value; };
      set value(v: any) {
        if (v !== this._value) {
          this._value = v;
          this.onChange(v);
        }
      }
    
      writeValue(value: any) {
        this._value = value;
        this.onChange(value);
      }
    
      onChange = (_) => { };
      onTouched = () => { };
      registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
      registerOnTouched(fn: () => void): void { this.onTouched = fn; }
    }
    
    export function MakeProvider(type: any) {
      return {
        provide: NG_VALUE_ACCESSOR,
        useExisting: forwardRef(() => type),
        multi: true,
      };
    }
    

    然后我有一个扩展抽象类的输入组件:

    @Component({
      selector: 'app-input',
      templateUrl: './input.component.html',
      styleUrls: ['./input.component.scss'],
      providers: [MakeProvider(InputComponent)],
    })
    
    export class InputComponent extends AbstractValueAccessor {
      @Input('displaytext') displaytext: string;
      @Input('placeholder') placeholder: string;
    }
    

    <ion-input 
      [(ngModel)]="value"
      type="text"
      ></ion-input>
    

    像这样很好:

    <app-input [(ngModel)]="value"></app-input>
    

    但是,当我围绕它制作一个组件时,就像这样:

    @Component({
      selector: 'app-form-input-item',
      templateUrl: './form-input-item.component.html',
      styleUrls: ['./form-input-item.component.scss'],
      providers: [MakeProvider(FormInputItemComponent)],
    })
    export class FormInputItemComponent extends AbstractValueAccessor {
      @Input() position: string;
    }
    

    用这种方式:

    <app-item>
      <app-label [position]="position"><ng-content></ng-content></app-label>
      <app-input [(ngModel)]="value"></app-input>
    </app-item>
    

    然后在它的父母身上我叫它:

    <app-form-input-item *ngFor='let item of data' position="floating" [(ngModel)]="item.value">
      <b>{{item.title}}</b>
    </app-form-input-item>
    

    <app-input> <ion-input> 又起作用了。我在这里做错什么了?

    更新:

    <ion-item>
      <ng-content></ng-content>
    </ion-item>
    

    Stackblitz(感谢@Gaurangdorda): https://stackblitz.com/edit/angular-uuoahx

    0 回复  |  直到 5 年前
        1
  •  1
  •   Developer    5 年前

    你可以在这里找到完整的工作示例 StackBlitz Link

    在这里,我们正在创建自定义单曲 Atom ControlValueAccessor . 是。。。根据 Atomic design pattern 整个答案是。。。。

    <input type="text"  [(ngModel)]="value"> 
    

    然后,我们创建一个自定义类 AbstractValueAccessor 控制值访问器 在里面。因此,每当我们需要将任何组件转换为 控制值访问器 抽象值访问器 输入.component.ts

    export class InputComponent extends AbstractValueAccessor {
      ngOnInit() {
      } 
    }
    

    自定义 是。。。

    import { Component, forwardRef, HostBinding, Input } from '@angular/core';
    import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
    const noop = () => {
    };
    export abstract class AbstractValueAccessor implements ControlValueAccessor {     
       //The internal data model
       private innerValue: any = '';
    
       //Placeholders for the callbacks which are later provided
       //by the Control Value Accessor
       private onTouchedCallback: () => void = noop;
       private onChangeCallback: (_: any) => void = noop;
    
       //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;
             this.onChangeCallback(v);
         }
       }
    
       //Set touched on blur
       onBlur() {
         this.onTouchedCallback();
       }
    
       //From ControlValueAccessor interface
       writeValue(value: any) {
          if (value !== this.innerValue) {
             this.innerValue = value;
          }
       }
    
       //From ControlValueAccessor interface
       registerOnChange(fn: any) {
           this.onChangeCallback = fn;
       }
    
       //From ControlValueAccessor interface
       registerOnTouched(fn: any) {
          this.onTouchedCallback = fn;
       }
    }
    
    export function MakeProvider(type: any) {
           return {
                    provide: NG_VALUE_ACCESSOR,
                    useExisting: forwardRef(() => type),
                    multi: true,
           };
    } 
    

    输入.component.ts 你必须提供 MakeProvider()

    import {AbstractValueAccessor,MakeProvider} from '.././abstract-value-accessor';
     @Component({
        selector: 'app-input',
        templateUrl: './input.component.html',
        styleUrls: ['./input.component.css'] ,
        providers: [MakeProvider(InputComponent)],
     })
    

    表单输入项 Molecules .

      <app-item>
        <app-input [(ngModel)]="value"></app-input>
        <ng-content></ng-content>
      </app-item>
    

    这是你的 主app.component.html ,这叫做 Organisms

    <div style="box-shadow : 1px 2px 6px ; padding:1rem; margin: 0 auto; width:50vw" >
          <app-form-input-item [ngStyle]="{'margin': ' 0 auto' }" 
                        *ngFor="let galaxy of galaxies; let in=index;" [(ngModel)]="galaxy.name">
               <div style="box-shadow: 1px 1px 3px #123456; margin:1em; padding:1em;    word-break: break-word;">
                   {{galaxy.name}}
               </div>
               <hr style="border: .5px solid red">
           </app-form-input-item>
    
    <app-form-input-item [(ngModel)]="bh" ></app-form-input-item>
    

    应用组件

     export class AppComponent  {
        name = 'Angular';
        galaxies = [
           {id:1,name:'Milky Way'},
           {id:2,name:'LMC'},
           {id:3,name:''},
           {id:4,name:'Cigar Galaxy'}
        ];
     }