Skip to content

DateAdapter: issues with setLocale(..) in main vs. lazy loaded module #12891

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

Open
juristr opened this issue Aug 29, 2018 · 9 comments
Open

DateAdapter: issues with setLocale(..) in main vs. lazy loaded module #12891

juristr opened this issue Aug 29, 2018 · 9 comments
Labels
area: material/datepicker P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent

Comments

@juristr
Copy link

juristr commented Aug 29, 2018

Bug, feature request, or proposal:

Bug (probably, otherwise docs need to be updated to clarify this behavior).

What is the expected behavior?

I expect the entire application to be in a certain locale, thus it should be enough to set it once using the DateAdapter's setLocale(..).

What is the current behavior?

Apparently the DateAdapter is not a single instance, but rather lazy modules get their own instance. As a result, setting the locale (like in the app.component.ts might not work (as one expects) when loading some components within a lazy loaded module (via routing).

What are the steps to reproduce?

Here's a Stackblitz to reproduce the issue: https://stackblitz.com/edit/angular-material-datepicker-locale?file=src%2Fapp%2Fapp.component.ts

  1. Clicking the "test" link loads the lazy /test route. Both, app.module and lazy.module import SharedModule which imports the Material stuff.
  2. The datepicker is in English by default. Clicking the button "set locale on app module" should set it to German, but it doesn't.
  3. Clicking the button "set locale in lazy module" however works as expected.

That makes me think there are 2 instances of the DateAdapter, one for the lazy module and one for the app module.

What is the use-case or motivation for changing an existing behavior?

Usually the locale is very much related to the application language. In most cases you load the user profile when the app starts and based on his settings, cookies or whatever, you set the language and locale respectively. Similarly at the same point you also want to adjust other components' locales such as the Material Datepicker.

The current behavior however is misleading, as one might expect that setting the locale on the DateAdapter object should automatically adjust them for all datepickers. Due to the bug that is not the case for lazy loaded modules however.

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

image

@devversion
Copy link
Member

devversion commented Aug 29, 2018

The problem is that the SharedModule is being imported in the lazy loaded module as well. The SharedModule then provides a new instance of the DateAdapter which is not equal to the DateAdapter which has been provided in the app module.

A possible workaround is to just make sure that the MatNativeDateModule is only imported once.


I personally think that it's reasonable to have multiple nested DateAdapter instances and that we should just improve documentation.

@juristr
Copy link
Author

juristr commented Aug 30, 2018

A possible workaround is to just make sure that the MatNativeDateModule is only imported once.

Yes sure, that's why usually you have the .forRoot() pattern to avoid multiple registrations of services. Unless the new providedIn: 'root' is being used which would solve it as well.

I personally think that it's reasonable to have multiple nested DateAdapter instances and that we should just improve documentation.

For sure this needs to be documented better 😉.

it's reasonable to have multiple nested DateAdapter instances

🤔 hmm...not sure. Currently thinking about that. How would that work. Like when registered in plain normal modules, there would be just 1 global singleton instance anyway. The "multiple" ones only happens in the lazy loaded modules case as I mentioned, as that one will get its own DI container.

To have different instances within my same app, I'd expect to re-registering the DateAdapter on the providers section of some component to enforce a new instance on that sub-tree. Right? And that would work just fine, even with .forRoot() or the new providedIn approach.

@devversion
Copy link
Member

devversion commented Aug 30, 2018

Yes sure, that's why usually you have the .forRoot() pattern to avoid multiple registrations of services. Unless the new providedIn: 'root' is being used which would solve it as well.

Yeah, if we decide to only have one instance of a DateAdapter per app, then this would be easy to fix by using providedIn: "root".


Regarding multiple DateAdapter instances: technically if people extract stuff into sub modules (regardless of lazy loading; e.g. if they have a shared "company" library), they most likely have a different DateAdapter for this specific "feature module".

So I don't think it's right to always assume that the DateAdapter should be provided at root.

To have different instances within my same app, I'd expect to re-registering the DateAdapter on the providers section of some component to enforce a new instance on that sub-tree. Right? And that would work just fine, even with .forRoot() or the new providedIn approach.

That would work, but not sure if we want to go this way API-wise. Let's see what @mmalerba thinks.

@devversion
Copy link
Member

devversion commented Aug 30, 2018

Actually I need to take back what I said. Only in lazy-loaded modules, the providers from the app root can be isolated. So my argument that people intentionally extract stuff into external feature modules in order to have their own instance of the DateAdapter does only apply if the module is lazily loaded.

Quote from the docs (https://angular.io/guide/providers).

Any component created within a lazy loaded module’s context, such as by router navigation, gets the local instance of the service, not the instance in the root application injector. Components in external modules continue to receive the instance created for the application root.

Good catch @juristr. Thanks

@mmalerba mmalerba added needs triage This issue needs to be triaged by the team area: material/datepicker P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent and removed needs triage This issue needs to be triaged by the team labels May 20, 2020
@ImMo3
Copy link

ImMo3 commented Sep 27, 2020

guys i cant solve this issue :(

@wattachai
Copy link

guys i cant solve this issue :(

have you try calling setLocale() within the lazy loaded module?

@nebojsazr
Copy link

guys i cant solve this issue :(

have you try calling setLocale() within the lazy loaded module?

Yap that helps. But I moved setLocale() to shared.module and works there also. It was not working in app.module

export class SharedModule { constructor(private dateAdapter: DateAdapter<any>, private translate: TranslateService) { this.dateAdapter.setLocale(this.translate.currentLang); this.translate.onLangChange.subscribe(() => { this.dateAdapter.setLocale(this.translate.currentLang); }); } }

@abides
Copy link

abides commented Oct 19, 2022

"@angular/material": "14.2.4"
setLocale() on every lazy loaded module components which has MatDatePicker feels wrong while doing it. Is there any workaround to make it single instance? Since there is option to make new instance as @juristr mentioned.

@malua
Copy link

malua commented Jan 16, 2023

I found that calling setLocale in a shared module only works, if it's executed right away. It does not work anymore in the translate onLangChange listener.
So when the user changes the language, I just reload the page for now. then the locale is correctly changed/set.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: material/datepicker P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
Projects
None yet
Development

No branches or pull requests

8 participants