javascriptasp.netblazorserver-side-rendering

Calling Javascript from a SSR Page Blazor .NET core


I have a .NET 8 Core Blazor page with Authentication. My App.razor contains the following html and code.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="/" />
    <link href="https://fonts.googleapis.com/css2?family=Montserrat&display=swap" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css2?family=Macondo&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="" id="userStylesheet" data-permanent />
    <link rel="stylesheet" href="css/CardFlip.css" />
    <link href="css/site.css" rel="stylesheet" />
    <link rel="stylesheet" href="css/cutealert.css" />
    <link rel="stylesheet" href="css/fontawesome.css" />
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <HeadOutlet @rendermode="RenderModeForPage" />
</head>

<body>
    <Routes @rendermode="RenderModeForPage" />

    <script>window.$ = window.jQuery = require('jquery');</script>
    <script src="js/all.min.js"></script>
    <script src="js/Jquery.js"></script>
    <script src="js/bootstrap.bundle.min.js"></script>
    <script src="js/ShowModal.js"></script>
    <script src="js/cute-alert.js"></script>
    <script src="js/jspdf.min.js"></script>
    <script src="js/html2canvas.js"></script>
    <script src="js/html2pdf.bundle.min.js"></script>
    <script src="_framework/blazor.server.js"></script>

</body>

</html>

@code {
    [CascadingParameter]
    private HttpContext HttpContext { get; set; } = default!;

    private IComponentRenderMode? RenderModeForPage => HttpContext.Request.Path.StartsWithSegments("/Account")
        ? null
        : InteractiveServer;
}

I have a global theme I am trying to apply to all of my pages and I do that with a javascript function.

function ChangeStyleSheet(newSheet) {
    var linkTag = document.getElementById('userStylesheet');
    linkTag.href = newSheet;
}

The problem is that I cannot seem to call this function when I am rendering in SSR. That is anytime I load in a page from /Account, it renders SSR. Other pages render in Interactive Server. I can easily call the javascript function using JSRuntime when rendered in Interactive server.

I found a microsoft page which discusses Javascript in SSR where it seems to create a component called PageScript and another js module which you create which I am unsure its purpose. Yet I still do not see in that example how I am supposed to call ChangeStyleSheet from an SSR page.

Does anyone have a solution? If I need to add more information, please let me know and I will certainly add it.


Solution

  • If you want to follow the official document to call js from a SSR page. Follow the steps below:

    Add the following JavaScript initializer to the server project's wwwroot folder. The {NAME} placeholder must be the name of the app's assembly in order for Blazor to locate and load the file automatically. If the server app's assembly name is BlazorSample, the file is named BlazorSample.lib.module.js.

    wwwroot/{NAME}.lib.module.js:

    const pageScriptInfoBySrc = new Map();
    
    function registerPageScriptElement(src) {
        if (!src) {
            throw new Error('Must provide a non-empty value for the "src" attribute.');
        }
    
        let pageScriptInfo = pageScriptInfoBySrc.get(src);
    
        if (pageScriptInfo) {
            pageScriptInfo.referenceCount++;
        } else {
            pageScriptInfo = { referenceCount: 1, module: null };
            pageScriptInfoBySrc.set(src, pageScriptInfo);
            initializePageScriptModule(src, pageScriptInfo);
        }
    }
    
    function unregisterPageScriptElement(src) {
        if (!src) {
            return;
        }
    
        const pageScriptInfo = pageScriptInfoBySrc.get(src);
    
        if (!pageScriptInfo) {
            return;
        }
    
        pageScriptInfo.referenceCount--;
    }
    
    async function initializePageScriptModule(src, pageScriptInfo) {
        if (src.startsWith("./")) {
            src = new URL(src.substr(2), document.baseURI).toString();
        }
    
        const module = await import(src);
    
        if (pageScriptInfo.referenceCount <= 0) {
            return;
        }
    
        pageScriptInfo.module = module;
        module.onLoad?.();
        module.onUpdate?.();
    }
    
    function onEnhancedLoad() {
        for (const [src, { module, referenceCount }] of pageScriptInfoBySrc) {
            if (referenceCount <= 0) {
                module?.onDispose?.();
                pageScriptInfoBySrc.delete(src);
            }
        }
    
        for (const { module } of pageScriptInfoBySrc.values()) {
            module?.onUpdate?.();
        }
    }
    
    export function afterWebStarted(blazor) {
        customElements.define('page-script', class extends HTMLElement {
            static observedAttributes = ['src'];
    
            attributeChangedCallback(name, oldValue, newValue) {
                if (name !== 'src') {
                    return;
                }
    
                this.src = newValue;
                unregisterPageScriptElement(oldValue);
                registerPageScriptElement(newValue);
            }
    
            disconnectedCallback() {
                unregisterPageScriptElement(this.src);
            }
        });
    
        blazor.addEventListener('enhancedload', onEnhancedLoad);
    }
    

    Add the following shared PageScript component to the server app.

    Components/PageScript.razor:

    <page-script StyleSheetUrl="@StyleSheetUrl"></page-script>
    <script>
        document.addEventListener('DOMContentLoaded', function () {
                ChangeStyleSheet('@StyleSheetUrl');
        });
    </script>
    <script src="/test.js"></script>
    @code {
        [Parameter] public string StyleSheetUrl { get; set; } = "app.css";
    }
    

    Account.razor:

    @page "/Account"
    <PageScript StyleSheetUrl="test.css" />
    

    js function ChangeStyleSheet locates in wwwroot/test.js:

    function ChangeStyleSheet(newSheet) {
        var linkTag = document.getElementById('userStylesheet');
        alert(linkTag);
        linkTag.href = newSheet;
    }