通过 Vue 组件、Vue Router、Vuex 和 Laravel 实现表单提交
在这篇教程中,我们来为咖啡店添加一些数据。我们构建这个应用的目的是为了帮助咖啡爱好者找到下一杯咖啡,所以首先需要让认证用户可以提交咖啡店到应用,不论何时在单页面应用中处理表单,都需要让 Laravel API 和调用 JavaScript 路由并更新 Vuex 储存数据的 Vuex 模块协同工作。听起来需要做的事情很多,但如果我们将它们拆解开来各个击破,也没有那么复杂。
第一步:回顾我们现在有什么
我们在 Laravel API 中定义了一个路由来处理新增咖啡店,该路由需要一下参数:
- 名称
- 地址
- 城市
- 省份
- 邮编
这些只是一个咖啡店的基本参数,后面还会添加更多。
我们还有一个 Vue Router 路由 /cafes/new
用来在前端处理新增咖啡店以及一个用于新增咖啡店的模板文件:/resources/assets/js/pages/NewCafe.vue
。
我们已经做好了以上准备工作,并且在 /resources/assets/js/api/cafe.js
文件中还有一个接收上述 5 个参数来提交一个咖啡店的 JavaScript API 调用:postAddNewCafe
,但是还有一些工作要做:
- 添加一个提交新咖啡店的表单到
NewCafe.vue
文件 - 发送请求动作到
Cafes
模块以便提交新咖啡店 - 通过 JavaScript API 提交新咖啡店到 Laravel API
- 将处理结果返回给前端,尤其是 Vuex 模块
- 重新加载咖啡店并更新 Vuex 模块
以上就是基于 API + Vue 驱动的单页面应用添加内容到数据库的基本流程,接下来我们就按照这个基本流程一步一步来操作。
第二步:添加表单到 NewCafe.vue 页面
首先打开 /resources/assets/js/pages/NewCafe.vue
文件,现在这个页面空的,我们将新增咖啡店表单插入到这个模板中:
<template>
<div class="page">
<form>
<div class="grid-container">
<div class="grid-x grid-padding-x">
<div class="large-12 medium-12 small-12 cell">
<label>Name
<input type="text" placeholder="咖啡店名">
</label>
</div>
<div class="large-12 medium-12 small-12 cell">
<label>Address
<input type="text" placeholder="地址">
</label>
</div>
<div class="large-12 medium-12 small-12 cell">
<label>City
<input type="text" placeholder="城市">
</label>
</div>
<div class="large-12 medium-12 small-12 cell">
<label>State
<input type="text" placeholder="省份">
</label>
</div>
<div class="large-12 medium-12 small-12 cell">
<label>Zip
<input type="text" placeholder="邮编">
</label>
</div>
</div>
</div>
</form>
</div>
</template>
注意:
- 我们使用的是 Zurb Foundation 6.4 XY-Grid 栅格系统进行布局,其中某些风格已经预定义好了,如果你想了解更多关于这个栅格布局的内容,可以查看官方文档。
- 我为
<div>
容器中新增了class
属性page
,对应的样式定义位于/resources/assets/sass/layouts/_page.scss
。
接下来需要在页面中设置模型,在页面组件中添加一个 data()
函数并以对象方式返回模型数据:
export default {
data() {
return {
name: '',
address: '',
city: '',
state: '',
zip: ''
}
}
}
现在我们需要通过 v-model
将模型数据和表单输入框绑定起来,Vue 会帮助我们同步模型数据和表单元素,并且这个同步是双向的,关于 v-model
指令可以参考 Vue 官方文档了解更多明细:
<div class="page">
<form>
<div class="grid-container">
<div class="grid-x grid-padding-x">
<div class="large-12 medium-12 small-12 cell">
<label>Name
<input type="text" placeholder="咖啡店名" v-model="name">
</label>
</div>
<div class="large-12 medium-12 small-12 cell">
<label>Address
<input type="text" placeholder="地址" v-model="address">
</label>
</div>
<div class="large-12 medium-12 small-12 cell">
<label>City
<input type="text" placeholder="城市" v-model="city">
</label>
</div>
<div class="large-12 medium-12 small-12 cell">
<label>State
<input type="text" placeholder="省份" v-model="state">
</label>
</div>
<div class="large-12 medium-12 small-12 cell">
<label>Zip
<input type="text" placeholder="邮编" v-model="zip">
</label>
</div>
</div>
</div>
</form>
</div>
至此,我们的表单组件已经构建完成了,它具备在模型数据和表单元素之间双向同步数据的功能。
运行 npm run dev
重新构建项目后就可以通过 http://roast.test/#/cafes/new
访问新增咖啡店页面了:
如果你打开开发者工具中 Vue 查看器,在表单中输入数据就可以看到数据会同步到数据模型中:
最后,我们为这个表单添加一个提交按钮,并为表单提交创建一个点击处理方法 submitNewCafe
,完整代码如下:
<style>
</style>
<template>
<div class="page">
<form>
<div class="grid-container">
<div class="grid-x grid-padding-x">
<div class="large-12 medium-12 small-12 cell">
<label>名称
<input type="text" placeholder="咖啡店名" v-model="name">
</label>
</div>
<div class="large-12 medium-12 small-12 cell">
<label>地址
<input type="text" placeholder="地址" v-model="address">
</label>
</div>
<div class="large-12 medium-12 small-12 cell">
<label>城市
<input type="text" placeholder="城市" v-model="city">
</label>
</div>
<div class="large-12 medium-12 small-12 cell">
<label>省份
<input type="text" placeholder="省份" v-model="state">
</label>
</div>
<div class="large-12 medium-12 small-12 cell">
<label>邮编
<input type="text" placeholder="邮编" v-model="zip">
</label>
</div>
<div class="large-12 medium-12 small-12 cell">
<a class="button" v-on:click="submitNewCafe()">提交</a>
</div>
</div>
</div>
</form>
</div>
</template>
<script>
export default {
data() {
return {
name: '',
address: '',
city: '',
state: '',
zip: ''
}
},
methods: {
submitNewCafe: function () {
}
}
}
</script>
这样每次用户点击「提交」按钮,都会调用 submitNewCafe
方法来处理新增咖啡店操作(目前留空)。
第三步:提交新增的咖啡店数据
我们在上一步已经定义好了表单数据模型和视图,现在我们来编写 submitNewCafe
方法提交收集的表单数据。
由于我们是在 Vuex 模块上调用处理动作,所以需要在 Vuex 上分发一个 addCafe
动作方法(下一步实现该方法),同时以 JSON 对象方式将表单数据传递给该 addCafe
方法:
submitNewCafe: function () {
this.$store.dispatch('addCafe', {
name: this.name,
address: this.address,
city: this.city,
state: this.state,
zip: this.zip
});
}
第四步:在 Vuex 模块中处理新增操作
很显然,我们紧接着要做的就是实现上一步中要分发的 addCafe
方法,打开 /resources/assets/js/modules/cafes.js
编辑 Vuex 模块 cafes
。
首先,需要添加一个变量 cafeAddStatus
到 state
以便跟踪咖啡店添加状态:
state: {
cafes: [],
cafesLoadStatus: 0,
cafe: {},
cafeLoadStatus: 0,
cafeAddStatus: 0
},
然后,将 addCafe
方法添加到 actions
中,紧随 loadCafe
之后:
addCafe( { commit, state, dispatch }, data ){
}
需要注意的是 addCafe
的方法签名,第一个参数中包含了 dispatch
,我们可以通过它从 Vuex 模块中分发动作,比如新增咖啡店后重新加载 Cafes 模块,第二个参数 data
就是我们在上一步传递过来的表单数据了。
我们已经在 通过 Axios 库构建 API 请求 这篇教程中定义好了前端 API 请求(位于 resources/assets/js/api/cafe.js
),其中包含新增咖啡店请求,在 addCafe
方法中,我们将添加如下代码调用之前定义好的 API 发送请求:
addCafe( { commit, state, dispatch }, data ){
// 状态1表示开始添加
commit( 'setCafeAddStatus', 1 );
CafeAPI.postAddNewCafe( data.name, data.address, data.city, data.state, data.zip )
.then( function( response ){
// 状态2表示添加成功
commit( 'setCafeAddStatus', 2 );
dispatch( 'loadCafes' );
})
.catch( function(){
// 状态3表示添加失败
commit( 'setCafeAddStatus', 3 );
});
}
最后记得在 mutations
中新增 setCafeAddStatus
方法:
setCafeAddStatus(state, status) {
state.cafeAddStatus = status;
}
在 getters
中新增 getCafeAddStatus
方法:
getCafeAddStatus( state) {
return state.cafeAddStatus;
}
至此,前端工作已经全部完成了。
第五步:回顾 Laravel 的处理并构建应用
相应的 Laravel 后端 API 也已经在 实现 Laravel 后端 API 接口 这篇教程中定义好了:POST /api/v1/cafes
。所以我们现在要做的就是通过 npm run dev
重新构建前端资源文件并通过 http://roast.test/#/cafes/new
页面新增一个咖啡店:
点击「提交」按钮,添加成功后就可以在数据库看到新增的数据了:
访问首页 http://roast.test/#/home
,在 Vue 组件和 Vuex 模块中也都可以看到刚刚新增的咖啡店了:
至此,我们已经成功在 Roast 应用中通过提交表单完成新增咖啡店的功能。
36 Comments
我也是500,请问你解决了吗?
学院君你好,想请教一下500要怎么解决? [POST http://roast.test/api/v1/cafes 500 (Internal Server Error)] 我在网上搜说大部分情况是没带csrf token,我在network里检查了之后是带上了。路由名字,变量名也都检查了。在axois里打印错误,返回的结果也没有什么其他信息。 Error: Request failed with status code 500 at createError (app.js:11957) at settle (app.js:41252) at XMLHttpRequest.handleLoad (app.js:11831)
我知道了。因为在后台创建新cafe接口中你依赖注入Request为$request然后直接$request->name这样获取前端传过来的参数即可。用版主这个接收参数的方法就会报错
我在network里面找到报错了 Request::get() should not be called statically" 。查询文档以后 https://laravel.com/docs/5.8/requests 修改了接收方法。然后还需要在数据库中给latitude和longitude一个默认值,我设置初始值未Null,然后通过了
把数据表cafes中,除主键id 以外的其他字段都设置为 允许为空 试试
学院君,我做到这篇的第2步,可是NewCafe.vue里的内容显示不出来
自问自答系列:检查路由!检查路由!检查路由!
我也是,觉的自己特别白痴
500 是因为有两个问题 1.学院君CafesController 获取数据有问题。Request 使用LARAVEL 的依赖注入。$request->all()获取所有数据 直接create就成了。2.那个经纬度不能为空,设置为空就成
Access to XMLHttpRequest at 'http://sap.com/api/v1/cafes' from origin 'http://spa.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
这个问题怎么解决?