mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
committed by
Géry Debongnie
parent
1ff3c32fe4
commit
22e48e3be0
+3
-3
@@ -176,7 +176,7 @@ function mapStoreToProps(state) {
|
|||||||
value: state.counter
|
value: state.counter
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const ConnectedCounter = owl.connect(mapStoreToProps)(Counter);
|
const ConnectedCounter = owl.connect(Counter, mapStoreToProps);
|
||||||
|
|
||||||
const counter = new ConnectedCounter({ store, qweb });
|
const counter = new ConnectedCounter({ store, qweb });
|
||||||
```
|
```
|
||||||
@@ -199,5 +199,5 @@ The arguments of `connect` are:
|
|||||||
- `deep`: [only useful if no hashFunction is given] if false, only watch
|
- `deep`: [only useful if no hashFunction is given] if false, only watch
|
||||||
for top level state changes (true by default)
|
for top level state changes (true by default)
|
||||||
|
|
||||||
The `connect` function returns a function that takes a `Component` as argument
|
The `connect` function returns a sub class of the given `Component` which is
|
||||||
and returns a sub Class of this `Component` that is connected to the `store`
|
connected to the `store`.
|
||||||
|
|||||||
+16
-14
@@ -150,10 +150,20 @@ type Constructor<T> = new (...args: any[]) => T;
|
|||||||
interface EnvWithStore extends Env {
|
interface EnvWithStore extends Env {
|
||||||
store: Store;
|
store: Store;
|
||||||
}
|
}
|
||||||
|
type HashFunction = (a: any, b: any) => number;
|
||||||
|
interface StoreOptions {
|
||||||
|
getStore?(Env): Store;
|
||||||
|
hashFunction?: HashFunction;
|
||||||
|
deep?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
let nextID = 1;
|
let nextID = 1;
|
||||||
|
|
||||||
export function connect(mapStateToProps, options: any = {}) {
|
export function connect<E extends EnvWithStore, P, S>(
|
||||||
|
Comp: Constructor<Component<E, P, S>>,
|
||||||
|
mapStoreToProps,
|
||||||
|
options: StoreOptions = <StoreOptions>{}
|
||||||
|
) {
|
||||||
let hashFunction = options.hashFunction || null;
|
let hashFunction = options.hashFunction || null;
|
||||||
const getStore = options.getStore || (env => env.store);
|
const getStore = options.getStore || (env => env.store);
|
||||||
|
|
||||||
@@ -181,25 +191,18 @@ export function connect(mapStateToProps, options: any = {}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return function<E extends EnvWithStore, P, S>(
|
|
||||||
Comp: Constructor<Component<E, P, S>>
|
|
||||||
) {
|
|
||||||
const Result = class extends Comp {
|
const Result = class extends Comp {
|
||||||
constructor(parent, props?: any) {
|
constructor(parent, props?: any) {
|
||||||
const env = parent instanceof Component ? parent.env : parent;
|
const env = parent instanceof Component ? parent.env : parent;
|
||||||
const store = getStore(env);
|
const store = getStore(env);
|
||||||
const ownProps = Object.assign({}, props || {});
|
const ownProps = Object.assign({}, props || {});
|
||||||
const storeProps = mapStateToProps(
|
const storeProps = mapStoreToProps(store.state, ownProps, store.getters);
|
||||||
store.state,
|
|
||||||
ownProps,
|
|
||||||
store.getters
|
|
||||||
);
|
|
||||||
const mergedProps = Object.assign({}, props || {}, storeProps);
|
const mergedProps = Object.assign({}, props || {}, storeProps);
|
||||||
super(parent, mergedProps);
|
super(parent, mergedProps);
|
||||||
(<any>this.__owl__).ownProps = ownProps;
|
(<any>this.__owl__).ownProps = ownProps;
|
||||||
(<any>this.__owl__).currentStoreProps = storeProps;
|
(<any>this.__owl__).currentStoreProps = storeProps;
|
||||||
(<any>this.__owl__).store = store;
|
(<any>this.__owl__).store = store;
|
||||||
(<any>this.__owl__).storeHash = hashFunction(
|
(<any>this.__owl__).storeHash = (<HashFunction>hashFunction)(
|
||||||
{
|
{
|
||||||
state: store.state,
|
state: store.state,
|
||||||
storeProps: storeProps,
|
storeProps: storeProps,
|
||||||
@@ -227,7 +230,7 @@ export function connect(mapStateToProps, options: any = {}) {
|
|||||||
|
|
||||||
_checkUpdate() {
|
_checkUpdate() {
|
||||||
const ownProps = (<any>this.__owl__).ownProps;
|
const ownProps = (<any>this.__owl__).ownProps;
|
||||||
const storeProps = mapStateToProps(
|
const storeProps = mapStoreToProps(
|
||||||
(<any>this.__owl__).store.state,
|
(<any>this.__owl__).store.state,
|
||||||
ownProps,
|
ownProps,
|
||||||
(<any>this.__owl__).store.getters
|
(<any>this.__owl__).store.getters
|
||||||
@@ -235,7 +238,7 @@ export function connect(mapStateToProps, options: any = {}) {
|
|||||||
const options: any = {
|
const options: any = {
|
||||||
currentStoreProps: (<any>this.__owl__).currentStoreProps
|
currentStoreProps: (<any>this.__owl__).currentStoreProps
|
||||||
};
|
};
|
||||||
const storeHash = hashFunction(
|
const storeHash = (<HashFunction>hashFunction)(
|
||||||
{
|
{
|
||||||
state: (<any>this.__owl__).store.state,
|
state: (<any>this.__owl__).store.state,
|
||||||
storeProps: storeProps,
|
storeProps: storeProps,
|
||||||
@@ -256,7 +259,7 @@ export function connect(mapStateToProps, options: any = {}) {
|
|||||||
}
|
}
|
||||||
_updateProps(nextProps, forceUpdate, patchQueue?: any[]) {
|
_updateProps(nextProps, forceUpdate, patchQueue?: any[]) {
|
||||||
if ((<any>this.__owl__).ownProps !== nextProps) {
|
if ((<any>this.__owl__).ownProps !== nextProps) {
|
||||||
(<any>this.__owl__).currentStoreProps = mapStateToProps(
|
(<any>this.__owl__).currentStoreProps = mapStoreToProps(
|
||||||
(<any>this.__owl__).store.state,
|
(<any>this.__owl__).store.state,
|
||||||
nextProps,
|
nextProps,
|
||||||
(<any>this.__owl__).store.getters
|
(<any>this.__owl__).store.getters
|
||||||
@@ -279,5 +282,4 @@ export function connect(mapStateToProps, options: any = {}) {
|
|||||||
let name = `ConnectedComponent${nextID++}`;
|
let name = `ConnectedComponent${nextID++}`;
|
||||||
Object.defineProperty(Result, "name", { value: name });
|
Object.defineProperty(Result, "name", { value: name });
|
||||||
return Result;
|
return Result;
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
+75
-33
@@ -411,10 +411,13 @@ describe("connecting a component to store", () => {
|
|||||||
state.todos.push({ msg });
|
state.todos.push({ msg });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
function mapStateToProps(s) {
|
function mapStoreToProps(s) {
|
||||||
return { todos: s.todos };
|
return { todos: s.todos };
|
||||||
}
|
}
|
||||||
const TodoApp = connect(mapStateToProps)(App);
|
const TodoApp = connect(
|
||||||
|
App,
|
||||||
|
mapStoreToProps
|
||||||
|
);
|
||||||
const store = new Store({ state, mutations });
|
const store = new Store({ state, mutations });
|
||||||
(<any>env).store = store;
|
(<any>env).store = store;
|
||||||
const app = new TodoApp(env);
|
const app = new TodoApp(env);
|
||||||
@@ -434,7 +437,7 @@ describe("connecting a component to store", () => {
|
|||||||
state.todos[0].title = title;
|
state.todos[0].title = title;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
function mapStateToProps(s) {
|
function mapStoreToProps(s) {
|
||||||
return { todos: s.todos };
|
return { todos: s.todos };
|
||||||
}
|
}
|
||||||
const store = new Store({ state, mutations });
|
const store = new Store({ state, mutations });
|
||||||
@@ -451,13 +454,15 @@ describe("connecting a component to store", () => {
|
|||||||
class App extends Component<any, any, any> {}
|
class App extends Component<any, any, any> {}
|
||||||
|
|
||||||
const DeepTodoApp = connect(
|
const DeepTodoApp = connect(
|
||||||
mapStateToProps,
|
App,
|
||||||
|
mapStoreToProps,
|
||||||
{ deep: true }
|
{ deep: true }
|
||||||
)(App);
|
);
|
||||||
const ShallowTodoApp = connect(
|
const ShallowTodoApp = connect(
|
||||||
mapStateToProps,
|
App,
|
||||||
|
mapStoreToProps,
|
||||||
{ deep: false }
|
{ deep: false }
|
||||||
)(App);
|
);
|
||||||
(<any>env).store = store;
|
(<any>env).store = store;
|
||||||
const deepTodoApp = new DeepTodoApp(env);
|
const deepTodoApp = new DeepTodoApp(env);
|
||||||
const shallowTodoApp = new ShallowTodoApp(env);
|
const shallowTodoApp = new ShallowTodoApp(env);
|
||||||
@@ -501,15 +506,16 @@ describe("connecting a component to store", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
function mapStateToProps(s) {
|
function mapStoreToProps(s) {
|
||||||
return { todos: s.todos };
|
return { todos: s.todos };
|
||||||
}
|
}
|
||||||
const TodoApp = connect(
|
const TodoApp = connect(
|
||||||
mapStateToProps,
|
App,
|
||||||
|
mapStoreToProps,
|
||||||
{
|
{
|
||||||
getStore: () => store
|
getStore: () => store
|
||||||
}
|
}
|
||||||
)(App);
|
);
|
||||||
const app = new TodoApp(env);
|
const app = new TodoApp(env);
|
||||||
|
|
||||||
await app.mount(fixture);
|
await app.mount(fixture);
|
||||||
@@ -532,7 +538,10 @@ describe("connecting a component to store", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ConnectedChild = connect(s => s)(Child);
|
const ConnectedChild = connect(
|
||||||
|
Child,
|
||||||
|
s => s
|
||||||
|
);
|
||||||
|
|
||||||
env.qweb.addTemplate(
|
env.qweb.addTemplate(
|
||||||
"Parent",
|
"Parent",
|
||||||
@@ -574,10 +583,13 @@ describe("connecting a component to store", () => {
|
|||||||
|
|
||||||
env.qweb.addTemplate("TodoItem", `<span><t t-esc="props.text"/></span>`);
|
env.qweb.addTemplate("TodoItem", `<span><t t-esc="props.text"/></span>`);
|
||||||
class TodoItem extends Component<any, any, any> {}
|
class TodoItem extends Component<any, any, any> {}
|
||||||
const ConnectedTodo = connect((state, props) => {
|
const ConnectedTodo = connect(
|
||||||
|
TodoItem,
|
||||||
|
(state, props) => {
|
||||||
const todo = state.todos.find(t => t.id === props.id);
|
const todo = state.todos.find(t => t.id === props.id);
|
||||||
return todo;
|
return todo;
|
||||||
})(TodoItem);
|
}
|
||||||
|
);
|
||||||
|
|
||||||
env.qweb.addTemplate(
|
env.qweb.addTemplate(
|
||||||
"TodoList",
|
"TodoList",
|
||||||
@@ -591,10 +603,13 @@ describe("connecting a component to store", () => {
|
|||||||
widgets = { ConnectedTodo };
|
widgets = { ConnectedTodo };
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapStateToProps(state) {
|
function mapStoreToProps(state) {
|
||||||
return { todos: state.todos };
|
return { todos: state.todos };
|
||||||
}
|
}
|
||||||
const ConnectedTodoList = connect(mapStateToProps)(TodoList);
|
const ConnectedTodoList = connect(
|
||||||
|
TodoList,
|
||||||
|
mapStoreToProps
|
||||||
|
);
|
||||||
|
|
||||||
(<any>env).store = store;
|
(<any>env).store = store;
|
||||||
const app = new ConnectedTodoList(env);
|
const app = new ConnectedTodoList(env);
|
||||||
@@ -632,13 +647,16 @@ describe("connecting a component to store", () => {
|
|||||||
</div>`
|
</div>`
|
||||||
);
|
);
|
||||||
class TodoItem extends Component<any, any, any> {}
|
class TodoItem extends Component<any, any, any> {}
|
||||||
const ConnectedTodo = connect((state, props, getters) => {
|
const ConnectedTodo = connect(
|
||||||
|
TodoItem,
|
||||||
|
(state, props, getters) => {
|
||||||
const todo = state.todos.find(t => t.id === props.id);
|
const todo = state.todos.find(t => t.id === props.id);
|
||||||
return {
|
return {
|
||||||
activeTodoText: getters.text(todo.id),
|
activeTodoText: getters.text(todo.id),
|
||||||
importantTodoText: getters.importantTodoText()
|
importantTodoText: getters.importantTodoText()
|
||||||
};
|
};
|
||||||
})(TodoItem);
|
}
|
||||||
|
);
|
||||||
|
|
||||||
env.qweb.addTemplate(
|
env.qweb.addTemplate(
|
||||||
"TodoList",
|
"TodoList",
|
||||||
@@ -652,10 +670,13 @@ describe("connecting a component to store", () => {
|
|||||||
widgets = { ConnectedTodo };
|
widgets = { ConnectedTodo };
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapStateToProps(state) {
|
function mapStoreToProps(state) {
|
||||||
return { todos: state.todos };
|
return { todos: state.todos };
|
||||||
}
|
}
|
||||||
const ConnectedTodoList = connect(mapStateToProps)(TodoList);
|
const ConnectedTodoList = connect(
|
||||||
|
TodoList,
|
||||||
|
mapStoreToProps
|
||||||
|
);
|
||||||
|
|
||||||
(<any>env).store = store;
|
(<any>env).store = store;
|
||||||
const app = new ConnectedTodoList(env);
|
const app = new ConnectedTodoList(env);
|
||||||
@@ -669,9 +690,12 @@ describe("connecting a component to store", () => {
|
|||||||
test("connected component is updated when props are updated", async () => {
|
test("connected component is updated when props are updated", async () => {
|
||||||
env.qweb.addTemplate("Beer", `<span><t t-esc="props.name"/></span>`);
|
env.qweb.addTemplate("Beer", `<span><t t-esc="props.name"/></span>`);
|
||||||
class Beer extends Component<any, any, any> {}
|
class Beer extends Component<any, any, any> {}
|
||||||
const ConnectedBeer = connect((state, props) => {
|
const ConnectedBeer = connect(
|
||||||
|
Beer,
|
||||||
|
(state, props) => {
|
||||||
return state.beers[props.id];
|
return state.beers[props.id];
|
||||||
})(Beer);
|
}
|
||||||
|
);
|
||||||
|
|
||||||
env.qweb.addTemplate(
|
env.qweb.addTemplate(
|
||||||
"App",
|
"App",
|
||||||
@@ -717,10 +741,13 @@ describe("connecting a component to store", () => {
|
|||||||
const store = new Store({ state, mutations });
|
const store = new Store({ state, mutations });
|
||||||
(<any>env).store = store;
|
(<any>env).store = store;
|
||||||
|
|
||||||
function mapStateToProps(state) {
|
function mapStoreToProps(state) {
|
||||||
return { beers: state.beers, otherKey: 1 };
|
return { beers: state.beers, otherKey: 1 };
|
||||||
}
|
}
|
||||||
const ConnectedApp = connect(mapStateToProps)(App);
|
const ConnectedApp = connect(
|
||||||
|
App,
|
||||||
|
mapStoreToProps
|
||||||
|
);
|
||||||
const app = new ConnectedApp(env);
|
const app = new ConnectedApp(env);
|
||||||
|
|
||||||
await app.mount(fixture);
|
await app.mount(fixture);
|
||||||
@@ -743,13 +770,16 @@ describe("connecting a component to store", () => {
|
|||||||
</div>`
|
</div>`
|
||||||
);
|
);
|
||||||
class Beer extends Component<any, any, any> {}
|
class Beer extends Component<any, any, any> {}
|
||||||
const ConnectedBeer = connect((state, props) => {
|
const ConnectedBeer = connect(
|
||||||
|
Beer,
|
||||||
|
(state, props) => {
|
||||||
return {
|
return {
|
||||||
selected: state.beers[props.id],
|
selected: state.beers[props.id],
|
||||||
consumed: state.beers[state.consumedID] || null,
|
consumed: state.beers[state.consumedID] || null,
|
||||||
taster: state.taster
|
taster: state.taster
|
||||||
};
|
};
|
||||||
})(Beer);
|
}
|
||||||
|
);
|
||||||
|
|
||||||
env.qweb.addTemplate(
|
env.qweb.addTemplate(
|
||||||
"App",
|
"App",
|
||||||
@@ -812,13 +842,16 @@ describe("connecting a component to store", () => {
|
|||||||
</div>`
|
</div>`
|
||||||
);
|
);
|
||||||
class Beer extends Component<any, any, any> {}
|
class Beer extends Component<any, any, any> {}
|
||||||
const ConnectedBeer = connect((state, props) => {
|
const ConnectedBeer = connect(
|
||||||
|
Beer,
|
||||||
|
(state, props) => {
|
||||||
return {
|
return {
|
||||||
selected: state.beers[props.id],
|
selected: state.beers[props.id],
|
||||||
consumed: state.beers[state.consumedID] || null,
|
consumed: state.beers[state.consumedID] || null,
|
||||||
taster: state.taster
|
taster: state.taster
|
||||||
};
|
};
|
||||||
})(Beer);
|
}
|
||||||
|
);
|
||||||
|
|
||||||
env.qweb.addTemplate(
|
env.qweb.addTemplate(
|
||||||
"App",
|
"App",
|
||||||
@@ -909,18 +942,24 @@ describe("connecting a component to store", () => {
|
|||||||
class Parent extends Component<any, any, any> {
|
class Parent extends Component<any, any, any> {
|
||||||
widgets = { Child: ConnectedChild };
|
widgets = { Child: ConnectedChild };
|
||||||
}
|
}
|
||||||
const ConnectedParent = connect(function(s) {
|
const ConnectedParent = connect(
|
||||||
|
Parent,
|
||||||
|
function(s) {
|
||||||
steps.push("parent");
|
steps.push("parent");
|
||||||
return { current: s.current, isvisible: s.isvisible };
|
return { current: s.current, isvisible: s.isvisible };
|
||||||
})(Parent);
|
}
|
||||||
|
);
|
||||||
|
|
||||||
env.qweb.addTemplate("Child", `<span><t t-esc="props.msg"/></span>`);
|
env.qweb.addTemplate("Child", `<span><t t-esc="props.msg"/></span>`);
|
||||||
class Child extends Component<any, any, any> {}
|
class Child extends Component<any, any, any> {}
|
||||||
|
|
||||||
const ConnectedChild = connect(function(s, props) {
|
const ConnectedChild = connect(
|
||||||
|
Child,
|
||||||
|
function(s, props) {
|
||||||
steps.push("child");
|
steps.push("child");
|
||||||
return { msg: s.msg[props.key] };
|
return { msg: s.msg[props.key] };
|
||||||
})(Child);
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const state = { current: "a", msg: { a: "a", b: "b" } };
|
const state = { current: "a", msg: { a: "a", b: "b" } };
|
||||||
const mutations = {
|
const mutations = {
|
||||||
@@ -953,9 +992,12 @@ describe("connecting a component to store", () => {
|
|||||||
steps.push("patched");
|
steps.push("patched");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const ConnectedApp = connect(function(s) {
|
const ConnectedApp = connect(
|
||||||
|
App,
|
||||||
|
function(s) {
|
||||||
return { msg: s.msg };
|
return { msg: s.msg };
|
||||||
})(App);
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const state = { msg: "a" };
|
const state = { msg: "a" };
|
||||||
const mutations = {
|
const mutations = {
|
||||||
|
|||||||
@@ -432,7 +432,7 @@ class TodoItem extends owl.Component {
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// TodoApp
|
// TodoApp
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
function mapStateToProps(state) {
|
function mapStoreToProps(state) {
|
||||||
return {
|
return {
|
||||||
todos: state.todos
|
todos: state.todos
|
||||||
};
|
};
|
||||||
@@ -489,7 +489,7 @@ class TodoApp extends owl.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ConnectedTodoApp = owl.connect(mapStateToProps)(TodoApp);
|
const ConnectedTodoApp = owl.connect(TodoApp, mapStoreToProps);
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// App Initialization
|
// App Initialization
|
||||||
|
|||||||
Reference in New Issue
Block a user