ViqMontana
ViqMontana

Reputation: 5688

How to observe/subscribe to changes in the state in Aurelia Store?

I have a property selectedOption on the state of my Aurelia Store, which can be changed via actions. I want to observe/subscribe to any changes to this property on the state. My problem is the subscription within the BindingEngine doesn't work because every time you change the state, you create a new copy of the state, therefore the subscription no longer works.

Here is my example:

import { Disposable, BindingEngine, autoinject } from "aurelia-framework";
import { connectTo, dispatchify } from "aurelia-store";

@autoinject()
@connectTo()
export class Holiday {
    subscription: Disposable;
    state: any;

    constructor(private bindingEngine: BindingEngine) {
    }

    async updateState()
    {
        await dispatchify(changeSelectedOption)();
    }

    attached() {
        this.subscription = this.bindingEngine
            .propertyObserver(this.state, 'selectedOption')
            .subscribe((newValue, oldValue) => {
                console.log("something has changed!")
            });
    }
}

export class State {
    selectedOption: number = 0;
}

export const changeSelectedOption = (state: State) => {
    let updatedState = { ...state };
    updatedState.selectedOption++;
    return updatedState;
}

store.registerAction("changeSelectedOption", changeSelectedOption);

The first time, my subscription will work and the console will log "something has changed!" as the state is the same object, but it won't work after.

Another solution I could use would be to have a computed property like so:

@computedFrom("state.selectedOption")
get selectedOptionChanged()
{
    return console.log("something has changed!");
}

This is a hack, and this computed won't ever be triggered as it is not bound to anything in the HTML.

For context, I want to trigger a server call every time the selectedOption property changes.

What can I do to receive all updates from the property on the state?

Upvotes: 1

Views: 855

Answers (1)

zewa666
zewa666

Reputation: 2603

The thing here is that the state observable exposed by the Store is a RxJS stream. So with the advent of the new "multi-selector" feature for connectTo you could create two bindings. By implementing a hook called selectorKey Changed, in your sample selectedOptionChanged it would get called on every change of said property.

@connectTo({
  selector: {
    state: (store) => store.state, // the complete state if you need
    selectedOption: (store) => store.state.pluck("selectedOption")
  }
})

class MyVM {
  ...
  selectedOptionChanged(newState, oldState) {
    // notification about new state
  }
}

Instead of store.state.pluck("selectedOption") you can also experiment with additional conditions when to notify about changes like adding distinctUntilChanged etc.

Read more about multi-selectors in the updated docs.


Alternatively if you don't want to use the connectTo decorator, simply use the state property and create another subscription

Upvotes: 2

Related Questions