Skip to content
This repository was archived by the owner on Apr 8, 2020. It is now read-only.

Allow custom values from JS to be returned from renderToString in SpaServices #384

Closed
loune opened this issue Oct 15, 2016 · 6 comments
Closed

Comments

@loune
Copy link

loune commented Oct 15, 2016

I think a useful feature would be to allow values other than the rendered html to be passed back from the renderToString JavaScript context to the .NET world. For example, the JS might provide a page title, which could then be rendered by the razor layout page. I think the ViewBag would be a good place for this data sharing.

For example, in boot-server.tsx

            resolve({
                html: renderToString(app),
                globals: { initialReduxState: store.getState() },
                viewBag: {
                    title: pageTitle
                }
            });

And in _Layout.cshtml

<html>
  <head>
    <title>@ViewBag.title</title>

Keen for thoughts and comments.

@SteveSandersonMS
Copy link
Member

Good news - we have this feature already :)

See under the subheading Passing data from server-side to client-side code on the docs here: https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices#3-supplying-javascript-code-to-perform-prerendering

@loune
Copy link
Author

loune commented Oct 17, 2016

Hi Steve, what I actually want is Passing data from JS server-side to .NET server-side code . Sort of like the reverse of asp-prerender-data. This is useful for pre-rendering meta-data such as page title server side, before the client gets it. I quickly skimmed the source and didn't find any functionality like it. Is this functionality available? If not, I'm happy to submit a PR based on the ViewBag implementation I outline above. Thanks

@SteveSandersonMS
Copy link
Member

@loune, the Prerenderer.RenderToString method already does have a way of passing raw data (as well as HTML) back from JS server-side to .NET server-side code. It returns a Task<RenderToStringResult>, and RenderToStringResult includes a property called Globals that you can set to any serializable JavaScript object inside your boot function (example). You'll then receive this in your .NET code, where if you want, you can put it in ViewBag or anywhere else.

@loune
Copy link
Author

loune commented Oct 18, 2016

Thanks for the suggestion @SteveSandersonMS. Does this mean I can't use asp-prerender-module and have to create my own implementation? It looks like in PrerenderTagHelper.cs it's coded to emit everything in Global to client side JS.

@MarkPieszak
Copy link
Contributor

@SteveSandersonMS This is the similar situation I was trying to figure out as well for the template. Currently we're not rendering <title> or <meta> tags since .NET has full control over the rest of the , and all we're prerendering is the <app>.

I have solved the issue of grabbing all the needed meta data/tags from the Application getting serialized, now I just need to get it to go -outside- of that, and actually effect the _Layout page, etc. I'm assuming ViewBag is best suitable for this, but I don't think it can be accomplished just yet?

@SteveSandersonMS
Copy link
Member

@MarkPieszak It can be accomplished already, but it takes more code than I'd prefer. For example, you can have an MVC controller like this:

public async Task<IActionResult> Index(
    [FromServices] IHostingEnvironment hostEnv,
    [FromServices] INodeServices nodeServices)
{
    // Instead of using the asp-prerender-module tag helper, we'll invoke Prerenderer.RenderToString
    // in the controller so we can get additional info to pass to the view
    var requestFeature = Request.HttpContext.Features.Get<IHttpRequestFeature>();
    var unencodedPathAndQuery = requestFeature.RawTarget;
    var unencodedAbsoluteUrl = $"{Request.Scheme}://{Request.Host}{unencodedPathAndQuery}";
    var prerenderResult = await Prerenderer.RenderToString(
        hostEnv.ContentRootPath,
        nodeServices,
        new JavaScriptModuleExport("ClientApp/dist/main-server"),
        unencodedAbsoluteUrl,
        unencodedPathAndQuery,
        /* custom data parameter */ null,
        /* timeout milliseconds */ 15*1000,
        Request.PathBase.ToString()
    );

    ViewData["SpaHtml"] = prerenderResult.Html;
    ViewData["Title"] = prerenderResult.Globals["pageTitle"];

    return View();
}

That lets you pass the prerender result data to the view where it can be used in arbitrary ways (e.g., to set the page title).

I recognise that this is non-obvious, and even when you can see the code that works, it's not too nice to be embedding that sort of logic (e.g., the unencodedAbsoluteUrl construction) in your application code. I hope not many people have to do this.

We could make this much more convenient, though. I've filed an issue to track this: #607

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants