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

Tips For Developers Using This Project #9

Open sbwalker opened 6 months ago

sbwalker commented 6 months ago

Thank you @MackinnonBuck for providing this sample project - I integrated it into Oqtane (https://github.com/oqtane/oqtane.framework) so that we can provide developers with a much easier method for integrating Bootstrap templates.

A few notes for other developers who may be interested in utilizing this BlazorPageScript component:

  1. It relies on "JavaScript Initializers" (https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/startup?view=aspnetcore-8.0) which allow you to execute logic before and after a Blazor app loads.
  2. The JavaScript Initializer is named wwwroot/BlazorPageScript.lib.module.js - it registers a custom HTML element called "page-script" which accepts a single parameter of "src".
  3. The most critical event when running on Static Blazor using Enhanced Navigation is the "onUpdate" event. This event is called on every page transition and basically replaces the traditional role of the JavaScript "onload" event.
  4. The onUpdate event is not just called once - it is called multiple times. Originally I thought it would be a good idea to create a function so that it only executes my custom script once (ie. by comparing the url with the previous url) however this turned out to be a bad idea as the onUpdate event can be triggered prior to all components on your page being fully rendered - which means that if your script relies on the existence of a specific element - that element may not exist on the page yet. So its best to process every onUpdate event.
  5. When you create your script you usually only need to implement the onUpdate event as it handles new page loads as well as page transitions.
    
    function myPageInitializer() {
    // implementation
    }

export function onUpdate() { myPageInitializer(); }


6. If the script you want to execute contains window.addEventListener('load', xxx) methods you will want to remove them as the "load" event is never called in page transitions.

window.addEventListener('load', () => { if (window.location.hash) { if (select(window.location.hash)) { scrollto(window.location.hash) } } });


becomes
if (window.location.hash) {
  if (select(window.location.hash)) {
    scrollto(window.location.hash)
  }
}
obrana-boranija commented 6 months ago

You had any luck w/ AOS?

sbwalker commented 6 months ago

Yes I was able to get AOS working as part of a BootstrapMade template

obrana-boranija commented 6 months ago

Any tips or an example? I was struggling with initiating actually. It is trying to initiate before script is loaded in page-script (my thought).

TBH, I'm not seasoned in js at all, so it is hard to me figuring it out.

Thanks in advance!

sbwalker commented 5 months ago

@obrana-boranija I should be able to post a functional example of AOS within the next week... however the example will be based on Oqtane (https://github.com/oqtane/oqtane.framework) so the implementation will be slightly different than the samples which Mackinnon provided in this repo.

obrana-boranija commented 5 months ago

@sbwalker Thanks a lot for that!

In the meantime, do you think I'm on the right path here?

const aosScriptInfo = {
    referenceCount: 0,
    module: null
};

async function initializeAosModule() {
    if (!aosScriptInfo.module) {
        // Load AOS CSS
        if (!document.querySelector('link[href="https://cdnjs.cloudflare.com/ajax/libs/aos/2.3.4/aos.css"]')) {
            const link = document.createElement('link');
            link.href = 'https://cdnjs.cloudflare.com/ajax/libs/aos/2.3.4/aos.css';
            link.rel = 'stylesheet';
            document.head.appendChild(link);
        }

        // Dynamically import AOS JS
        const module = await import('https://cdnjs.cloudflare.com/ajax/libs/aos/2.3.4/aos.js');
        aosScriptInfo.module = module;
        module.default.init({
            duration: 1000, // AOS initialization options if needed
            easing: 'ease-in-out',
            once: false,
            mirror: false
        });
    }

    aosScriptInfo.module.default.refreshHard();
}

function onEnhancedLoad() {
    if (aosScriptInfo.referenceCount <= 0) {
        aosScriptInfo.module?.default?.refreshHard();
    }
}

export function afterWebStarted(blazor) {
    customElements.define('aos-script', class extends HTMLElement {
        connectedCallback() {
            aosScriptInfo.referenceCount++;
            initializeAosModule();
        }

        disconnectedCallback() {
            aosScriptInfo.referenceCount--;
        }
    });

    blazor.addEventListener('enhancedload', onEnhancedLoad);
}

export function onUpdate() {
    // Ensure AOS is refreshed on every update/navigation
    initializeAosModule();
}

It is working, but I have concerns about optimization and memory leaks. As I already said, I am not proficient in JS, so it's better to ask someone seasoned. :)

sbwalker commented 5 months ago

@obrana-boranija I published a repo with a functional example of how blazor-page-script is used in Oqtane:

https://github.com/oqtane/Oqtane.Theme.Arsha

As I mentioned above, how you integrate blazor-page-script will vary based upon the requirements of your own application... but the script you posted above looks fine to me at first glance.

richardaubin commented 2 months ago

Thank you @sbwalker for posting this.

I was having trouble having bootstrap event (hovers, clicks) working with the theme I'm working with. Turns out the author had bundled bootstrap with the theme specific javascript, and when using enhanced navigation with the pagescript component, all bootstrap event handlers stopped working on every 2nd update.

I yanked bootstrap from there after looking how you're doing it in the octane theme, and loading it normally with my other vendor libraries.

Only took me 8 hours to figure out what was going on and prevented me from deciding on using JSInterop lol :)