Skip to content

Commit ea0a32a

Browse files
Implement and document asp-prerender-data
1 parent 1a53411 commit ea0a32a

File tree

5 files changed

+45
-8
lines changed

5 files changed

+45
-8
lines changed

src/Microsoft.AspNetCore.SpaServices/Prerendering/PrerenderTagHelper.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public class PrerenderTagHelper : TagHelper
1919
private const string PrerenderModuleAttributeName = "asp-prerender-module";
2020
private const string PrerenderExportAttributeName = "asp-prerender-export";
2121
private const string PrerenderWebpackConfigAttributeName = "asp-prerender-webpack-config";
22+
private const string PrerenderDataAttributeName = "asp-prerender-data";
2223
private static INodeServices _fallbackNodeServices; // Used only if no INodeServices was registered with DI
2324

2425
private readonly string _applicationBasePath;
@@ -51,6 +52,9 @@ public PrerenderTagHelper(IServiceProvider serviceProvider)
5152
[HtmlAttributeName(PrerenderWebpackConfigAttributeName)]
5253
public string WebpackConfigPath { get; set; }
5354

55+
[HtmlAttributeName(PrerenderDataAttributeName)]
56+
public object CustomDataParameter { get; set; }
57+
5458
[HtmlAttributeNotBound]
5559
[ViewContext]
5660
public ViewContext ViewContext { get; set; }
@@ -67,7 +71,8 @@ public override async Task ProcessAsync(TagHelperContext context, TagHelperOutpu
6771
WebpackConfig = WebpackConfigPath
6872
},
6973
request.GetEncodedUrl(),
70-
request.Path + request.QueryString.Value);
74+
request.Path + request.QueryString.Value,
75+
CustomDataParameter);
7176
output.Content.SetHtmlContent(result.Html);
7277

7378
// Also attach any specified globals to the 'window' object. This is useful for transferring

src/Microsoft.AspNetCore.SpaServices/Prerendering/Prerenderer.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,17 @@ public static Task<RenderToStringResult> RenderToString(
2222
INodeServices nodeServices,
2323
JavaScriptModuleExport bootModule,
2424
string requestAbsoluteUrl,
25-
string requestPathAndQuery)
25+
string requestPathAndQuery,
26+
object customDataParameter)
2627
{
2728
return nodeServices.InvokeExport<RenderToStringResult>(
2829
NodeScript.Value.FileName,
2930
"renderToString",
3031
applicationBasePath,
3132
bootModule,
3233
requestAbsoluteUrl,
33-
requestPathAndQuery);
34+
requestPathAndQuery,
35+
customDataParameter);
3436
}
3537
}
3638
}

src/Microsoft.AspNetCore.SpaServices/README.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,34 @@ If you try running your app now, you should see the HTML snippet generated by yo
8181

8282
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.
8383

84+
**Passing data from .NET code into JavaScript code**
85+
86+
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.
87+
88+
For example, in your `cshtml`,
89+
90+
<div id="my-spa" asp-prerender-module="ClientApp/boot-server"
91+
asp-prerender-data="new {
92+
IsGoldUser = true,
93+
Cookies = ViewContext.HttpContext.Request.Cookies
94+
}""></div>
95+
96+
Now in your JavaScript prerendering function, you can access this data by reading `params.data`, e.g.:
97+
98+
```javascript
99+
module.exports = function(params) {
100+
return new Promise(function (resolve, reject) {
101+
var result = '<h1>Hello world!</h1>'
102+
+ '<p>Is gold user: ' + params.data.isGoldUser + '</p>'
103+
+ '<p>Number of cookies: ' + params.data.cookies.length + '</p>';
104+
105+
resolve({ html: result });
106+
});
107+
};
108+
```
109+
110+
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.
111+
84112
**Passing data from server-side to client-side code**
85113

86114
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`
245273

246274
You can now run your React code on the client by adding the following to one of your MVC views:
247275

248-
<div id="my-spa"></div>
276+
<div id="my-spa"></div>
249277
<script src="/dist/main.js"></script>
250278

251279
#### Running React code on the server

src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "aspnet-prerendering",
3-
"version": "1.0.1",
3+
"version": "1.0.2",
44
"description": "Helpers for server-side rendering of JavaScript applications in ASP.NET Core projects. Works in conjunction with the Microsoft.AspNetCore.SpaServices NuGet package.",
55
"main": "index.js",
66
"scripts": {

src/Microsoft.AspNetCore.SpaServices/npm/aspnet-prerendering/src/Prerendering.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export interface BootFuncParams {
2424
url: string; // e.g., '/some/path'
2525
absoluteUrl: string; // e.g., 'https://example.com:1234/some/path'
2626
domainTasks: Promise<any>;
27+
data: any; // any custom object passed through from .NET
2728
}
2829

2930
export interface BootModuleInfo {
@@ -32,7 +33,7 @@ export interface BootModuleInfo {
3233
webpackConfig?: string;
3334
}
3435

35-
export function renderToString(callback: RenderToStringCallback, applicationBasePath: string, bootModule: BootModuleInfo, absoluteRequestUrl: string, requestPathAndQuery: string) {
36+
export function renderToString(callback: RenderToStringCallback, applicationBasePath: string, bootModule: BootModuleInfo, absoluteRequestUrl: string, requestPathAndQuery: string, customDataParameter: any) {
3637
findBootFunc(applicationBasePath, bootModule, (findBootFuncError, bootFunc) => {
3738
if (findBootFuncError) {
3839
callback(findBootFuncError, null);
@@ -51,7 +52,8 @@ export function renderToString(callback: RenderToStringCallback, applicationBase
5152
origin: parsedAbsoluteRequestUrl.protocol + '//' + parsedAbsoluteRequestUrl.host,
5253
url: requestPathAndQuery,
5354
absoluteUrl: absoluteRequestUrl,
54-
domainTasks: domainTaskCompletionPromise
55+
domainTasks: domainTaskCompletionPromise,
56+
data: customDataParameter
5557
};
5658

5759
// Open a new domain that can track all the async tasks involved in the app's execution
@@ -86,7 +88,7 @@ function findBootModule<T>(applicationBasePath: string, bootModule: BootModuleIn
8688
const bootModuleNameFullPath = path.resolve(applicationBasePath, bootModule.moduleName);
8789
if (bootModule.webpackConfig) {
8890
const webpackConfigFullPath = path.resolve(applicationBasePath, bootModule.webpackConfig);
89-
91+
9092
let aspNetWebpackModule: any;
9193
try {
9294
aspNetWebpackModule = require('aspnet-webpack');

0 commit comments

Comments
 (0)