Skip to content
This repository has been archived by the owner on Nov 22, 2024. It is now read-only.

Provide a token from the server side #1646

Closed
1 of 6 tasks
waterplea opened this issue Apr 29, 2020 · 9 comments
Closed
1 of 6 tasks

Provide a token from the server side #1646

waterplea opened this issue Apr 29, 2020 · 9 comments

Comments

@waterplea
Copy link

🐞 Bug report

What modules are related to this issue?

  • aspnetcore-engine
  • builders
  • common
  • express-engine
  • hapi-engine
  • module-map-ngfactory-loader

Is this a regression?

No

Description

I am trying to provide a token on the server side. This token has a factory for default value. I provide it in server.ts but app still gets default value from the factory.

🔬 Minimal Reproduction

test.ts:

export const TEST = new InjectionToken<string>(
    'Test',
    {
        factory: () => 'default',
    },
);

server.ts:

app.get('*', (req, res) => {
    res.render('index', {req, providers: [
        {
            provide: TEST,
            useValue: 'test'
        }
    ]});
});

app.component.ts:

    constructor(@Inject(TEST) test: string) {
        console.log(test);
    }

I get default printed to console.

It works if I use just a string. Is there a way to get this to work with tokens? I'm creating a library and I want to provide SSR overrides for its tokens so people would be able to just add them in server.ts. They rely on req so I cannot just use them in AppServerModule

🌍 Your Environment


Angular CLI: 9.1.3
Node: 12.14.1
OS: win32 x64
@waterplea
Copy link
Author

Just noticed that when I add universal via CLI I get this in my server.ts:
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
it works, but when I try to provide my token with factory it fails and returns factory value:
https://github.com/ng-web-apis/common/blob/master/projects/common/src/tokens/user-agent.ts
Perhaps this is an issue with tokens with factories?

@CaerusKaru
Copy link
Member

Can you encapsulate this in a GitHub repository?

@CaerusKaru CaerusKaru added the need: investigation Requires some digging to determine if action is needed label Jun 14, 2020
@waterplea
Copy link
Author

Here's a plain Angular CLI + Universal repo with 2 tokens:
https://github.com/waterplea/angular-universal-token-issue

I've added 5 seconds delay to transition from SSR to browser so you can see tokens values on the page. Just run npm i + npm run dev:ssr. Code of interest is in server.ts line 37 and app.component.ts.

@alfaproject
Copy link

I seem to have the same issue ):

@ghost
Copy link

ghost commented Oct 2, 2021

I am also looking for a way to solve this issue :(

@waterplea
Copy link
Author

Any news on this? :(

@Teebo
Copy link

Teebo commented Dec 2, 2021

Any news on this?

@CaerusKaru
Copy link
Member

CaerusKaru commented Feb 6, 2022

Ok I dug through this for what turned out to be far too long, and here's the answer, though I understand if it's frustrating.

Angular DI is structured in a reverse-hierarchal fashion. This means that at the top-level, where you define the platform (platform-server, browser, etc), there is an initial set of providers, and you keep building on this set. When you render a module, you are, in the following order: creating a platform, providing additional providers, then instantiating and rendering your module. At the final stage in this process, you are registering the factory value for your injection token, which is why it "wins" the DI race. When you define a provider at the server.ts level, this is only one level above the platform, and so it almost always "loses". To fix this, you need to instead define any "competing" tokens at the ServerAppModule level. This sits one level above AppModule, and so its providers in this case will "win". In the case of values provided only at server runtime, please make sure that they are singleton, and therefore noncompeting. After all, the browser shouldn't have a fallback for APP_BASE_HREF, since that token is meant to be populated explicitly by the user (either on server or browser). If you absolutely must have a competing factory, then you should instantiate a FactoryProvider using the useFactory syntax, and create a specific BrowserAppModule that can exist outside of the scope of the server.

tl;dr move any competing tokens into ServerAppModule and you should be fine.

I'm going to leave this open as a docs ticket, so we can clarify this behavior for other users.

@CaerusKaru CaerusKaru added type: docs and removed need: investigation Requires some digging to determine if action is needed labels Feb 6, 2022
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Mar 25, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants