学习 Vue.js:与 Laravel 结合持久化 Todo 数据(实现篇)

上一节 做了一些准备工作,这一节来完成 Todo 项目。

引入 bootstrap.css

项目中用到了 Bootstrap 的样式文件,在 index.html 文件中引入之。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>lara-vue</title>
    <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

使用 axios.js

本项目中前端发送 Ajax 请求使用 axios.js,为了能在 vue 项目中更好的集成它,也使用 vue-axios

$ npm install --save axios vue-axios

修改项目入口文件 src/main.js,引入 axiosVueAxios

import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
import App from './App'
import router from './router'

Vue.use(VueAxios, axios)

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  template: '<App/>',
  components: { App }
})

创建组件

src/components 文件夹中新建两个组件 Todos.vueTodo.vue,前者是为了显示 Todo 列表,后者是为了显示 Todo 详情。本项目中,详情页只是为了说明 vue-router 的使用,没有提供比在首页中更多的信息。

vue-router 来说,这两个组件就是两个页面。

设定路由

src/router/index.js 中设定路由。

import Vue from 'vue'
import Router from 'vue-router'
import Todos from '@/components/Todos'
import Todo from '@/components/Todo'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Todos',
      component: Todos
    },
    {
      path: '/todo/:id',
      name: 'Todo',
      component: Todo
    }
  ]
})

这里设定了两个路由 //todo/xx,对应两个组件,所以说在 vue 中,组件就是页面。

name 是路由的名字, 给 <router-link> 标签使用,用于链接到指定路由。

写组件

接下来写 Todos.vueTodo.vue 组件的内容,

Todo.vue

<template>
  <div class="todo">
    <div v-if="loading" class="loading">
      Loading...
    </div>

    <div v-if="error" class="error">
      {{ error }}
    </div>

    <div v-if="todo" class="content">
      <nav>
        <router-link :to="{ name: 'Todos'}">←返回</router-link>
      </nav>
      <h2>{{ todo.title }}</h2>
    </div>
  </div>
</template>

<script>
  export default {
    data () {
      return {
        loading: false,
        todo: null,
        error: null
      }
    },
    created () {
      // 组件创建完后获取数据,
      // 此时 data 已经被 observed 了
      this.fetchData()
    },
    watch: {
      // 如果路由有变化,会再次执行该方法
      '$route': 'fetchData'
    },
    methods: {
      fetchData () {
        this.error = this.todo = null
        this.loading = true

        this.axios.get('http://localhost:8000/api/todo/' + this.$route.params.id)
          .then((response) => {
            this.loading = false
            this.todo = response.data
          })
          .catch((error) => {
            this.error = error.toString()
          });
      }
    }
  }
</script>

<style scoped>
  .todo {
    text-align: center;
  }
</style

举个例子:当地址栏请求 http://localhost:8080/#/todo/4 地址时,触发 fetchData 方法, axios 发出 get 请求,this.$route.params.id(对应路由定义中的 :id) 拿到 4,取得数据后显示。

<div v-if="todo" class="content">
    <nav>
        <router-link :to="{ name: 'Todos'}">←返回</router-link>
    </nav>
    <h2>{{ todo.title }}</h2>
</div>

<router-link> 标签本质是一个超链接,链接到名叫 Todos 的路由,我们已经知道,这是 Todo 列表页。

Todos.vue

<template>
  <div class="container">
    <div class="row">
      <div class="col-md-6 col-md-offset-3">
        <div class="panel panel-default">
          <div class="panel-heading text-center">
              计划要做的事情,共 {{ todos.length }} 件
          </div>
          <div class="panel-body">
            <div class="list-groups">
              <a class="list-group-item" v-bind:class="{ 'completed': todo.completed }" v-for="(todo, index) in todos">
                <router-link :to="{ name: 'Todo', params: { id: todo.id }}">{{ todo.title }}</router-link>
                <button class="btn btn-xs btn-danger pull-right" v-on:click='destroy(index, todo)'title="删除">✘</button>
                <button class="btn btn-xs pull-right" v-on:click='toggleDone(index, todo)' v-bind:class="[todo.completed ? 'btn-success': '']" v-bind:title="[todo.completed ? '点击,标记为未完成': '点击,标记为已完成']">✔</button>
              </a>
            </div>
          </div>
          <div class="panel-footer">
            <form v-on:submit.prevent="add">
              <div class="form-group">
                <input type="text" class="form-control text-center" v-model="newTodo.title">
              </div>
              <button class="btn btn-default btn-block" type="submit">添加</button>
            </form>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'todos',
  props: ['todos'],
  data () {
    return {
      newTodo: { title: ''}
    }
  },
  methods: {
    add() {
      if (! this.newTodo.title.trim()) { return ; }
      this.axios.post('http://localhost:8000/api/todos/create', { 'title': this.newTodo.title })
          .then((response) => {
              this.todos.push(response.data)
                            this.newTodo.title = ''
          });
    },
    destroy(index, todo) {
      this.axios.delete('http://localhost:8000/api/todo/' + todo.id + '/delete')
          .then((response) => {
              this.todos.splice(index, 1)
          });
    },
    toggleDone(index, todo) {
      this.axios.patch('http://localhost:8000/api/todo/' + todo.id + '/toggleComplete')
          .then((response) => {
              this.todos[index].completed = !this.todos[index].completed
          });
    }
  }
}
</script>

<style scoped>
.btn-xs+.btn-xs {
  margin-right: .5rem;
}

a.list-group-item.completed {
  background-color: #f5f5f5;
  text-decoration: line-through;
}
</style>
本作品采用《CC 协议》,转载必须注明作者和本文链接
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!