From ea0a32a15b814f7a8c23417eaffe1e9f86fd704c Mon Sep 17 00:00:00 2001 From: SteveSandersonMS Date: Tue, 21 Jun 2016 15:46:52 +0100 Subject: [PATCH] Implement and document asp-prerender-data --- .../Prerendering/PrerenderTagHelper.cs | 7 ++++- .../Prerendering/Prerenderer.cs | 6 ++-- .../README.md | 30 ++++++++++++++++++- .../npm/aspnet-prerendering/package.json | 2 +- .../aspnet-prerendering/src/Prerendering.ts | 8 +++-- 5 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.AspNetCore.SpaServices/Prerendering/PrerenderTagHelper.cs b/src/Microsoft.AspNetCore.SpaServices/Prerendering/PrerenderTagHelper.cs index 6e36481..b09eaae 100644 --- a/src/Microsoft.AspNetCore.SpaServices/Prerendering/PrerenderTagHelper.cs +++ b/src/Microsoft.AspNetCore.SpaServices/Prerendering/PrerenderTagHelper.cs @@ -19,6 +19,7 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering private const string PrerenderModuleAttributeName = "asp-prerender-module"; private const string PrerenderExportAttributeName = "asp-prerender-export"; private const string PrerenderWebpackConfigAttributeName = "asp-prerender-webpack-config"; + private const string PrerenderDataAttributeName = "asp-prerender-data"; private static INodeServices _fallbackNodeServices; // Used only if no INodeServices was registered with DI private readonly string _applicationBasePath; @@ -51,6 +52,9 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering [HtmlAttributeName(PrerenderWebpackConfigAttributeName)] public string WebpackConfigPath { get; set; } + [HtmlAttributeName(PrerenderDataAttributeName)] + public object CustomDataParameter { get; set; } + [HtmlAttributeNotBound] [ViewContext] public ViewContext ViewContext { get; set; } @@ -67,7 +71,8 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering WebpackConfig = WebpackConfigPath }, request.GetEncodedUrl(), - request.Path + request.QueryString.Value); + request.Path + request.QueryString.Value, + CustomDataParameter); output.Content.SetHtmlContent(result.Html); // Also attach any specified globals to the 'window' object. This is useful for transferring diff --git a/src/Microsoft.AspNetCore.SpaServices/Prerendering/Prerenderer.cs b/src/Microsoft.AspNetCore.SpaServices/Prerendering/Prerenderer.cs index 3b08eff..5e10322 100644 --- a/src/Microsoft.AspNetCore.SpaServices/Prerendering/Prerenderer.cs +++ b/src/Microsoft.AspNetCore.SpaServices/Prerendering/Prerenderer.cs @@ -22,7 +22,8 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering INodeServices nodeServices, JavaScriptModuleExport bootModule, string requestAbsoluteUrl, - string requestPathAndQuery) + string requestPathAndQuery, + object customDataParameter) { return nodeServices.InvokeExport( NodeScript.Value.FileName, @@ -30,7 +31,8 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering applicationBasePath, bootModule, requestAbsoluteUrl, - requestPathAndQuery); + requestPathAndQuery, + customDataParameter); } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.SpaServices/README.md b/src/Microsoft.AspNetCore.SpaServices/README.md index 361b53a..291ce09 100644 --- a/src/Microsoft.AspNetCore.SpaServices/README.md +++ b/src/Microsoft.AspNetCore.SpaServices/README.md @@ -81,6 +81,34 @@ If you try running your app now, you should see the HTML snippet generated by yo As you can see, your JavaScript code receives context information (such as the URL being requested), and returns a `Promise` so that it can asynchronously supply the markup to be injected into the page. You can put whatever logic you like here, but typically you'll want to execute a component from your Angular 2 / React / etc. application. +**Passing data from .NET code into JavaScript code** + +If you want to supply additional data to the JavaScript function that performs your prerendering, you can use the `asp-prerender-data` attribute. You can give any value as long as it's JSON-serializable. Bear in mind that it will be serialized and sent as part of the remote procedure call (RPC) to Node.js, so avoid trying to pass massive amounts of data. + +For example, in your `cshtml`, + +
+ +Now in your JavaScript prerendering function, you can access this data by reading `params.data`, e.g.: + +```javascript +module.exports = function(params) { + return new Promise(function (resolve, reject) { + var result = '

Hello world!

' + + '

Is gold user: ' + params.data.isGoldUser + '

' + + '

Number of cookies: ' + params.data.cookies.length + '

'; + + resolve({ html: result }); + }); +}; +``` + +Notice that the property names are received in JavaScript-style casing (e.g., `isGoldUser`) even though they were sent in C#-style casing (e.g., `IsGoldUser`). This is because of how the JSON serialization is configured by default. + **Passing data from server-side to client-side code** If, as well as returning HTML, you also want to pass some contextual data from your server-side code to your client-side code, you can supply a `globals` object alongside the initial `html`, e.g.: @@ -245,7 +273,7 @@ At this stage, run `webpack` on the command line to build `wwwroot/dist/main.js` You can now run your React code on the client by adding the following to one of your MVC views: -
+
#### Running React code on the server diff --git a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/package.json b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/package.json index 186f11e..2c0651b 100644 --- a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/package.json +++ b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/package.json @@ -1,6 +1,6 @@ { "name": "aspnet-prerendering", - "version": "1.0.1", + "version": "1.0.2", "description": "Helpers for server-side rendering of JavaScript applications in ASP.NET Core projects. Works in conjunction with the Microsoft.AspNetCore.SpaServices NuGet package.", "main": "index.js", "scripts": { diff --git a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/src/Prerendering.ts b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/src/Prerendering.ts index babc1d2..4d9269b 100644 --- a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/src/Prerendering.ts +++ b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/src/Prerendering.ts @@ -24,6 +24,7 @@ export interface BootFuncParams { url: string; // e.g., '/some/path' absoluteUrl: string; // e.g., 'https://example.com:1234/some/path' domainTasks: Promise; + data: any; // any custom object passed through from .NET } export interface BootModuleInfo { @@ -32,7 +33,7 @@ export interface BootModuleInfo { webpackConfig?: string; } -export function renderToString(callback: RenderToStringCallback, applicationBasePath: string, bootModule: BootModuleInfo, absoluteRequestUrl: string, requestPathAndQuery: string) { +export function renderToString(callback: RenderToStringCallback, applicationBasePath: string, bootModule: BootModuleInfo, absoluteRequestUrl: string, requestPathAndQuery: string, customDataParameter: any) { findBootFunc(applicationBasePath, bootModule, (findBootFuncError, bootFunc) => { if (findBootFuncError) { callback(findBootFuncError, null); @@ -51,7 +52,8 @@ export function renderToString(callback: RenderToStringCallback, applicationBase origin: parsedAbsoluteRequestUrl.protocol + '//' + parsedAbsoluteRequestUrl.host, url: requestPathAndQuery, absoluteUrl: absoluteRequestUrl, - domainTasks: domainTaskCompletionPromise + domainTasks: domainTaskCompletionPromise, + data: customDataParameter }; // Open a new domain that can track all the async tasks involved in the app's execution @@ -86,7 +88,7 @@ function findBootModule(applicationBasePath: string, bootModule: BootModuleIn const bootModuleNameFullPath = path.resolve(applicationBasePath, bootModule.moduleName); if (bootModule.webpackConfig) { const webpackConfigFullPath = path.resolve(applicationBasePath, bootModule.webpackConfig); - + let aspNetWebpackModule: any; try { aspNetWebpackModule = require('aspnet-webpack');