4

I am trying to intialize the default value within a nested component but the native HTML element is never updated.

Root [(ngModel)]="value"  
  |-> Child [(ngModel)]="value"
    | -> native

I have tried already constructor and ngOnInit. AfterViewInit and AfterViewChecked throws an error, that there is a value manipulation after change detection.

root template:

<child name="child" [(ngModel)]="value"></child>

child template:

<select name="childSelect" [(ngModel)]="value">

child component:

@Component({
    selector: 'child',
    templateUrl: './child.component.tpl.html',
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => ChildComponent),
            multi: true
        }
    ],
})
export class ChildComponent implements OnInit, ControlValueAccessor {
    @Input() _value: number;
    private _onTouchedCallback: () => void = null;
    private _onChangeCallback: (a: any) => void = null;

    ngOnInit() {
        this.value = 1;
        // this._value = 1; // no difference
    }

    get value(): number {
        return this._value;
    }

    set value(value: number) {
        this._value = value;
        if (this._onChangeCallback !== null) {
            this._onChangeCallback(this._value);
        }
    }

    writeValue(value: any) {
        this._value = value;
    }

    registerOnChange(fn: any): void {
        this._onChangeCallback = fn;
    }

    registerOnTouched(fn: any): void {
        this._onTouchedCallback = fn;
    }

I think I missed a small detail.

http://plnkr.co/edit/F9pfUQ50YPV5UPiH0kw7

CSchulz
  • 10,882
  • 11
  • 60
  • 114

1 Answers1

1

I think you can see this similar question: Angular 2 ngModel in child component updates parent component property

@Mark Rajcok most-voted answer: https://stackoverflow.com/a/35329533/4956569

You can achieve with the two-way binding offered by Angular: https://angular.io/guide/template-syntax#two-way-binding---

Following your example...

You can simply use the [(x)] syntax in the parent template, instead of [(ngModel)] to achieve two-way databinding with the child.

root template:

<child name="child" [(x)]="value"></child>

Rename "x" with whatever you want (ie: "myProp", "myValue", "y"...)

On the child, you have to create an Output property of type "EventEmitter" with the name xChange, Angular will automatically update the parent property!

child component:

@Output xChange = new EventEmitter();

For example: if you renamed "x" with "myProp" the Output property MUST be "myPropChange"

You then need to emit() an event whenever the child changes the value.

child component:

valueChanged(value) {
   this.xChange.emit(value);
}

Call "valueChanged" into your "set value()"

set value(value: number) {
   this._value = value;
   this.valueChanged(value);
   if (this._onChangeCallback !== null) {
      this._onChangeCallback(this._value);
   }
}
Luca Ritossa
  • 1,118
  • 11
  • 22