diff --git a/doc/store.md b/doc/store.md index 1cb45f44..32d19530 100644 --- a/doc/store.md +++ b/doc/store.md @@ -227,6 +227,25 @@ The `ConnectedComponent` class can be configured with the following fields: - `deep` (boolean): [only useful if no hashFunction is given] if `false`, only watch for top level state changes (`true` by default) +Note that the class `ConnectedComponent` has a `dispatch` method. This means +that the previous example could be simplified like this: + +```javascript +class Counter extends owl.ConnectedComponent { + static mapStoreToProps(state) { + return { + value: state.counter + }; + } +} +``` + +```xml + +``` + ### Semantics The `Store` and the `ConnectedComponent` try to be smart and to optimize as much diff --git a/src/store/connected_component.ts b/src/store/connected_component.ts index 72c187ca..7064104e 100644 --- a/src/store/connected_component.ts +++ b/src/store/connected_component.ts @@ -39,10 +39,18 @@ export class ConnectedComponent extends Component return {}; } + dispatch() { + const [name, ...payload] = arguments; + (this.__owl__ as any).store.dispatch(name, ...payload); + } + /** * Need to do this here so 'deep' can be overrided by subcomponent easily */ - async __prepareAndRender(scope?: Object, vars?: any): ReturnType["__prepareAndRender"]> { + async __prepareAndRender( + scope?: Object, + vars?: any + ): ReturnType["__prepareAndRender"]> { const store = this.getStore(this.env); const ownProps = this.props || {}; this.storeProps = (this.constructor).mapStoreToProps(store.state, ownProps, store.getters); @@ -87,22 +95,22 @@ export class ConnectedComponent extends Component this.storeProps = storeProps; let didChange = options.didChange; if (storeHash !== (this.__owl__ as any).storeHash) { - (this.__owl__ as any).storeHash = storeHash; + (this.__owl__ as any).storeHash = storeHash; didChange = true; } (this.__owl__ as any).rev = store.observer.rev; - return didChange + return didChange; } async __checkUpdate() { const observer = (this.__owl__ as any).store.observer; if (observer.rev === (this.__owl__ as any).rev) { - // update was already done by updateProps, from parent - return; + // update was already done by updateProps, from parent + return; } const didChange = this.__updateStoreProps(this.props); if (didChange) { - this.render(); + this.render(); } } } diff --git a/tests/store/connected_component.test.ts b/tests/store/connected_component.test.ts index 82e84ba8..7039f501 100644 --- a/tests/store/connected_component.test.ts +++ b/tests/store/connected_component.test.ts @@ -140,6 +140,43 @@ describe("connecting a component to store", () => { expect(fixture.innerHTML).toMatchSnapshot(); }); + test("can dispatch actions from a connected component", async () => { + env.qweb.addTemplates(` + +
+ + +
+
+ `); + + const store = new Store({ + state: { value: 1 }, + actions: { + inc({ state }) { + state.value++; + } + } + }); + (env).store = store; + + class App extends ConnectedComponent { + static mapStoreToProps(s) { + return { value: s.value }; + } + } + const app = new App(env); + + await app.mount(fixture); + expect(fixture.innerHTML).toBe("
1
"); + + const button = (app.el).getElementsByTagName("button")[0]; + await button.click(); + await nextTick(); + + expect(fixture.innerHTML).toBe("
2
"); + }); + test("connected child components with custom hooks", async () => { let steps: any = []; env.qweb.addTemplates(` diff --git a/tools/playground/samples.js b/tools/playground/samples.js index 88d93275..9b6c0611 100644 --- a/tools/playground/samples.js +++ b/tools/playground/samples.js @@ -275,13 +275,11 @@ const LOCALSTORAGE_KEY = "todos-odoo"; //------------------------------------------------------------------------------ const actions = { addTodo({ state }, title) { - const id = state.nextId++; - const todo = { - id, + state.todos.push({ + id: state.nextId++, title, completed: false - }; - state.todos.push(todo); + }); }, removeTodo({ state }, id) { const index = state.todos.findIndex(t => t.id === id); @@ -298,7 +296,7 @@ const actions = { dispatch("removeTodo", todo.id); }); }, - toggleAll({ state, commit }, completed) { + toggleAll({ state, dispatch }, completed) { state.todos.forEach(todo => { dispatch("editTodo", { id: todo.id, @@ -341,11 +339,11 @@ class TodoItem extends owl.Component { state = { isEditing: false }; removeTodo() { - this.env.dispatch("removeTodo", this.props.id); + this.env.store.dispatch("removeTodo", this.props.id); } toggleTodo() { - this.env.dispatch("toggleTodo", this.props.id); + this.env.store.dispatch("toggleTodo", this.props.id); } async editTodo() { @@ -426,20 +424,12 @@ class TodoApp extends owl.store.ConnectedComponent { if (ev.keyCode === ENTER_KEY) { const title = ev.target.value; if (title.trim()) { - this.env.dispatch("addTodo", title); + this.dispatch("addTodo", title); } ev.target.value = ""; } } - clearCompleted() { - this.env.dispatch("clearCompleted"); - } - - toggleAll() { - this.env.dispatch("toggleAll", !this.allChecked); - } - setFilter(filter) { this.state.filter = filter; } @@ -453,7 +443,6 @@ const qweb = new owl.QWeb(TEMPLATES); const env = { qweb, store, - dispatch: store.dispatch.bind(store), }; const app = new TodoApp(env); app.mount(document.body); @@ -466,7 +455,7 @@ const TODO_APP_STORE_XML = `
- +
    @@ -492,7 +481,7 @@ const TODO_APP_STORE_XML = ` Completed
-