refactoring, work on environment

This commit is contained in:
Géry Debongnie
2019-01-23 14:28:41 +01:00
parent 08938d2334
commit 76fedae5a9
18 changed files with 138 additions and 63 deletions
+5 -5
View File
@@ -1,16 +1,16 @@
import { init } from "../../../libs/snabbdom/src/snabbdom";
import sdListeners from "../../../libs/snabbdom/src/modules/eventlisteners";
import sdAttrs from "../../../libs/snabbdom/src/modules/attributes";
import sdListeners from "../../../libs/snabbdom/src/modules/eventlisteners";
import { init } from "../../../libs/snabbdom/src/snabbdom";
import { VNode } from "../../../libs/snabbdom/src/vnode";
import QWeb from "./qweb_vdom";
const patch = init([sdListeners, sdAttrs]);
export interface WidgetEnv {
export interface WEnv {
qweb: QWeb;
}
export default class Widget<T extends WidgetEnv> {
export default class Widget<T extends WEnv> {
name: string = "widget";
template: string = "<div></div>";
vnode: VNode | null = null;
@@ -49,7 +49,7 @@ export default class Widget<T extends WidgetEnv> {
async mount(target?: HTMLElement): Promise<VNode> {
await this.willStart();
this.env!.qweb.addTemplate(this.name, this.template);
this.env.qweb.addTemplate(this.name, this.template);
delete this.template;
const vnode = await this.render();
+2 -2
View File
@@ -640,7 +640,7 @@ const forEachDirective: Directive = {
const onDirective: Directive = {
name: "on",
priority: 90,
atNodeCreation({ ctx, fullName, value, nodeID }) {
atNodeCreation({ ctx, fullName, value, nodeID, qweb }) {
const eventName = fullName.slice(5);
let extraArgs;
let handler = value.replace(/\(.*\)/, function(args) {
@@ -649,7 +649,7 @@ const onDirective: Directive = {
});
ctx.addLine(
`p${nodeID}.on = {${eventName}: context['${handler}'].bind(context${
extraArgs ? ", " + extraArgs : ""
extraArgs ? ", " + qweb._formatExpression(extraArgs) : ""
})}`
);
}
+25 -8
View File
@@ -1,12 +1,29 @@
import QWeb from "./core/qweb_vdom";
import { WEnv } from "./core/widget";
import actions, { Action } from "./services/actions";
import ActionManager from "./services/action_manager";
import Ajax from "./services/ajax";
import Router from "./services/router";
import actions from "./services/actions";
import { Env } from "./types";
const env: Env = {
qweb: new QWeb(),
router: new Router(),
services: { actions }
};
export interface Env extends WEnv {
actionManager: ActionManager;
actions: Action[];
ajax: Ajax;
router: Router;
}
export function makeEnvironment(): Env {
const qweb = new QWeb();
const router = new Router();
const ajax = new Ajax();
const actionManager = new ActionManager();
return {
qweb,
ajax,
router,
actionManager,
actions
};
}
export default env;
+2 -1
View File
@@ -1,9 +1,10 @@
///<amd-module name="main" />
import RootWidget from "./widgets/root_widget";
import env from "./env";
import {makeEnvironment} from "./env";
document.addEventListener("DOMContentLoaded", async function() {
const env = makeEnvironment();
const rootWidget = new RootWidget(env);
await rootWidget.mount(document.body);
});
@@ -0,0 +1,24 @@
import { Action } from "./actions";
// export interface Action {
// id: number;
// title: string;
// Widget: Type<Widget<Env>>;
// default?: boolean;
// }
// const actions: Action[] = [
// { id: 1, title: "Discuss", Widget: Discuss, default: true },
// { id: 2, title: "CRM", Widget: CRM }
// ];
export default class ActionManager {
doAction(action: Action) {
console.log(action);
// load data (??)
// trigger action somewhere
// upload url with router
}
}
+10 -2
View File
@@ -1,9 +1,11 @@
import CRM from "../widgets/crm";
import Discuss from "../widgets/discuss";
import Widget from "../core/widget";
import { Env, Type } from "../types";
import { Env } from "../env";
interface Type<T> extends Function {
new (...args: any[]): T;
}
export interface Action {
id: number;
@@ -19,3 +21,9 @@ const actions: Action[] = [
export default actions;
// class ActionManager {
// doAction(action: Action) {
// }
// }
+2
View File
@@ -0,0 +1,2 @@
export default class Ajax{}
+27 -20
View File
@@ -1,39 +1,46 @@
export type Route = string;
export type Query = { [key: string]: string };
export interface RouteInfo {
route: Route;
export interface Route {
// this is the part before the hash: www.something.com/web#action=1 => web
path: string;
query: Query;
title: string;
}
function clearSlaches(s: string): string {
return s.replace(/\/$/, "").replace(/^\//, "");
}
export default class Router {
listeners: { owner: any; callback: (info: RouteInfo) => void }[] = [];
listeners: { owner: any; callback: (info: Route) => void }[] = [];
constructor() {
window.addEventListener("popstate", this.onUrlChange.bind(this));
window.onhashchange = function () { console.log('aaaa'); debugger; }
}
onUrlChange() {
const info = this.getRouteInfo();
for (let listener of this.listeners) {
listener.callback.call(listener.owner, info);
}
onUrlChange(event: PopStateEvent) {
debugger
event.preventDefault();
const info = this.getRoute();
for (let listener of this.listeners) {
listener.callback.call(listener.owner, info);
}
}
/**
* @param {Route} route relative route: for example, /web/
* @param {Query} query
*/
navigate(info: Partial<RouteInfo>) {
const currentRouteInfo = this.getRouteInfo();
const route = info.route || currentRouteInfo.route;
navigate(info: Partial<Route>) {
const currentRoute = this.getRoute();
const route = info.path || currentRoute.path;
const query = info.query || {};
const title = info.title || currentRouteInfo.title;
const title = info.title || currentRoute.title;
const url = this.formatURL(route, query);
window.history.pushState(null, title, url);
}
register(owner: any, callback: (info: RouteInfo) => void) {
register(owner: any, callback: (info: Route) => void) {
this.listeners.push({ owner, callback });
}
@@ -41,24 +48,24 @@ export default class Router {
this.listeners = this.listeners.filter(l => l.owner !== owner);
}
formatURL(route: Route, query: Query): string {
let url = route;
formatURL(path: string, query: Query): string {
let url = clearSlaches(path);
let hasHash = false;
for (let key in query) {
url = url + (hasHash ? "&" : "#");
url = url + `${key}=${query[key]}`;
hasHash = true;
}
return url;
return "/" + url;
}
getRouteInfo(): RouteInfo {
const route = window.location.pathname.slice(1);
getRoute(): Route {
const path = clearSlaches(window.location.pathname);
const query = {};
for (let part of window.location.hash.slice(1).split("?")) {
let [key, value] = part.split("=");
query[key] = value;
}
return { route, query, title: document.title };
return { path, query, title: document.title };
}
}
-11
View File
@@ -1,11 +0,0 @@
import { WidgetEnv } from "./core/widget";
import Router from "./services/router";
export interface Env extends WidgetEnv {
router: Router;
services: { [key: string]: any };
}
export interface Type<T> extends Function {
new (...args: any[]): T;
}
+1 -1
View File
@@ -1,5 +1,5 @@
import Widget from "../core/widget";
import { Env } from "../types";
import { Env } from "../env";
const template = `
<div class="o_crm">
+1 -1
View File
@@ -1,5 +1,5 @@
import Widget from "../core/widget";
import { Env } from "../types";
import { Env } from "../env";
const template = `
<div>
+1 -1
View File
@@ -1,6 +1,6 @@
import Widget from "../core/widget";
import Counter from "./counter";
import { Env } from "../types";
import { Env } from "../env";
const template = `
<div class="o_discuss">
+10 -3
View File
@@ -1,13 +1,15 @@
import Widget from "../core/widget";
import { Action } from "../services/actions";
import { Env } from "../types";
import { Env } from "../env";
const template = `
<div class="o_navbar">
<span class="title">Odoo</span>
<ul>
<li t-foreach="env.services.actions" t-as="action">
<a t-att-href="getUrl(action)"><t t-esc="action.title"/></a>
<li t-foreach="env.actions" t-as="action">
<a t-on-click="activateAction(action)" t-att-href="getUrl(action)">
<t t-esc="action.title"/>
</a>
</li>
</ul>
</div>
@@ -21,4 +23,9 @@ export default class Navbar extends Widget<Env> {
const action_id = String(action.id);
return this.env.router.formatURL("web", { action_id });
}
activateAction(action: Action, event: MouseEvent) {
event.preventDefault();
this.env.actionManager.doAction(action);
}
}
+3 -3
View File
@@ -1,7 +1,7 @@
import Widget from "../core/widget";
import Navbar from "./navbar";
import { Action } from "../services/actions";
import { Env } from "../types";
import { Env } from "../env";
const template = `
<div class="o_web_client">
@@ -39,9 +39,9 @@ export default class RootWidget extends Widget<Env> {
}
getAction(): Action {
const routeInfo = this.env.router.getRouteInfo();
const routeInfo = this.env.router.getRoute();
const actionID = parseInt(routeInfo.query.action_id);
let actions: Action[] = this.env.services.actions;
let actions: Action[] = this.env.actions;
let action = actions.find(a => a.id === actionID);
if (!action) {
action = actions.find(a => a.default === true);
+15
View File
@@ -651,6 +651,21 @@ describe("t-on", () => {
(<HTMLElement>node).click();
expect(a).toBe(6);
});
test("can bind handlers with loop variable as argument", () => {
expect.assertions(1);
const qweb = new QWeb();
qweb.addTemplate("test", `
<ul>
<li t-foreach="['someval']" t-as="action"><a t-on-click="activate(action)">link</a></li>
</ul>`);
const node = renderToDOM(qweb, "test", {
activate(action) {
expect(action).toBe('someval');
}
});
(<HTMLElement>node).getElementsByTagName('a')[0].click();
});
});
+6 -3
View File
@@ -1,9 +1,12 @@
import Widget, {WidgetEnv} from "../src/ts/core/widget";
import Widget, {WEnv} from "../src/ts/core/widget";
import QWeb from "../src/ts/core/qweb_vdom";
import { Type } from "../src/ts/types";
type TestEnv = WidgetEnv;
interface Type<T> extends Function {
new (...args: any[]): T;
}
type TestEnv = WEnv;
type TestWidget = Widget<TestEnv>
function makeWidget(W: Type<TestWidget>): TestWidget {