宜兴通达竭诚为您服务。

通达科技 - 宜兴电脑维护及IT服务外包服务商

OWL 教程: TodoApp-13、最后一击
  • 上传者: Administrator
  • 上传时间:2023年09月15日 08时29分02秒
摘要:
我们的列表功能齐全。我们仍然可以添加一些额外的细节来改善用户体验。 当用户鼠标停留在任务上时,添加一个视觉反馈: . task : hover { background-color : # def0ff ; } 使任务的文本可点击,以切换其复选框: < input type = " c ......
正文 相关文章 请喝咖啡

    我们的列表功能齐全。我们仍然可以添加一些额外的细节来改善用户体验。

    1. 当用户鼠标停留在任务上时,添加一个视觉反馈:
    .task:hover {
      background-color: #def0ff;
    }
    1. 使任务的文本可点击,以切换其复选框:
    <input type="checkbox" t-att-checked="props.task.isCompleted"
        t-att-id="props.task.id"
        t-on-click="() => store.toggleTask(props.task)"/>
    <label t-att-for="props.task.id"><t t-esc="props.task.text"/></label>
    1. 敲击完成任务的文本:
    .task.done label {
      text-decoration: line-through;
    }

    最终代码

    我们的应用程序现在完成了。它可以工作,UI代码与业务逻辑代码很好地分离,它是可测试的,所有代码都在150行以下(包含模板!)

    作为参考,这里是最终的代码:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <title>OWL Todo App</title>
        <link rel="stylesheet" href="app.css" />
      </head>
      <body>
        <script src="owl.js"></script>
        <script src="app.js"></script>
      </body>
    </html>
    (function () {
      const { Component, mount, xml, useRef, onMounted, useState, reactive, useEnv } = owl;
    
      // -------------------------------------------------------------------------
      // Store
      // -------------------------------------------------------------------------
      function useStore() {
        const env = useEnv();
        return useState(env.store);
      }
    
      // -------------------------------------------------------------------------
      // TaskList
      // -------------------------------------------------------------------------
      class TaskList {
        constructor(tasks) {
          this.tasks = tasks || [];
          const taskIds = this.tasks.map((t) => t.id);
          this.nextId = taskIds.length ? Math.max(...taskIds) + 1 : 1;
        }
    
        addTask(text) {
          text = text.trim();
          if (text) {
            const task = {
              id: this.nextId++,
              text: text,
              isCompleted: false,
            };
            this.tasks.push(task);
          }
        }
    
        toggleTask(task) {
          task.isCompleted = !task.isCompleted;
        }
    
        deleteTask(task) {
          const index = this.tasks.findIndex((t) => t.id === task.id);
          this.tasks.splice(index, 1);
        }
      }
    
      function createTaskStore() {
        const saveTasks = () => localStorage.setItem("todoapp", JSON.stringify(taskStore.tasks));
        const initialTasks = JSON.parse(localStorage.getItem("todoapp") || "[]");
        const taskStore = reactive(new TaskList(initialTasks), saveTasks);
        saveTasks();
        return taskStore;
      }
    
      // -------------------------------------------------------------------------
      // Task Component
      // -------------------------------------------------------------------------
      class Task extends Component {
        static template = xml/* xml */ `
          <div class="task" t-att-class="props.task.isCompleted ? 'done' : ''">
            <input type="checkbox"
              t-att-id="props.task.id"
              t-att-checked="props.task.isCompleted"
              t-on-click="() => store.toggleTask(props.task)"/>
            <label t-att-for="props.task.id"><t t-esc="props.task.text"/></label>
            <span class="delete" t-on-click="() => store.deleteTask(props.task)">🗑</span>
          </div>`;
    
        static props = ["task"];
    
        setup() {
          this.store = useStore();
        }
      }
    
      // -------------------------------------------------------------------------
      // Root Component
      // -------------------------------------------------------------------------
      class Root extends Component {
        static 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">
              <t t-foreach="displayedTasks" t-as="task" t-key="task.id">
                <Task task="task"/>
              </t>
            </div>
            <div class="task-panel" t-if="store.tasks.length">
              <div class="task-counter">
                <t t-esc="displayedTasks.length"/>
                <t t-if="displayedTasks.length lt store.tasks.length">
                    / <t t-esc="store.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="() => this.setFilter(f)"
                  t-esc="f"/>
              </div>
            </div>
          </div>`;
        static components = { Task };
    
        setup() {
          const inputRef = useRef("add-input");
          onMounted(() => inputRef.el.focus());
          this.store = useStore();
          this.filter = useState({ value: "all" });
        }
    
        addTask(ev) {
          // 13 is keycode for ENTER
          if (ev.keyCode === 13) {
            this.store.addTask(ev.target.value);
            ev.target.value = "";
          }
        }
    
        get displayedTasks() {
          const tasks = this.store.tasks;
          switch (this.filter.value) {
            case "active":
              return tasks.filter((t) => !t.isCompleted);
            case "completed":
              return tasks.filter((t) => t.isCompleted);
            case "all":
              return tasks;
          }
        }
    
        setFilter(filter) {
          this.filter.value = filter;
        }
      }
    
      // -------------------------------------------------------------------------
      // Setup
      // -------------------------------------------------------------------------
      const env = { store: createTaskStore() };
      mount(Root, document.body, { dev: true, env });
    })();
    .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;
    }
    本文章从网上收集,如有侵权请联系tderp@tderp.com删除
  • 微信扫一扫,一分也是爱:
  • 微信

服务原则及地区范围

宜兴通达网络科技有限公司,地处中国宜兴环科园内,是一家高新技术企业。公司在企业网络维护和企业信息化建设与咨询方面,有10多年经验。

我公司愿与客户一道,力求彻底解决客户问题!
我们不是在给企业提供“头痛医头、脚痛医脚”的暂时解决方案,而是在部署根本性安全与稳定服务!!
我们愿携手客户,建立企业IT规划;杜绝随意安装系统、软件等操作;力求共同维护有序、安全、稳定的网络办公环境!!!
IT服务,服务是根本,客户是上帝;我们提供快速响应、快速上门、快速排查,提供优质高效的服务!!!!

通达科技提供全国范围内的服务,服务形式包括远程协助、电话咨询、电子邮件咨询、传真咨询、问答平台的问题解决等。

宜兴地区提供上门服务:

  • 市区服务:宜城街道、城北街道(屺亭街道)、新街街道、新庄街道、环科园、渚桥开发区
  • 市郊服务:张渚镇、西渚镇、太华镇、徐舍镇、官林镇、杨巷镇、新建镇、和桥镇、高塍镇、万石镇、周铁镇、芳桥镇、丁蜀镇、湖父镇。
  • 联系电话:189-21-343434
  • 在线沟通: