X Tutup
The Wayback Machine - https://web.archive.org/web/20230125055027/https://github.com/angular/angular/issues/14988
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

ValueAccessor.writeValue is being called twice, first time with a phantom null value #14988

Open
alexi2014 opened this issue Mar 7, 2017 · 61 comments
Labels
area: forms forms: ControlValueAccessor freq2: medium P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent state: confirmed type: bug/fix
Milestone

Comments

@alexi2014
Copy link

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
The ValueAccessor.writeValue method is called twice on a custom value accessor during component initialization, when a control is bound using [(ngModel)], first time with a phantom null value.

Note that this is not an issue in form builder scenarios when control is bound using [formControlName].

Expected behavior
ValueAccessor.writeValue should be called only once, with an actual value of the property bound to [(ngModel)].

Minimal reproduction of the problem with instructions
Run the following plunker:
http://plnkr.co/edit/g5kjqQ9bQ9DF2cs4DbEH?p=preview

MyValueAccessor is applied to the MyCmp component, the latter is bound using [(ngModel)] to the property initialized to 'Hello'. MyValueAccessor.writeValue writes the passed value to the console, the output looks as follows:

MyValueAccessor.writeValue: null
MyValueAccessor.writeValue: Hello

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

Please tell us about your environment:

  • Angular version: RC.4 - 2.4.9
  • Browser: Chrome, IE11
  • Language: TypeScript

  • Node (for AoT issues): node --version =

@alexi2014
Copy link
Author

I don't understand how this relates to synchronous or asynchronous. ngModel is bound to a property declared as
theValue = 'Hello';
, but receives a null on initialization. If theValue never has a null value, why null is passed to the accessor?

@alexi2014
Copy link
Author

@IgorMinar Thanks Igor. When do you think it could be fixed?

@DzmitryShylovich
Copy link
Contributor

why null is passed to the accessor?

because FormControl is initialized with null value. Just add a check if (obj != null) {}

@alexi2014
Copy link
Author

Yes, and this check will break initialization in reactive forms scenario, where this "phantom" null is not passed.

@DzmitryShylovich
Copy link
Contributor

how it will break initialization in reactive forms scenario?

@alexi2014
Copy link
Author

If initial value of a bound property is null, the if (obj != null) check you suggested will ignore this assignment and not propagate it to the control where ValueAccessor is applied to.

@DzmitryShylovich
Copy link
Contributor

Ok I see how to fix it but they don't merge fixes until v4 release :)

@alexi2014
Copy link
Author

@DzmitryShylovich Why do you think it can't fixed in v2?

@DzmitryShylovich
Copy link
Contributor

it will be fixed in 2.x but the fix will be available after v4 release

@alexi2014
Copy link
Author

I see, thanks for the clarification. It's better then nothing anyway:)

@CSchulz
Copy link

CSchulz commented Apr 27, 2017

It is the same in Angular 4.1.0:
http://plnkr.co/edit/cOHEHCu55X2MNEUxBhou?p=preview

@myagoo
Copy link

myagoo commented Aug 4, 2017

What is the status on this ?

@dpxxdp
Copy link

dpxxdp commented Nov 3, 2017

This is an open issue for us too. @DzmitryShylovich, any status on this?

@anjmao
Copy link

anjmao commented Nov 19, 2017

Any news on this issue? I'm creating custom component and for my case it is very important to do some logic on first writeValue call, but since only second writeValue call is giving real value this is a big issue :)

@mofogasy
Copy link

mofogasy commented Mar 9, 2018

Problem still exists in Angular 5.2.1.

Any news on that? Is this by design?

@jcimoch
Copy link

jcimoch commented Mar 19, 2018

Still present in latest angular version.

@mvsrinivasan
Copy link

mvsrinivasan commented Jun 9, 2018

This is still an issue.

I have a nested user control
form

Form component :
<partsList [(ngModel)]="partsList">

PartsList Component :
`<div *ngFor="let p of parts;let i=index">
<part name="part-{{i}}" [(ngModel)]="parts[i]">

`

Part Component has [(ngModel)]="part.quantity"

