I have custom objects for holding child objects full of data. The child objects are initiated with null values for all their properties, so the objects can be referenced and their properties filled from remote sources. This creates a lazy-loading setup.
This code is going to be extremely trimmed down, but everything relevant should be here:
class Collection extends Object { constructor(){ this.loaded = false; var allLoaders = []; var loaderPropmises = []; var resolver; const $this = this; var trackLoaders = function(){ $this.loaded = false; loaderPromises.push(Promise.all(allLoaders).then(() => { //... irrelevant logic in here to ensure only the latest promise sets loaded to true $this.loaded = true; //This is getting called where I expect resolver(); })); } //hook for outside things to watch the promise if they want this.loader = new Promise((resolve) => { //this only gets resolved once, which is fine resolver = resolve; }); //... bunch of code around adding child objects, but the important part: this.add(child){ this[child.id] = child; this.allLoaders.push(child.loader); trackLoaders(); } } }
The child then looks like:
class Child extends Object { constructor(){ this.loaded = false; var resolver; const $this = this; this.loader = new Promise((resolve) => { resolver = resolve; }).then((){ $this.loaded = true; }); this.populate(data){ //bunch of stuff to set data to properties on this object resolver(); } } }
In Vuex 4 I have these Collections as properties on an “AppData” object in the store:
const store = createStore({ state: function(){ AppData: {} }, mutations: { setupCollection(state, name){ if (!Object.hasOwnProperty.call(state.AppData, name){ state.AppData[name] = new Collection(); } } }, actions: { //this is called on each row of data returned from an Axios call add (context, {name, data}){ context.state.AppData[name][data.id].populate(data); } } });
The idea is that whenever a Child
is added to a Collection
, the collection loaded
property will be false until all the Child
loader promises resolve. This all executes perfectly… Except that the loaded
bools aren’t reactive.
Right now, I have a Promise.all
in each component’s Created function that flags the component as “loaded” once all the objects needed for the component have had their “loader” promises resolved. This absolutely works, but isn’t ideal as different data will be available at different times, and there are sometimes hundreds or more of these classes on screen at once. What I’m trying to accomplish is:
<div v-if="!myCollection.loaded"> Loading... </div> <div v-else> Show the data I want here {{myCollection.property}} </div>
So I have two thoughts on overcoming this, either of which would be great:
VueJS3 no longer has a need for Vue.set(), because Proxies. How would I make the
loaded
bools here reactive then? Or more specifically, what am I doing that prevents this from working?Alternatively, is there a practical way to use the
loader
promise directly in a template?
Advertisement
Answer
It looks like Vue’s ref
is what I needed:
this.loaded = ref(false);
This works, at least on the Child
class. I have some sort of circular referencing issue going on and haven’t been able to test on the Collection
class yes, but it should work the same.