Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NgModel defers writeValue call to the next tick. Conflicts with OnInit's contract of child components. #13568

Open
mykolav opened this issue Dec 19, 2016 · 5 comments
Labels
area: core Issues related to the framework runtime core: change detection freq1: low P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent type: bug/fix
Milestone

Comments

@mykolav
Copy link

mykolav commented Dec 19, 2016

I'm submitting a ... (check one with "x")

[x] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

NgModel defers calling writeValue of the ControlValueAccessor with the supplied value to the next tick.

private _updateValue(value: any): void {
    resolvedPromise.then(
        () => { this.control.setValue(value, {emitViewToModelChange: false}); });
}

While OnInit's documentation states

Lifecycle hook that is called after data-bound properties of a directive are initialized.

As a result of the two things described above, it is not obvious, how to from a component implementing ControlValueAccessor supply values to inner components that expect their data-bound properties to be initialized in onInit.

Expected behavior

Minimal reproduction of the problem with instructions

The minimal plunker to reproduce

What is the motivation / use case for changing the behavior?

I am trying to place a color picker on a form. To do this I created a wrapper component that implements ControlValueAccessor which uses angular2-color-picker underneath.
The wrapper's _value is set in writeValue and the wrapper is bound using ngModel. The wrapper, in turn, has the following in its markup:

<input
    class="form-control"
    [colorPicker]="_value"
    (colorPickerChange)="onColorPickerChange($event)"
    ...
    />

The problem is angular2-color-picker tries to access its data-bound inputs in onInit which triggers a bug down the road.
color-picker.directive.ts

export class ColorPickerDirective implements OnInit, OnChanges {
    @Input('colorPicker') colorPicker: string;
    @Output('colorPickerChange') colorPickerChange = new EventEmitter<string>(true);

    // ...

    ngOnInit() {
        let hsva = this.service.stringToHsva(this.colorPicker);
        // ...
        this.colorPickerChange.emit(this.service.outputFormat(hsva, this.cpOutputFormat, this.cpAlphaChannel === 'hex8'));
    }

Please tell us about your environment:

  • Windows 10.
  • WebStorm and VSCode.
  • npm and yarn.

Angular version: 2.3.0

Browser: all

Language: TypeScript

@mykolav mykolav changed the title NgModel defers bound properties initialized to the next tick. Contradicts OnInit's documentation. NgModel defers bound properties initialization to the next tick. Contradicts OnInit's documentation. Dec 19, 2016
@mykolav mykolav changed the title NgModel defers bound properties initialization to the next tick. Contradicts OnInit's documentation. NgModel defers bound properties initialization to the next tick. Conflicts with OnInit's contract of child components. Dec 19, 2016
@mykolav mykolav changed the title NgModel defers bound properties initialization to the next tick. Conflicts with OnInit's contract of child components. NgModel defers writeValue call to the next tick. Conflicts with OnInit's contract of child components. Dec 19, 2016
@DzmitryShylovich
Copy link
Contributor

Not sure but I think it's done to avoid Expression has changed after it was checked error

@mykolav
Copy link
Author

mykolav commented Dec 19, 2016

Hi @DzmitryShylovich
The motivation for ngModel's current behavior is explained nicely in ng_model.ts:

 `ngModel` forces an additional change detection run when its inputs change:
 E.g.:
 
 <div>{{myModel.valid}}</div>
 <input [(ngModel)]="myValue" #myModel="ngModel">
 
 I.e. `ngModel` can export itself on the element and then be used in the template.
 Normally, this would result in expressions before the `input` that use the exported directive
 to have and old value as they have been
 dirty checked before. As this is a very common case for `ngModel`, we added this second change
 detection run.

I'm still wondering whether the particular case described in this issue can be addressed? Or at least maybe described as a caveat somewhere in Angular's documentation.

Thank you,
Mykola.

@chuckjaz chuckjaz added area: core Issues related to the framework runtime type: bug/fix labels Dec 19, 2016
@kemsky
Copy link

kemsky commented Dec 20, 2016

Recently i saw related issue when using OnPush, some values in template (placed before NgModel) remain old and some (placed after NgModel) were updated.

@rubenlagus
Copy link

Any idea when this will be fixed?

@ngbot ngbot bot added this to the Backlog milestone Jan 23, 2018
@jelbourn jelbourn added P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent and removed severity3: broken labels Oct 1, 2020
@kemsky
Copy link

kemsky commented Jan 18, 2022

@jelbourn, this is really broken for custom value accessors, because it introduces difference between ngModel input and any other input:

<input [ngModel]="value" [data]="data">

ngModel is applied to component one tick later than data. There is no way to consistently implement action that depends on both ngModel and data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: core Issues related to the framework runtime core: change detection freq1: low P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent type: bug/fix
Projects
None yet
Development

No branches or pull requests

8 participants