Aspnetcore-angular-universal: How to properly get server side variables passed to node/client side

Created on 15 May 2017  路  14Comments  路  Source: TrilonIO/aspnetcore-angular-universal

In main.server.ts and main.server.aot.ts I've added the following:

response.globals.transferData = createTransferScript({
  someData: 'Transfer this to the client on the window.TRANSFER_CACHE {} object',
  serviceUrl: params.data.serviceUrl,
  url: params.data.url,
  clientId: params.data.clientId
});

Which appears to work fine on client side rendering but I also need to have node know this so that it has the url to our service api loaded in for the api calls that it needs to make.

I can't for the life of me figure out how to get these values in angular on client side given that window.xxx doesn't exist on node.

In the past with JavaScriptServices you could do this: this.Url = Zone.current.get("url");

But that doesn't work in this case as far as I can tell.

So how do I retrieve these values in my app?

Most helpful comment

So here's my (ugly) solution for those that need this:

  1. in your main.server and main.server.aot.json add providers like this around line 24:
    { provide: 'SERVICE_URL', useValue: params.data.serviceUrl },

  2. around line 38 add like this: response.globals.SERVICE_URL = params.data.serviceUrl;

in browser-app.module.ts add a provider like this:

{
provide: 'SERVICE_URL', useValue: (window).TRANSFER_CACHE.serviceUrl
}

Now you can use @Inject(SERVICE_URL) serviceUrl: string anywhere you need it.

This will work server side, but in the browser it doesn't work, so you have to always use serviceUrl || window.TRANSFER_CACHE.serviceUrl to actually get the value reliably.

Wish this just worked and didn't require the nasty @Inject syntax.

All 14 comments

Hey @JohnGalt1717 So transferData within globals is just to transfer everything to the Client, what'd you'd want to do is create a Provider / Service class, and inject that within main.server.

So now that I think about it, I'll definitely have to revist this and improve this developer experience...

https://github.com/MarkPieszak/aspnetcore-angular2-universal/blob/master/Client/main.server.ts#L23-L25

  // Platform-server provider configuration
  const setupOptions: IEngineOptions = {
    appSelector: '<app></app>',
    ngModule: ServerAppModule,
    request: params,
    providers: [
      // **** HERE *****
      // Optional - Any other Server providers you want to pass (remember you'll have to provide them for the Browser as well)
    ]
  };

You could map those values to some static properties on a Class perhaps then just add the Class to the list of Providers. Now the annoying part is that you'd have to map them from window[] like TransferCache does so that the Client could just re-use those exact ones...

Definitely not ideal, I'll try to come up with an even easier way of doing this...

Let me get back to you!

Ok. I figured this would work the same as it did before. request: params would generally be those parameters. But since we're not using the code injection of the javascriptservices tag I guess not.

You could map those values to some static properties on a Class perhaps then just add the Class to the list of Providers.

How could I map them? I can't even figure out how to access them at all in node (and of course I have no way of breaking into node and splunking looking for them)

Basically I have an app.settings that stores global application settings and pulls these values when the app runs. I provide that stuff through including app.settings in any components and it's a singleton provider.

So really all I need to know how to do is in that app.settings.cs pull the values through the pipeline from this in the HomeController/index:

        TransferData transferData = new TransferData();
        transferData.request = AbstractHttpContextRequestInfo(Request);
        transferData.serviceUrl = Settings.ApiUrl;
        ViewBag.ServiceUrl = Settings.ApiUrl;
        transferData.clientId = Settings.ClientId;
        ViewBag.ClientId = Settings.ClientId;
        transferData.url = Request.Host.Host;
        ViewBag.Url = Request.Host.Host;

Through the main.server.ts and then into my app.settings.ts. (i.e. how to replicate the Zone.get("XXX") that was happening in Javascriptservices)

If you could enlighten me on that then I could have a short term fix while you work on an easier way.

So here's my (ugly) solution for those that need this:

  1. in your main.server and main.server.aot.json add providers like this around line 24:
    { provide: 'SERVICE_URL', useValue: params.data.serviceUrl },

  2. around line 38 add like this: response.globals.SERVICE_URL = params.data.serviceUrl;

in browser-app.module.ts add a provider like this:

{
provide: 'SERVICE_URL', useValue: (window).TRANSFER_CACHE.serviceUrl
}

Now you can use @Inject(SERVICE_URL) serviceUrl: string anywhere you need it.

This will work server side, but in the browser it doesn't work, so you have to always use serviceUrl || window.TRANSFER_CACHE.serviceUrl to actually get the value reliably.

Wish this just worked and didn't require the nasty @Inject syntax.

@JohnGalt1717 I'm just storing my token in a variable at View(asp.net core) like var message = "@ViewBag.Token" want to use this message in angular 2 side .. what are the steps I need to take to accomplish this.

In your homecontroller add it to the transfer data object.
Then follow my above instructions and it should work fine.

@JohnGalt1717 Could you look in to this

Follow the instructions above. They're the only way to actually get it into node properly.

@JohnGalt1717 browser-app.module.ts (49,56): Property 'TRANSFER_CACHE' does not exist on type 'Window'., what did I miss?

You need to control what you pass. It's trying to inject the transfer case on the browser site after SSR. If you don't use SSR then you need to disable this (and a ton of other stuff.

I am using SSR, my IDE (vscode) complains and the build fails on that line. What do you mean with "control what you pass"?

{
provide: 'SERVICE_URL', useValue: (window).TRANSFER_CACHE.serviceUrl
}

(window) should just be window. but TRANSFER_CACHE is added to your page automatically in the homecontroller and then in the view. You need ot make sure it's there. You can also remove any of those that you don't care to have. They're just mine.

@Flood closing this one, please reopen if you are still having issues

@JohnGalt1717 is this still the method you use to get data from .NET into Angular code?

also, since window is only available on the browser, how do you make calls for server pre-rendering?

For example the only way I can get my "SERVICE_URL"is from the window object which is only available to the browser. I can't make sense of what I would do here in the case of server rendering in the UNiverrsal flow of things.

Sample code to illustrate my point.

  public getLeaderboardItemsBySeason(season: number): Observable<any> {
    if (isPlatformBrowser(this.platformId)) {
      return this.http.get<ILeaderboard>(this.getEndPoint() + 'leaderboard?singular=1&seasonId=' + season);
    }
  }

  private getEndPoint(): string {
      console.log(window);
        return window['TRANSFER_CACHE'].myConfig.APISettings.AmbassadorsAPI;
  }

@jrmcdona look my other post I just did

Was this page helpful?
0 / 5 - 0 ratings