odoo14--odoo前端框架owl【odoo web library】
阅读原文时间:2023年07月11日阅读:1

原文链接:https://www.alanhou.org/odoo-14-owl-todolist/

1、组件树

Root

        /   \

       A     B

      / \

     C   D

2、状态(state):各组件可管理其自身的本地状态。这是一个简单的ES6类,没有特殊规则:

const { Component, useState } = owl;
const { xml } = owl.tags;

class Counter extends Component {
static template = xml`
`;

state = useState({ value: 0 });
}

class App extends Component {
static template = xml`

Hello Owl
`;

static components = { Counter };
}

const app = new App();
app.mount(document.body);

owl_todoapp

(function () {
const {Component, Store} = owl; // const {xml} = owl.tags; //用来插入xml代码片段的
const {whenReady} = owl.utils; //工具类
const {useRef, useDispatch, useState, useStore} = owl.hooks; //钩子

// -------------------------------------------------------------------------  
// Store  
// -------------------------------------------------------------------------  
const actions = {  
    addTask({state}, title) {  
        title = title.trim();  
        if (title) {  
            const task = {  
                id: state.nextId++,  
                title: title,  
                isCompleted: false,  
            };  
            state.tasks.push(task);  
        }  
    },  
    toggleTask({state}, id) {  
        const task = state.tasks.find((t) => t.id === id);  
        task.isCompleted = !task.isCompleted;  
    },  
    deleteTask({state}, id) {  
        const index = state.tasks.findIndex((t) => t.id === id);  
        state.tasks.splice(index, 1);  
    },  
};

const initialState = {  
    nextId: 1,  
    tasks: \[\],  
};

// -------------------------------------------------------------------------  
// Task Component  可点击任务标题来切换复选框状态:

// -------------------------------------------------------------------------  
const TASK\_TEMPLATE = xml/\* xml \*/\`  
<div class="task" t-att-class="props.task.isCompleted ? 'done' : ''">  
    <input type="checkbox" t-att-checked="props.task.isCompleted"  
        t-att-id="props.task.id"  
        t-on-click="dispatch('toggleTask', props.task.id)"/>  
    <label t-att-for="props.task.id"><t t-esc="props.task.title"/></label>  
    <span class="delete" t-on-click="dispatch('deleteTask', props.task.id)"></span>  
</div>\`;

class Task extends Component {  
    static template = TASK\_TEMPLATE;  
    static props = \["task"\];  
    dispatch = useDispatch();  
}

// -------------------------------------------------------------------------  
// App Component  
// -------------------------------------------------------------------------  
const APP\_TEMPLATE = xml/\* xml \*/\`  
<div class="todo-app">  
    <input placeholder="Enter a new task" t-on-keyup="addTask" t-ref="add-input"/>  
    <div class="task-list">  
        <Task t-foreach="displayedTasks" t-as="task" t-key="task.id" task="task"/>  
    </div>  
    <div class="task-panel" t-if="tasks.length">  
        <div class="task-counter">  
            <t t-esc="displayedTasks.length"/>  
            <t t-if="displayedTasks.length lt tasks.length">  
                / <t t-esc="tasks.length"/>  
            </t>  
            task(s)  
        </div>  
        <div>  
            <span t-foreach="\['all', 'active', 'completed'\]"  
                t-as="f" t-key="f"  
                t-att-class="{active: filter.value===f}"  
                t-on-click="setFilter(f)"  
                t-esc="f"/>  
        </div>  
    </div>  
</div>\`;

class App extends Component {  
    static template = APP\_TEMPLATE;  
    static components = {Task};

    inputRef = useRef("add-input");  
    tasks = useStore((state) => state.tasks);  
    filter = useState({value: "all"});  
    dispatch = useDispatch();

    mounted() {  
        this.inputRef.el.focus();  
    }

    addTask(ev) {  
        // 13 is keycode for ENTER  
        if (ev.keyCode === 13) {  
            this.dispatch("addTask", ev.target.value);  
            ev.target.value = "";  
        }  
    }

    get displayedTasks() {  
        switch (this.filter.value) {  
            case "active":  
                return this.tasks.filter((t) => !t.isCompleted);  
            case "completed":  
                return this.tasks.filter((t) => t.isCompleted);  
            case "all":  
                return this.tasks;  
        }  
    }

    setFilter(filter) {  
        this.filter.value = filter;  
    }  
}

// -------------------------------------------------------------------------  
// Setup code  
// -------------------------------------------------------------------------  
function makeStore() {  
    const localState = window.localStorage.getItem("todoapp");  
    const state = localState ? JSON.parse(localState) : initialState;  
    const store = new Store({state, actions});  
    store.on("update", null, () => {  
        localStorage.setItem("todoapp", JSON.stringify(store.state));  
    });  
    return store;  
}

function setup() {  
    owl.config.mode = "dev";  
    App.env.store = makeStore();  
    const app = new App();  
    app.mount(document.body);  
}

whenReady(setup);  

})();

.todo-app {
width: 300px;
margin: 50px auto;
background: aliceblue;
padding: 10px;
}

.todo-app > input {
display: block;
margin: auto;
}

.task-list {
margin-top: 8px;
}

.task {
font-size: 18px;
color: #111111;
display: grid;
grid-template-columns: 30px auto 30px;
}

//在用户鼠标悬浮到任务上方时添加视觉反馈:

.task:hover {
background-color: #def0ff;
}

.task > input {
margin: auto;
}

.delete {
opacity: 0;
cursor: pointer;
text-align: center;
}

.task:hover .delete {
opacity: 1;
}

.task.done {
opacity: 0.7;
}

//对已完成任务的标题添加中划线

.task.done label {
text-decoration: line-through;
}

.task-panel {
color: #0088ff;
margin-top: 8px;
font-size: 14px;
display: flex;
}

.task-panel .task-counter {
flex-grow: 1;
}

.task-panel span {
padding: 5px;
cursor: pointer;
}

.task-panel span.active {
font-weight: bold;
}


OWL Todo App

owl.js  在   https://github.com/odoo/owl

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器