[Vue.js + Axios] ToDoList (上)

铁人赛终於来到最後两篇(最後一天是废文心得文)

这两天来个大颗ㄧ点(跟前几篇比)的栗子,用 Vue.js 实作一个「一眼瞬间」版的 ToDoList。
为什麽叫「一眼瞬间」呢?因为 F5 刷新就没啦! 先来做这个最基本连 localStorage 都没有的 ToDoList,当做 Vue.js 应用练习。
但怕被认为这样太混,再多加一项初始载入指定的 API(Axios GET)。


练习素材:


先来将该做的事情列成清单:

  1. (data)资料格式定义,每笔待办项有:'id' / 'title' / 'completed'

    • id 值不可重复
    • completed 为 true / false
  2. 可以读取(R)初始资料(API GET)

  3. 可以新增(C)待办项并即时显示(预设状态为「未完成」)

  4. 可以编辑(U)目前的待办项

    • 更名
    • 点击时变更完成状态
  5. 可以删除(D)待办项

  6. 可以一次删除所有待办项

  7. 页签(Tab)依完成状态分类清单:全部 / 进行中 / 已完成

  8. 计数目前未完成的项,且有变动时即时更新计数


查看完整栗子 >> Codepen Demo


(下列 HTML 的部分这边只截取有应用到 Vue.js 的部分,也就是要触发事件 / 进行资料绑定的,完整版请看 Codepen)



<div class="container" id="app">
  <input class="form-control" type="text" placeholder="准备要做的任务" 
          v-model="newTodo" @keyup.enter="addTodo"/>
  <div class="input-group-append">
    <button class="btn" type="button" @click="addTodo">新增</button>
    <template v-for="(item, index) in visibilityList">
      <li class="nav-item" :key="index"></li>
      <a class="nav-link" href="#" :class="{ active: visibility == item.value }" 
          @click="(visibility = item.value)">{{ item.name }}
      </a>
    </template>
    <template v-for="(item) in filteredTodos">
      <li class="list-group-item" :key="item.id" @dblclick="editTodo(item)">
        <div v-if="item.id !== cacheTodo.id">
          <input class="form-check-input" :id="item.id" type="checkbox" 
                  @click="changeComplated(item.id)" 
                  v-model="item.completed"/>
          <label class="form-check-label" :class="{ completed: item.completed }"
                  :for="item.id"> {{ item.title }}
          </label>
          <button class="close" type="button" aria-label="Close" 
                  @click="removeTodo(item)">×
          </button>
        </div>
        <input class="form-control" type="text" 
            v-model="cacheTitle" v-if="item.id == cacheTodo.id" 
            @keyup.esc="cancelEdit" @keyup.enter="doneEdit(item)"/>
      </li>
    </template>
  </div>
  <div class="card-footer">
      {{ `还有${activeTodosLength}笔任务未完成` }}
      <a href="#" @click="cleanTodo">清除所有任务</a>
  </div>
</div>
var urlAPI = "https://eudora-hsj.github.io/Vue-practice/data/todolist.json"

var app = new Vue({
    el: "#app",
    data() {
        return {
            newTodo:'',
            todos: [],
            visibilityList: [
                { name: "全部", value: "all" },
                { name: "进行中", value: "active" },
                { name: "已完成", value: "completed" }
            ],
            visibility: 'all',
            cacheTodo: {},
            cacheTitle: '',
        }
    },
    created() {
        this.getList(urlAPI)
    },
    methods: {
        getList(url) {
            axios
                .get(url)
                .then((res) => {
                    this.todos = res.data.data
                })
                .catch((err) => {
                    console.log(err)
                })
        },
        addTodo() {
            let newTodoStr = this.newTodo.trim()
            if (!newTodoStr) {
                return
            }
            this.newTodo = ""
            let submitData = {
                id: Math.floor(Date.now()),
                title: newTodoStr,
                completed: false
            }
            this.todos.push(submitData)
        },
        removeTodo(item) {
            this.todos.splice(this.getIndex(item.id), 1)
        },
        editTodo(item) {
            this.cacheTodo = item
            this.cacheTitle = item.title
        },
        cancelEdit() {
            this.cacheTodo = {}
        },
        doneEdit(item) {
            item.title = this.cacheTitle
            this.cacheTitle = ""
            this.cacheTodo = {}
        },
        cleanTodo() {
            this.todos.splice(0, this.todos.length)
        },

        getIndex(id) {
            return this.todos.findIndex((el) => el.id == id)
        },
        changeComplated(id){
            let index = this.getIndex(id)
            this.todos[index].completed = !this.todos[index].completed
        }
    },
    computed: {
        filteredTodos() {
            let nowTab = this.visibility
            switch (nowTab) {
                case "all":
                    return this.todos.filter((item) => true)
                case "active":
                    return this.todos.filter((item) => !item.completed)
                case "completed":
                    return this.todos.filter((item) => item.completed)
            }
        },
        getNewKey() {
            return Math.max(...this.todos.map((el) => +el.id))
        },
        activeTodosLength() {
            return this.todos.filter((item) => !item.completed).length
        }
    }
})

程序码说明:

~ 待下篇继续罗!


个人 Blog: https://eudora.cc/


<<:  Day 28: gulp 是怎麽运作的

>>:  微聊 术业有专攻 Blame要找对人

【第6天】资料前处理-资料扩增

现况 辨识手写中文字时,若图档内中文字迹有部分缺失,或是油墨泄漏造成字迹脏污,可能导致模型辨识错误,...

Day20:状态参数判读

在 WebSocket 中,对於是否处於连线状态,或是连线有无成功,预设有提供参数供开发者判断,因此...

Day 14: Draft

GOOGLE公云使用案例 大纲 Introduction(Global view) How to c...

[Day25] - Using Redux with Web Component

使用组件跟组件的组合 , 形成一个页面 势必会遇到经典的组件传值 issue (下图左侧) 以大家常...

[DAY 01] 在台南想瘦是不可能的吧...那就一起胖吧=w=

这一次被公司派往盐水支援自动化专案 每天最期待的就是下班後要吃什麽 是要待在盐水吃意面 还是要去新营...