Open ThaNarie opened 7 years ago
Are we still considering this? The singleton models are very simple to use, anything more advanced can be decided per project. And if that is a common need then it should be a lib IMO.
This is 2,5 years old, so need to re-check if and how we still need it, and if the solutions still make sense, or if there are new options.
I hope that you and others who did more with muban have some insights/experience on what's needed/missing in this area.
I'm using the singleton model for most of my projects. In my cases most of the use-cases are fairly simple or simple enough to not have to use a different method.
This is a two-part issue, since this needs to be solved for two situations:
.hbs
components1. Normal Muban
In this case it really depends on how much global state we need.
Most state only lives on the current page, and will be lost when the page is reloaded
Some state might be more persistent (cookie/LocalStorage), question is if we interface directly with the DOM APIs, or if we are going to abstract that away in our own global state. Examples are:
Even if state only exists on the current page, it might still cross component boundaries; updates to data/state in one component should also reflect in other components. When this is required, you probably want to have some global state management in place.
2. SPA Muban
In some cases, one page of the website is a complex tool that only lives on the client. The backend might provide an initial data set (embedded or via API), and all logic happens on the client. Such a tool can consist of multiple virtual pages, and can become quite large.
The best solution depends on how much data/communication is needed between components.
Options
Singleton Model
The simplest option would be to have a Modal class that exports itself as a single instance. It can be required in all components, where they can read and write data.
The class would consist of multiple observables, so components can subscribe to them to receive updates.
Model:
ViewModel:
However, when dealing with large (external) datasets, updating and detecting changes in deeply nested objects can become quite cumbersome. Other libraries like mobX and Vue deal with this in a way that in userland you working with normal objects, but in the background they are able to detect changes to them. This feature make those options more friendly to users. Unfortunately, knockout observables don't have this option.
Pros:
Cons:
Redux
By choosing redux as the global state container, and creating an interface to map the redux state to observables in components, we can make it easier to work with data.
I created a proof-of-concept function called
knockout-redux-connect
, where the interface is based on theconnect
function of react-redux.When having the following component:
And the following class:
You can connect to redux like this:
The
params
object is already used by Knockout components to receive data from a parent component. The connect function sits in between, and merges the object returned from its functions to pass it to the component.When doing this (the first time), it converts the returned state object to observables by using the ko.mapping plugin. When the redux state changes at a later time, it uses that function again to updated the stored observables.
Using this method the component can use/subscribe the received observable params the same way as if they were passed from the parent component.
Actions work the same way as in redux, they can called from the component. Optionally we could unwrap the passed payload so it doesn't contain any observables before it's dispatched.
The normal handlebars components could also be updated to support this util.
Pros:
Cons:
ko.mapping
is not really maintained (used a fork that received some updates), so could contain bugs with a low chance of being fixed by othersCustom Stores
The ko.mapping plugin could also be used to create a custom store, similar to it's done in mobx/vue.
Vuex uses actions and mutation to update data in the store. These could be converted to observables after execution. It also has a concept of getters, which are like knockout computes, or redux selectors.
MobX uses uses actions as a context where all updates to the observables in the store are batched.
Pros:
Cons:
Feedback is much appreciated! @flut1 (you have knockout and redux experience) Edgar (you have knockout, redux and mobX experience) @larsvanbraam, @mmricco, @ReneDrie (you have knockout and Vue experience)