When writeValue is first called with null, I can put a safety band aid around it to check for null inside the part component.But the template engine screws up the [(ngModel)]= "part.quantity" and complains about "Cannot read property 'quantity' of undefined".

@ckelley7
Copy link

This bug is ruining my marriage. Please fix. I know. I'm not worthy.

@RoyiNamir
Copy link

@ckelley7 You can check for null value

http://plnkr.co/edit/UNYbyNK609F9o2vOtzRy?p=preview

 writeValue(obj: any): void {
    
    if (obj!=null){
    
    console.log(`MyValueAccessor.writeValue: ${obj}`);
    }
  }

Until it's fixed. At lest you won't get exception.

@mvsrinivasan
Copy link

@RoyiNamir : Thanks for the suggestion, but this won't help the errors encountered if you reference your innerValue in your template.

My component template has
<input [(ngModel)]="innerValue.quantity"/>

The template engine still tries to navigate to innerValue.quantity with this error
ERROR TypeError: Cannot read property 'quantity' of undefined

@RoyiNamir
Copy link

@SV-efi Have you tried wrapping it with a Form tag? as this will cause initialization to be schedule :

problematic : https://plnkr.co/edit/fOL57Hjw2PAOx6WXh36t?p=preview

Solution : https://plnkr.co/edit/5Ez9VgN0yNz7QaF7DMck?p=preview

N2D4 added a commit to N2D4/angular that referenced this issue Jul 19, 2020
`ValueAccessor.writeValue(...)` is called twice on the very first `ngModel` change if the form has
not yet been set up; once by `setUpControl` and once when the value is actually updated. However,
because `setUpControl` is called before the value is set, the first call passes `null`.

The bug is most common for standalone form controls which are never set up before the first
`ngModel` change but may also happen if any other form control is not added to its
parent first by calling `NgForm.addControl(...)`.

Fix that by optionally skipping the `writeValue(...)` call in `setUpControl` if an optional flag
is given.

Fixes angular#14988
@N2D4
Copy link

N2D4 commented Jul 19, 2020

#38140 fixes this. Hope your marriage is alright, @ckelley7! 🙏

