MackinnonBuck / blazor-page-script

A Blazor component enabling per-page JavaScript initialization logic in statically rendered Blazor Web apps
65 stars 7 forks source link

[Question] Accessing Razor Component Parameters in JavaScript #3

Open LuohuaRain opened 8 months ago

LuohuaRain commented 8 months ago

Hi there,

Firstly, I want to express my gratitude for your incredible NuGet package. It has been extremely useful in my projects.

I'm currently working with a Blazor component and facing a challenge in accessing a Razor component's parameters within a JavaScript file. Any guidance on this would be greatly appreciated.

Here's the specific scenario:

Weather.razor (work)

<PageTitle>Weather</PageTitle>

<script>
    var weather = `@Content`;
    console.log(weather);
</script>

@Content

@code {
    [Parameter] public required string Content { get; set; }
}

Weather.razor (not work)

<PageTitle>Weather</PageTitle>

<PageScript Src="./Components/Pages/Weather.razor.js" />

@Content

@code {
    [Parameter] public required string Content { get; set; }
}

In the above Razor component, I am trying to pass the Content parameter value to the embedded JavaScript.

Weather.razor.js

export function onLoad() {
    console.log('Load');

   // Question: How can I access the `Content` parameter from the Razor component here?
   // For example, I'd like to do something like this:
   // console.log(@Content);
}

export function onUpdate() {
    console.log('Update');
}

export function onDispose() {
    console.log('Dispose');
}

In the isolated JavaScript file, I'm unsure how to access the Content parameter defined in the Razor component.

Could you provide some insights or suggestions on how to achieve this? Any help or direction would be greatly appreciated.

MackinnonBuck commented 8 months ago

Hi, thanks for reaching out!

The simplest way I can think of would be to embed the "content" value in the DOM and read it from within your script. Something like this should work:

Weather.razor

<div class="script-data-content" data-value="@Content" style="diplay:none"></div>
<PageScript Src="./Components/Pages/Weather.razor.js" />

...

Weather.razor.js

export function onLoad() {
    const content = document.getElementsByClassName('script-data-content')[0].getAttribute('data-value');
    console.log(content);
}

However, if you want something more reusable, you could generalize this further and create your own PageData component:

PageData.razor

<div class="page-data-@Key" data-value="@Value" style="display:none"></div>

@code {
    [Parameter, EditorRequired]
    public string? Key { get; set; } = default!;

    [Parameter, EditorRequired]
    public string? Value { get; set; } = default!;
}

wwwroot/js/page-data.js

function find(key) {
    const pageDataElements = document.getElementsByClassName('page-data-' + key);
    for (let pageDataElement of pageDataElements) {
        const data = pageDataElement.getAttribute('data-value');
        if (data !== null) {
            return data;
        }
    }

    return undefined;
}

export const pageData = {
    find,
};

Weather.razor

<PageData Key="content" Value="@Content" />
<PageScript Src="./Components/Pages/Weather.razor.js" />

...

Weather.razor.js

import { pageData } from '/js/page-data.js';

export function onLoad() {
    const content = pageData.find('content');
    console.log(content);
}

This might be an interesting feature to consider for this package. I'm not sure yet whether it would look like the example above, or if it would work by directly adding parameters to the PageScript that get supplied to the onLoad and onUpdate callbacks. I'll consider adding something like this to the package if there's enough feedback that it would be useful πŸ™‚

Hope this helps!

JeepNL commented 8 months ago

Is it okay if I use (in Home.razor):

<script type="application/json" id="json-data">@((MarkupString)jsonString)</script>

instead of a div, because my json string has 1500 json records, and every record has 8 fields and I don't want &quot; or \u0022 for every double quote " because with it the jsonString is 722kb and with " only 504kb.

I've a live website with this, so you can see even with 1500 json records it's still blazingly fast. It's in Dutch, but it's only about the data and source of the web page: πŸ‘‰ https://nieuwsjunkies.nl/ (runs on a Ubuntu 22.04 VPS with Kestrel and YARP)


And off topic but FYI: If you minify (for example) Weather.razor.js and try to load it with <PageScript Src="./Components/Pages/Weather.razor.min.js" /> it gets a 404 error.

MackinnonBuck commented 8 months ago

Is it okay if I use...

@JeepNL yep, that should be totally ok! You're right in that it does have nicer encoding characteristics when compared to embedding the data in an HTML attribute.

JeepNL commented 8 months ago

@MackinnonBuck Thank you! I wondered if the <script> tag would interfere, but I'm glad to hear I can use it. At this moment I've optimized my code and JSON records so the HTML page itself is less then 230kB, for 1500 records (!) which are Dutch news articles, The complete data transfer for the request is less than 450kB. I am still updating the code,

Note: besides this website is running on an Ubuntu VPS, with Kestrel and behind YARP, it scores also an A+ at Qualys SSL Labs & Immuniweb and πŸ’―% at Internet.nl

It's (still) a simple site, and under development, but there's a lot going on behind the scenes πŸ˜‰For example, the (SQLite) database is updated 3 times per hour with new articles from several RSS feeds, and the server is using in-memory caching for the .NET Framework Core LINQ query. So it queries the database also only 3 times per hour. And I read something about response & output caching for Blazor SSR and maybe it then will also be possible to generate static HTML pages. Can't wait!

I'm a frequent watcher of the Blazor Community standup and really like the projects you're working on for the ASP.NET Blazor team!