学习 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
,引入 axios
和 VueAxios
。
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.vue
和 Todo.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.vue
和 Todo.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 协议》,转载必须注明作者和本文链接