(Quick edit: I just want to mention that this only fixes the bug that null was sometimes passed during initialization, and doesn't prevent you from calling .reset(), which sets the value of the control to null by design.)

N2D4 added a commit to N2D4/angular that referenced this issue Jul 20, 2020
`ValueAccessor.writeValue(...)` is called twice on the very first `ngModel` change if the form has
not yet been set up; once by `setUpControl` and once when the value is actually updated. However,
because `setUpControl` is called before the value is set, the first call passes `null`.

The bug is most common for standalone form controls which are never set up before the first
`ngModel` change but may also happen if any other form control is not added to its
parent first by calling `NgForm.addControl(...)`.

Fix that by optionally skipping the `writeValue(...)` call in `setUpControl` if the value is a
special sentinel value indicating it has not yet been initialized.

Fixes angular#14988
N2D4 added a commit to N2D4/angular that referenced this issue Jul 20, 2020
`ValueAccessor.writeValue(...)` is called twice on the very first `ngModel` change if the form has
not yet been set up; once by `setUpControl` and once when the value is actually updated. However,
because `setUpControl` is called before the value is set, the first call passes `null`.

The bug is most common for standalone form controls which are never set up before the first
`ngModel` change but may also happen if any other form control is not added to its
parent first by calling `NgForm.addControl(...)`.

Fix that by optionally skipping the `writeValue(...)` call in `setUpControl` if the value is a
special sentinel value indicating it has not yet been initialized.

Fixes angular#14988
N2D4 added a commit to N2D4/angular that referenced this issue Jul 20, 2020
`ValueAccessor.writeValue(...)` is called twice on the very first `ngModel` change if the form has
not yet been set up; once by `setUpControl` and once when the value is actually updated. However,
because `setUpControl` is called before the value is set, the first call passes `null`.

The bug is most common for standalone form controls which are never set up before the first
`ngModel` change but may also happen if any other form control is not added to its
parent first by calling `NgForm.addControl(...)`.

Fix that by optionally skipping the `writeValue(...)` call in `setUpControl` if the value is a
special sentinel value indicating it has not yet been initialized.

Fixes angular#14988
@mohamedelshorbagy
Copy link

Until the bug fix is merged for people who still have this problem and the problem for calling writeValue when using setValue or patchValue.
ControlValueAccessor extended component don't ever try to use setValue or patchValue in passed formControl always try to keep the changes of the formControl value through onChange method and if you have to listen to valueChanges for example and you setValue inside it try to have another Subject or BehaviourSubject to lock the valueChanges & writeValue if you are setting the value of the FormControl.

N2D4 added a commit to N2D4/angular that referenced this issue Aug 26, 2020
`ValueAccessor.writeValue(...)` is called twice on the very first `ngModel` change if the form has
not yet been set up; once by `setUpControl` and once when the value is actually updated. However,
because `setUpControl` is called before the value is set, the first call passes `null`.

The bug is most common for standalone form controls which are never set up before the first
`ngModel` change but may also happen if any other form control is not added to its
parent first by calling `NgForm.addControl(...)`.

Fix that by optionally skipping the `writeValue(...)` call in `setUpControl` if an optional flag
is given.

Fixes angular#14988

fixup! fix(forms): ValueAccessor.writeValue(...) sometimes called with null

revert: "fixup! fix(forms): ValueAccessor.writeValue(...) sometimes called with null"

This reverts commit 0babb27.

fixup! fix(forms): ValueAccessor.writeValue(...) sometimes called with null

fix(forms): ValueAccessor.writeValue(...) sometimes called with null

`ValueAccessor.writeValue(...)` is called twice on the very first `ngModel` change if the form has
not yet been set up; once by `setUpControl` and once when the value is actually updated. However,
because `setUpControl` is called before the value is set, the first call passes `null`.

The bug is most common for standalone form controls which are never set up before the first
`ngModel` change but may also happen if any other form control is not added to its
parent first by calling `NgForm.addControl(...)`.

Fix that by optionally skipping the `writeValue(...)` call in `setUpControl` if the value is a
special sentinel value indicating it has not yet been initialized.

Fixes angular#14988

fix(forms): ValueAccessor.writeValue(...) sometimes called with null

`ValueAccessor.writeValue(...)` is called twice on the very first `ngModel` change if the form has
not yet been set up; once by `setUpControl` and once when the value is actually updated. However,
because `setUpControl` is called before the value is set, the first call passes `null`.

The bug is most common for standalone form controls which are never set up before the first
`ngModel` change but may also happen if any other form control is not added to its
parent first by calling `NgForm.addControl(...)`.

Fix that by optionally skipping the `writeValue(...)` call in `setUpControl` if the value is a
special sentinel value indicating it has not yet been initialized.

Fixes angular#14988

fixup! fix(forms): ValueAccessor.writeValue(...) sometimes called with null

fixup! fix(forms): ValueAccessor.writeValue(...) sometimes called with null

fixup! fix(forms): ValueAccessor.writeValue(...) sometimes called with null

revert: "fixup! fix(forms): ValueAccessor.writeValue(...) sometimes called with null"

This reverts commit f0978db.

revert: "fixup! fix(forms): ValueAccessor.writeValue(...) sometimes called with null"

This reverts commit f0978db.

fixup! fix(forms): ValueAccessor.writeValue(...) sometimes called with null
N2D4 added a commit to N2D4/angular that referenced this issue Aug 26, 2020
`ValueAccessor.writeValue(...)` is called twice on the very first `ngModel` change if the form has
not yet been set up; once by `setUpControl` and once when the value is actually updated. However,
because `setUpControl` is called before the value is set, the first call passes `null`.
The bug is most common for standalone form controls which are never set up before the first
`ngModel` change but may also happen if any other form control is not added to its
parent first by calling `NgForm.addControl(...)`.
Fix that by optionally skipping the `writeValue(...)` call in `setUpControl` if an optional flag
is given.

Fixes angular#14988
@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
@Twynzen
Copy link

Twynzen commented Nov 4, 2020

I come from the future, it's a bad year. This bug has not been fixed yet. They say to create a condition to detect if the "writeValue" is null. But "writeValue" belongs to the nodeModules, therefore go to "@angular/forms" and modify it, it seems a very bad idea. That I understood. Explain to me if I'm wrong. Sorry for my english, i speak spanish.

@ckelley7
Copy link

ckelley7 commented Nov 6, 2020

I come from the future, it's a bad year. This bug has not been fixed yet. They say to create a condition to detect if the "writeValue" is null. But "writeValue" belongs to the nodeModules, therefore go to "@angular/forms" and modify it, it seems a very bad idea. That I understood. Explain to me if I'm wrong. Sorry for my english, i speak spanish.

Tu inglés es lo suficientemente bueno como para hacer reír a carcajadas a un hablante nativo, Daniel. Creo que lo que están sugiriendo es modificar el método ValueAccessor.writeValue de su control personalizado para detectar nulo y hacer lo necesario. Me he equivocado antes.

@nullifiedtsk
Copy link

Any progress on this?
Stuck with the same thing, the value produced once for non-standalone controls, but for standalone controls always emits null first. As workaround i am started to use formControls instead of ngModel bindings, but it just makes code complicated.

@kagan94
Copy link

kagan94 commented Mar 29, 2021

I also faced with same issue when using [(ngModel)]="myValue" and changeDetection: ChangeDetectionStrategy.OnPush in parent component.

In my case, the second call of writeValue with real value didn't re-render the view, so I applied following temporary workaround
(to run change detector after setting value in writeValue):

  constructor(private changeDetection: ChangeDetectorRef) {
  }

  ...

  public writeValue(obj: any) {
    this.selected = obj;
    this.changeDetection.markForCheck();
  }

@jforjava1981
Copy link

https://stackoverflow.com/questions/39835811/ngmodel-binding-is-null-in-oninit-in-angular-2
@N2D4 is this issue related to question asked in above link?
If not Is there a way I can somehow initialise the value in my custom component using ngcontrolvalueaccessor with [(ngModel)]?

@afusa
Copy link

afusa commented Apr 29, 2021

This is getting ridiculous now. When is this getting fixed ?

@SadeemGxG
Copy link

So is there any progress?

@stewx
Copy link
Contributor

stewx commented Aug 24, 2021

@maxime1992 There is already an open PR, wise guy. It's been open for over a year: #38140

@luketanner
Copy link

@maxime1992 There is already an open PR, wise guy. It's been open for over a year: #38140

@stewx Well, even-wiser-guy, now that PR is closed and this bug remains unfixed after FIVE years.

@Expertus777
Copy link

Expertus777 commented Jun 6, 2022

How to fix it simple and properly?

My solution at the moment is just wrap the myValue from [(ngModel)]="myValue" in setTimeout '0' like:

setTimeout(() => {
this.myValue= someFetchData;
}, 0)

Another solution I found is to add *ngIf directive to <input *ngIf="myValue" [(ngModel)]="myValue"> something like that. But I think I'll stick to the first one.

Would be glad to hear some better solution is any.

@martynas9
Copy link

In my case. I've noticed that first writeValue with a null value is called before registerOnChange is called, and then, the writeValue is called with the real value again.

So my workaround for this bug was this:

Create class property for onChange but don't assign a value to it:

onChange: (value: unknown) => void;

Then on registerOnChange we assign a function to onChange property:

  registerOnChange(fn: (value: unknown) => void): void {
    this.onChange = fn;
  }

And check if onChange is set in writeValue function:

writeValue(value: unknown): void {
    if(this.onChange) {
      // your code here
    }
  }

Maybe it will be helpful for someone.

@william-fargues
Copy link

Hey Angular team, I've had time to start a family, see my children grow up, graduate and learn React instead of Angular just to avoid this bug, do you think this is normal?

@amitbeck
Copy link

amitbeck commented Jan 17, 2023

Wasn't this solved by using the nonNullable (which replaced the deprecated initialValueIsDefault) property of FormControlOptions?

@ayubUOL
Copy link

ayubUOL commented Jan 19, 2023

I'm using Reactive forms on different pages but this issue only occur in One page and code is similar. I've no clue why i'm facing this only on one page. Can anyone help me ? how to solve this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: forms forms: ControlValueAccessor freq2: medium P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent state: confirmed type: bug/fix
Projects
None yet
Development

Successfully merging a pull request may close this issue.

X Tutup