无法通过2个级别传递ngModel

时间:2019-12-12 08:40:48

标签: angular ionic-framework components

我正在尝试通过第二个子组件传递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>

ngModel为空。我可以对其进行编辑,但它会发生变化,但某些内容会不断清空。如果我将<app-input>更改为<ion-input>,它将再次起作用。我在这里做什么错了?

更新:

这是我的应用程序项:

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

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

1 个答案:

答案 0 :(得分:1)

您可以在此StackBlitz Link

中找到完整的工作示例

在这里,我们正在使用Atom创建自定义的单个ControlValueAccessor输入组件。根据{{​​3}}, Input.component.html 是...。

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

然后,我们正在创建名为AbstractValueAccessor的自定义类,并在其中实现ControlValueAccessor。这样一来,每当需要将任何组件转换为ControlValueAccessor时,我们都将AbstractValueAccessor类扩展为component.ts类。因此 input.component.ts 是...

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

自定义 AbstractValueAccessor.ts 是...

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

然后必须在您的 input.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)],
 })

form-input-item 组件中使用此自定义输入组件。根据原子设计模式,这称为Molecules

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

这是您的主要app.component.html ,在Atomic Design中称为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>

app.component.ts 是..

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