powerbuoy
powerbuoy

Reputation: 12838

Aurelia event won't fire on first page load

I'm using Aurelia's EventAggregator to publish and subscribe to events in my app. Some of my custom elements take a while to load, so I've used a loading event to tell my main app.js to add a spinner to the page during loading.

This works fine once the app has loaded and I start switching between routes, however, on first page load the event doesn't seem to fire - or at least, it isn't picked up by the subscribe method.

Here's basically what my app.js does:

attached () {
    this.mainLoadingSubscription = this.eventAggregator.subscribe('main:loading', isLoading => {
        // If main is loading
        if (isLoading) {
            document.documentElement.classList.add('main-loading');
        }
        // Stopped loading
        else {
            document.documentElement.classList.remove('main-loading');
        }
    });
}

And here's what my custom elements do:

constructor () {
    this.eventAggregator.publish('main:loading', true);
}

attached () {
    this.doSomeAsyncAction.then(() => {
        this.eventAggregator.publish('main:loading', false);
    });
}

This causes the first page load to not show a spinner and instead the page looks kind of broken.

Btw, I am aware of the fact that you can return a Promise from the element's attached method but I can't do this because of this other problem

Upvotes: 1

Views: 1012

Answers (1)

Matthew James Davis
Matthew James Davis

Reputation: 12295

Set up your subscriptions in your viewModel's constructor or activate callback

In the above example, you set up subscriptions in the viewModel's attached() callback. Unfortunately, this will not be called until all child custom element's attached() callbacks are called, which is long after any one custom element's constructor() function is called.

Try this:

app.js

@inject(EventAggregator)
export class AppViewModel {

    constructor(eventAggregator) {
        this.mainLoadingSubscription = eventAggregator.subscribe('main:loading', isLoading => {
            // do your thing
        }
    }
}

If the viewModel is a route that can be navigated to, then handle this in the activate() callback with appropriate teardown in the deactivate() callback.

@inject(EventAggregator)
export class AppViewModel {

    constructor(eventAggregator) {
        this.eventAggregator = eventAggregator;
    }

    activate() {
        this.mainLoadingSubscription = this.eventAggregator.subscribe('main:loading', isLoading => {
            // do your thing
        }
    }

    deactivate() {
        this.mainLoadingSubscription.dispose();
    }
}

Upvotes: 2

Related Questions