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

writeValue gets called before ngOnInit #29218

Open
romansattler opened this issue Mar 11, 2019 · 19 comments
Open

writeValue gets called before ngOnInit #29218

romansattler opened this issue Mar 11, 2019 · 19 comments
Assignees
Labels
area: forms freq2: medium P4 A relatively minor issue that is not relevant to core functions state: confirmed type: bug/fix
Milestone

Comments

@romansattler
Copy link

romansattler commented Mar 11, 2019

🐞 bug report

Affected Package

The issue is caused by package `@angular/forms`

Is this a regression?

I don't know. I don't think so.

Description

When NgControl is injected into the constructor of a component and the value accessor is set manually to avoid cyclic dependency errors, writeValue is called before ngOnInit.
This workaround is suggested by the Material Angular doc under ngControl.

🔬 Minimal Reproduction

Stackblitz

🌍 Your Environment

Angular Version:


Angular CLI: 7.3.5
Node: 11.9.0
OS: win32 x64
Angular: 7.2.8
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router, service-worker

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.13.5
@angular-devkit/build-angular     0.13.5
@angular-devkit/build-optimizer   0.13.5
@angular-devkit/build-webpack     0.13.5
@angular-devkit/core              7.3.5
@angular-devkit/schematics        7.3.5
@angular/cdk                      7.3.3
@angular/cli                      7.3.5
@angular/material                 7.3.3
@angular/pwa                      0.13.5
@ngtools/webpack                  7.3.5
@schematics/angular               7.3.5
@schematics/update                0.13.5
rxjs                              6.4.0
typescript                        3.2.4
webpack                           4.29.0

Anything else relevant?

I was sent here by the @angular/material2 team. They believe it's an issue with @angular/forms.
angular/components#15434

@ngbot ngbot bot added this to the needsTriage milestone Mar 11, 2019
@atscott
Copy link
Contributor

atscott commented Aug 13, 2019

Hi @romansattler, it's a unclear to me why writeValue should be called before ngOnInit. It currently behaves like other inputs would, i.e. that value gets set before ngOnInit: example. It seems like this would be working as intended.

@rahul-sharma-uipath
Copy link

rahul-sharma-uipath commented Aug 22, 2019

For the NgControl case, this is not behaviorally consistent. If you use NG_VALUE_ACCESSOR for the control value accessor, then ngOnInit is called before writeValue, whereas with the new way that @romansattler pointed out, ngOnInit is called after writeValue. This is likely where he discovered the bug and definitely caused us a lot of headaches as well.

stackblitz

@Tonio-lavanda
Copy link

Tonio-lavanda commented Feb 27, 2020

Any update on this? I experience the same issue, the workaround I use is to add setTimeout in writeValue function but it's not ideal

  writeValue(valueToWrite: string) {
    setTimeout(() => {
      if (valueToWrite !== undefined) {
        this.value = valueToWrite;
      }
    });
  }

@iranicus
Copy link

iranicus commented Mar 5, 2020

Currently ran into this issue recently when initialising a form group via FormBuilder which is made up of custom reactive form controls, one using the NgControl constructor injection approach instead of the usual provide NG_VALUE_ACCESSOR approach. In the writeValue function theres some logic to target the ngControl.control which gets executed when we call a setValue() on the control with an initial value we get from an API call which is being ran before the ngControl.control has had time to initialise (the ngControl has been initialised however). A work around I've done is override the original writeValue() and check the ngControl.control is present prior to it being accessed.

@corinaivanov
Copy link

corinaivanov commented May 22, 2020

I have encountered the same issue when injecting NgControl. In writeValue I don't have my inner form initialized just yet since it needs some info from ngControl.control which I can obtain only in ngOnInit.

@grajeshg2000
Copy link

grajeshg2000 commented Jul 21, 2020

I would like to be volunteer for this bug. Please assign this bug to me . grajeshg2000

@petebacondarwin
Copy link
Member

petebacondarwin commented Jul 21, 2020

Thanks @grajeshg2000 - please coordinate with @AndrewKushnir on any fix you would propose.

@grajeshg2000
Copy link

grajeshg2000 commented Jul 28, 2020

The bug reproduction code is using angular/forms and angular/material libraries.
I have removed the material related code from reproduction code, Bug is still coming.
So this problem is not because of material library, instead the problem is in angular/form. Now I am debugging angular form to find out the problem.

@AndrewKushnir
Copy link
Contributor

AndrewKushnir commented Aug 12, 2020

Hi @grajeshg2000, could you please share a link to a simplified repro (without Material), so we can have a look? Thank you.

@grajeshg2000
Copy link

grajeshg2000 commented Aug 16, 2020

@mlc-mlapis
Copy link
Contributor

mlc-mlapis commented Aug 16, 2020

@grajeshg2000

I can't see any @angular/material dependencies included.

@Kayes-Islam
Copy link

Kayes-Islam commented Sep 25, 2020

I have the same issue. Any update?

@jefrog1844
Copy link

jefrog1844 commented Sep 27, 2020

I have the same issue as well. My component implements ControlValueAccessor, injects ngControl in the constructor, and sets ngControl.valueAccessor = this . I don't specify providers in order to avoid the error for circular references due to having multiple valueAccessors.

