12. 面向对象的任务列表实现 (1)
后台
初始化
创建一个新应用:
$ laravel new vue-to-do
$ cd vue-to-do
创建 Task 相关:
$ php artisan make:model Task -mc
Model created successfully.
Created Migration: 2017_05_01_050911_create_tasks_table
Controller created successfully.
配置数据库:
// /.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=vue_todo
DB_USERNAME=root
DB_PASSWORD=
编辑迁移文件:
/database/migrations/2017_05_01_050911_create_tasks_table.php
Schema::create('tasks', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->text('description');
$table->timestamps();
});
执行迁移:
$ php artisan migrate
业务逻辑
路由:
/routes/web.php
Route::get('/tasks','TaskController@index');
Route::post('/tasks','TaskController@store');
控制器:
/app/Http/Controllers/TaskController.php
<?php
namespace App\Http\Controllers;
use App\Task;
use Illuminate\Http\Request;
class TaskController extends Controller
{
/**
* 显示与创建任务列表
* @return [Response] [view]
*/
public function index()
{
$tasks = Task::all();
return view('tasks.index', compact('tasks'));
}
/**
* 保存任务列表
* @return [Response] [message]
*/
public function store()
{
$this->validate(request(), [
'name' => 'required',
'description' => 'required'
]);
Task::forceCreate(request('name', 'description'));
return ['message' => '任务创建成功'];
}
}
前端
基本功能实现
首先定义视图:
/resources/views/tasks/index.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue-To-Do-List</title>
</head>
<body>
<div id="app" class="container">
@if (count($tasks))
<table class="table">
<thead>
<tr>
<th>#</th>
<th>任务名</th>
<th>描述</th>
</tr>
</thead>
<tbody>
@foreach ($tasks as $task)
<tr>
<th>{{ $task['id']}}</th>
<td>{{ $task['name'] }}</td>
<td>{{ $task['description'] }}</td>
</tr>
@endforeach
</tbody>
</table>
@endif
<form method="POST" action="/tasks" @submit.prevent="onSubmit">
<fieldset class="form-group">
<label for="name">任务名</label>
<input type="text" class="form-control" id="name" name="name" v-model="name">
</fieldset>
<fieldset class="form-group">
<label for="description">任务描述</label>
<input type="text" class="form-control" id="description" name="description" v-model="description">
</fieldset>
<button type="submit" class="btn btn-primary">创建</button>
</form>
</div>
<link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/vue/2.2.6/vue.js"></script>
<script src="https://cdn.bootcss.com/axios/0.16.1/axios.js"></script>
<script type="text/javascript" src="/js/app.js"></script>
</body>
</html>
为了阻止表单的提交,可以使用 prevent
修饰符,相当于调用 Event.preventDefault()
方法。
前端业务逻辑:
/public/js/app.js
Vue.prototype.$http = axios;
var vm = new Vue({
el: '#app',
data: {
name:'',
description:''
},
methods: {
onSubmit(){
this.$http.post('/tasks',this.$data)
}
}
})
这里使用了之前介绍的 axios
包,其中,$data
为实例的 data 对象。在这里相当于:
this.$http.post('/tasks',{
name: this.name,
description: this.description
})
基本的效果如下:
Errors 类
之前只实现了正确提交的功能。接下来实现错误提交的情况。
axios
的 post
请求的基本方法如下:
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
// 成功
.then(function (response) {
console.log(response);
})
// 失败
.catch(function (error) {
console.log(error);
});
假如提交了一个空的表单,后台会返回错误消息,我们可以通过 error.response.data
来访问。比如
data: {
name:'',
description:'',
errors:{}
},
methods: {
onSubmit(){
this.$http.post('/tasks',this.$data)
.catch(error => this.errors = error.response.data);
}
}
为了方便操作,可以将其封装成类:
Vue.prototype.$http = axios;
class Errors {
/**
* 初始化
*/
constructor(){
this.errors = {}
}
/**
* 保存错误信息
*/
record(errors){
this.errors = errors;
}
}
var vm = new Vue({
el: '#app',
data: {
name:'',
description:'',
errors:new Errors()
},
methods: {
onSubmit(){
this.$http.post('/tasks',this.$data)
.catch(error => this.errors.record = error.response.data);
}
}
})
在控制台中查看对应的 errors
实例:
也就是说,可以通过 errors[name][0]
的方式来访问错误信息,将其写成方法:
get(field) {
if (this.errors[field]) {
return this.errors[field][0];
}
}
页面中就可以显示错误消息了:
<fieldset class="form-group">
<label for="name">任务名</label>
<input type="text" class="form-control" id="name" v-model="name">
<small class="text-danger" v-text="errors.get('name')"></small>
</fieldset>
<fieldset class="form-group">
<label for="description">任务描述</label>
<input type="text" class="form-control" id="description" v-model="description">
<small class="text-danger " v-text="errors.get('description')"></small>
</fieldset>
效果如下:
接着,增加条件判断,以决定是否显示错误信息的页面:
<small class="text-danger" v-if="errors.has('name')" v-text="errors.get('name')"></small>
<small class="text-danger" v-if="errors.has('description')" v-text="errors.get('description')"></small>
对应的方法:
/**
* 判断属性是否存在
* @param {string} field
*/
has(field){
return this.errors.hasOwnProperty(field);
}
当存在错误消息的时候,不允许用户再次提交:
<button type="submit" class="btn btn-primary" :disabled="errors.any()">创建</button>
对应的方法:
any(){
return Object.keys(this.errors).length > 0;
}
最后,用户重新输入时,应当清空错误消息:
<form method="POST" action="/tasks" @submit.prevent="onSubmit" @keydown="errors.clear($event.target.name)">
对应方法:
clear(field){
if (field) {
delete this.errors[field];
return;
}
this.errors = {};
}
附录:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。