mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
committed by
Géry Debongnie
parent
5ff501f8b8
commit
a56939f13a
@@ -142,6 +142,12 @@ const getters = {
|
|||||||
const post = store.getters.getPost(id);
|
const post = store.getters.getPost(id);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Getters take *at most* one argument.
|
||||||
|
|
||||||
|
|
||||||
|
Note that getters are cached if they don't take any argument, or their argument
|
||||||
|
is a string or a number.
|
||||||
|
|
||||||
### Connecting a Component
|
### Connecting a Component
|
||||||
|
|
||||||
By default, an Owl `Component` is not connected to any store. The `connect`
|
By default, an Owl `Component` is not connected to any store. The `connect`
|
||||||
|
|||||||
+14
-1
@@ -47,6 +47,7 @@ export class Store extends EventBus {
|
|||||||
env: any;
|
env: any;
|
||||||
observer: Observer;
|
observer: Observer;
|
||||||
getters: { [name: string]: (payload?) => any };
|
getters: { [name: string]: (payload?) => any };
|
||||||
|
_gettersCache: { [name: string]: {} };
|
||||||
|
|
||||||
constructor(config: StoreConfig, options: StoreOption = {}) {
|
constructor(config: StoreConfig, options: StoreOption = {}) {
|
||||||
super();
|
super();
|
||||||
@@ -56,19 +57,31 @@ export class Store extends EventBus {
|
|||||||
this.mutations = config.mutations;
|
this.mutations = config.mutations;
|
||||||
this.env = config.env;
|
this.env = config.env;
|
||||||
this.observer = new Observer();
|
this.observer = new Observer();
|
||||||
this.observer.notifyCB = this.trigger.bind(this, "update");
|
this.observer.notifyCB = () => {
|
||||||
|
this._gettersCache = {};
|
||||||
|
this.trigger("update");
|
||||||
|
};
|
||||||
this.observer.allowMutations = false;
|
this.observer.allowMutations = false;
|
||||||
this.observer.observe(this.state);
|
this.observer.observe(this.state);
|
||||||
this.getters = {};
|
this.getters = {};
|
||||||
|
this._gettersCache = {};
|
||||||
|
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
this.history.push({ state: this.state });
|
this.history.push({ state: this.state });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cTypes = ["undefined", "number", "string"];
|
||||||
for (let entry of Object.entries(config.getters || {})) {
|
for (let entry of Object.entries(config.getters || {})) {
|
||||||
const name: string = entry[0];
|
const name: string = entry[0];
|
||||||
const func: (...any) => any = entry[1];
|
const func: (...any) => any = entry[1];
|
||||||
this.getters[name] = payload => {
|
this.getters[name] = payload => {
|
||||||
|
if (this._commitLevel === 0 && cTypes.indexOf(typeof payload) >= 0) {
|
||||||
|
this._gettersCache[name] = this._gettersCache[name] || {};
|
||||||
|
this._gettersCache[name][payload] =
|
||||||
|
this._gettersCache[name][payload] ||
|
||||||
|
func({ state: this.state, getters: this.getters }, payload);
|
||||||
|
return this._gettersCache[name][payload];
|
||||||
|
}
|
||||||
return func({ state: this.state, getters: this.getters }, payload);
|
return func({ state: this.state, getters: this.getters }, payload);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -182,6 +182,145 @@ describe("basic use", () => {
|
|||||||
expect((<any>store.getters).beerTasterName(1)).toBe("aaron");
|
expect((<any>store.getters).beerTasterName(1)).toBe("aaron");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("getters are memoized", async () => {
|
||||||
|
const state = {
|
||||||
|
beers: {
|
||||||
|
1: {
|
||||||
|
id: 1,
|
||||||
|
name: "bertinchamps",
|
||||||
|
tasterID: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tasters: {
|
||||||
|
1: {
|
||||||
|
id: 1,
|
||||||
|
name: "aaron"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let n = 0;
|
||||||
|
const getters = {
|
||||||
|
beerTasterName({ state }, beerID) {
|
||||||
|
n++;
|
||||||
|
return state.tasters[state.beers[beerID].tasterID].name;
|
||||||
|
},
|
||||||
|
bestBeerName({ state }) {
|
||||||
|
n++;
|
||||||
|
return state.beers[1].name;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const store = new Store({ state, mutations: {}, actions: {}, getters });
|
||||||
|
expect((<any>store.getters).bestBeerName()).toBe("bertinchamps");
|
||||||
|
expect((<any>store.getters).beerTasterName(1)).toBe("aaron");
|
||||||
|
expect(n).toBe(2);
|
||||||
|
expect((<any>store.getters).bestBeerName()).toBe("bertinchamps");
|
||||||
|
expect((<any>store.getters).beerTasterName(1)).toBe("aaron");
|
||||||
|
expect(n).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getters taking Array as argument aren't memoized", async () => {
|
||||||
|
const state = {
|
||||||
|
beers: {
|
||||||
|
1: {
|
||||||
|
id: 1,
|
||||||
|
name: "bertinchamps",
|
||||||
|
tasterID: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let n = 0;
|
||||||
|
const getters = {
|
||||||
|
getBeerNames({ state }, beerIDs) {
|
||||||
|
n++;
|
||||||
|
return beerIDs.map(beerID => {
|
||||||
|
return state.beers[beerID].name;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const store = new Store({ state, mutations: {}, actions: {}, getters });
|
||||||
|
expect((<any>store.getters).getBeerNames([1])).toEqual(["bertinchamps"]);
|
||||||
|
expect(n).toBe(1);
|
||||||
|
expect((<any>store.getters).getBeerNames([1])).toEqual(["bertinchamps"]);
|
||||||
|
expect(n).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getters cache is nuked on store changes", async () => {
|
||||||
|
const state = {
|
||||||
|
beers: {
|
||||||
|
1: {
|
||||||
|
id: 1,
|
||||||
|
name: "bertinchamps",
|
||||||
|
tasterID: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tasters: {
|
||||||
|
1: {
|
||||||
|
id: 1,
|
||||||
|
name: "aaron"
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
id: 2,
|
||||||
|
name: "gery"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const mutations = {
|
||||||
|
changeTaster({ state }, {beerID, tasterID}) {
|
||||||
|
state.beers[beerID].tasterID = tasterID;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let n = 0;
|
||||||
|
const getters = {
|
||||||
|
beerTasterName({ state }, beerID) {
|
||||||
|
n++;
|
||||||
|
return state.tasters[state.beers[beerID].tasterID].name;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const store = new Store({ state, mutations: mutations, actions: {}, getters });
|
||||||
|
expect((<any>store.getters).beerTasterName(1)).toBe("aaron");
|
||||||
|
expect(n).toBe(1);
|
||||||
|
expect((<any>store.getters).beerTasterName(1)).toBe("aaron");
|
||||||
|
expect(n).toBe(1);
|
||||||
|
|
||||||
|
store.commit('changeTaster', {beerID: 1, tasterID: 2});
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
expect((<any>store.getters).beerTasterName(1)).toBe("gery");
|
||||||
|
expect(n).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getters cache is disabled during a mutation", async () => {
|
||||||
|
const state = {
|
||||||
|
beers: {
|
||||||
|
1: {
|
||||||
|
id: 1,
|
||||||
|
name: "bertinchamps"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const mutations = {
|
||||||
|
renameBeer({ state, getters }, beerID) {
|
||||||
|
expect(getters.beerName(beerID)).toBe('bertinchamps');
|
||||||
|
state.beers[1].name = 'chouffe';
|
||||||
|
expect(getters.beerName(beerID)).toBe('chouffe');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let n = 0;
|
||||||
|
const getters = {
|
||||||
|
beerName({ state }, beerID) {
|
||||||
|
n++;
|
||||||
|
return state.beers[beerID].name;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const store = new Store({ state, mutations: mutations, actions: {}, getters });
|
||||||
|
|
||||||
|
store.commit('renameBeer', 1);
|
||||||
|
expect((<any>store.getters).beerName(1)).toBe('chouffe');
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
expect(n).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
test("getters given to actions", async () => {
|
test("getters given to actions", async () => {
|
||||||
expect.assertions(3);
|
expect.assertions(3);
|
||||||
const state = {
|
const state = {
|
||||||
|
|||||||
Reference in New Issue
Block a user