When trying to initial variables from ViewChild, I cache a reference for using in the renderer, for example:
@ViewChild('input', { static: true, read: ElementRef }) input: ElementRef;
inputEl : HTMLInputElement;

ngAfterViewInit() {
this.inputEl = this.input.nativeElement;
this.renderer.setAttribute(inputEl, 'id', this.id);
}

If I try to use this.inputEl in the writeValue method, it throws an undefined error for inputEl. As a workaround, I use the following in the writeValue method which is to just use input.nativeElement.

writeValue(value: any): void {
// update input field with value received from outer component
this.renderer.setProperty(this.input.nativeElement, 'value', value);
}

@jelbourn jelbourn added P4 A relatively minor issue that is not relevant to core functions needs: clarification This issue needs additional clarification from the reporter before the team can investigate. and removed severity1: confusing labels Oct 1, 2020
griest024 added a commit to griest024/daffodil that referenced this issue Jan 12, 2021
the this._onChange null check here is necessary because of an ongoing bug in angular forms
where writeValue can be called before the component initializes: angular/angular#29218
griest024 added a commit to griest024/daffodil that referenced this issue Jan 12, 2021
the this._onChange null check here is necessary because of an ongoing bug in angular forms
where writeValue can be called before the component initializes: angular/angular#29218
griest024 added a commit to griest024/daffodil that referenced this issue Jan 13, 2021
the this._onChange null check here is necessary because of an ongoing bug in angular forms
where writeValue can be called before the component initializes: angular/angular#29218
griest024 added a commit to griest024/daffodil that referenced this issue Jan 13, 2021
the this._onChange null check here is necessary because of an ongoing bug in angular forms
where writeValue can be called before the component initializes: angular/angular#29218
griest024 added a commit to griest024/daffodil that referenced this issue Jan 14, 2021
the this._onChange null check here is necessary because of an ongoing bug in angular forms
where writeValue can be called before the component initializes: angular/angular#29218
griest024 added a commit to griest024/daffodil that referenced this issue Jan 14, 2021
the this._onChange null check here is necessary because of an ongoing bug in angular forms
where writeValue can be called before the component initializes: angular/angular#29218
griest024 added a commit to griest024/daffodil that referenced this issue Jan 20, 2021
the this._onChange null check here is necessary because of an ongoing bug in angular forms
where writeValue can be called before the component initializes: angular/angular#29218
damienwebdev pushed a commit to graycoreio/daffodil that referenced this issue Jan 20, 2021
the this._onChange null check here is necessary because of an ongoing bug in angular forms
where writeValue can be called before the component initializes: angular/angular#29218
@alxhub alxhub removed needs reproduction This issue needs a reproduction in order for the team to investigate further needs: clarification This issue needs additional clarification from the reporter before the team can investigate. labels Mar 4, 2022
@alxhub
Copy link
Contributor

alxhub commented Mar 4, 2022

The real problem here is that you can't both provide NG_VALUE_ACCESSOR and inject NgControl, because implementations of NgControl internally inject NG_VALUE_ACCESSOR. That necessitates workarounds like the patching of NgControl.valueAccessor, which cause inconsistent behavior with writeValue.

@alvaromartmart
Copy link
Contributor

alvaromartmart commented Mar 15, 2022

I am too facing this issue on a custom control. Any current alternatives to the setTimeout() workaround?

@jsevcikmoravio
Copy link

jsevcikmoravio commented Mar 15, 2022

I am too facing this issue on a custom control. Any current alternatives to the setTimeout() workaround?

not sure, but how about injecting the Injector itself into the constructor and getting ngControl (FormControl) through it and use providers as usual? It's a dirty fix, but it seems to work.

@alvaromartmart
Copy link
Contributor

alvaromartmart commented Mar 15, 2022

I am too facing this issue on a custom control. Any current alternatives to the setTimeout() workaround?

not sure, but how about injecting the Injector itself into the constructor and getting ngControl (FormControl) through it and use providers as usual? It's a dirty fix, but it seems to work.

That's the approach I'm following but for some reason writeValue gets called before ngOnInit in that case as opposed to when using NG_VALUE_ACCESSOR

See example app: https://stackblitz.com/edit/angular-ivy-mmcdey?file=src/app/custom-input-ngcontrol/custom-input-ngcontrol.component.html

@leonelvsc
Copy link

leonelvsc commented Aug 24, 2022

It should always respect the same order of execution before or after init Lifecycle hooks (ngOnInit, ngAfterViewInit, etc) but always consistent

@mralbobo
Copy link

mralbobo commented Dec 29, 2022

As a fun bonus of this angular 15 change
https://angular.io/guide/update-to-version-15#setdisabledstate-is-always-called-when-a-controlvalueaccessor-is-attached
setDisabledState joins writeValue in being called before ngOnInit when manually setting the valueAccessor in the constructor.
this.ngControl.valueAccessor = this;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: forms freq2: medium P4 A relatively minor issue that is not relevant to core functions state: confirmed type: bug/fix
Projects
None yet
Development

No branches or pull requests