經過 Laravel 建立一個 Vue 單頁面應用(四)

文章轉發自專業的Laravel開發者社區,原始連接:learnku.com/laravel/t/3…php

咱們在第三部分中放棄構建真實的用戶端,而學習使用 Vue 路由獲取組件數據的新方式。如今咱們準備將注意力轉移到爲用戶建立 CRUD(增刪改查)的功能上 —— 本教程將聚焦在編輯已存在的用戶。css

在處理第一個表單時,咱們有機會了解如何定義動態 Vue 路由。咱們的路由的動態部分是與用戶數據記錄匹配的用戶 ID。對於編輯用戶,Vue 路由以下所示:前端

/users/:id/edit

複製代碼

這個路由的動態部分是 :id 參數,它將取決於用戶的 ID。咱們將使用數據庫中的 id 字段,但你也可使用 UUID 或者其餘的數據標識。vue

安裝

在處理 Vue 組件以前,咱們須要定一個新的 API 接口來獲取指定的用戶,而後再定義一個接口來處理更新。ios

打開 routes/api.php 路由文件,在獲取所有用戶的 index 路由下方添加下面的路由:laravel

Route::namespace('Api')->group(function () {
    Route::get('/users', 'UsersController@index');
    Route::get('/users/{user}', 'UsersController@show');
});

複製代碼

理由 Laravel 內置的路由模型綁定,控制器方法將很簡單明瞭。在 app/Http/Controllers/Api/UsersController.php 中添加下面的方法:數據庫

// app/Http/Controllers/Api/UsersController

public function show(User $user)
{
    return new UserResource($user);
}

複製代碼

/api/users/1 這樣請求一個用戶,將返回以下的 JSON編程

{
    "data": {
        "name": "Antonetta Zemlak",
        "email":"znikolaus@example.org"
    }
}

複製代碼

第三部分的 UserResource 須要包含 id 列,因此須要更新 app/Http/Resources/UserResource.php 添加 id 鍵。整個文件以下:axios

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\Resource;

class UserResource extends Resource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
        ];
    }
}

複製代碼

如今 /api/users 和 /api/users/{user} 路由都將返回 id 字段,經過這個,能夠在路由中區分用戶。後端

定義 UsersEdit 組件

定義了 show 以後,咱們接着定義 Vue 中的路由和相應的組件。新增相應的路由到 resources/js/app.js 中。下面是引入 UsersEdit 組件(還沒有建立)和整個路由實例的代碼片斷:

import UsersEdit from './views/UsersEdit';

// ...

const router = new VueRouter({
    mode: 'history',
    routes: [
        {
            path: '/',
            name: 'home',
            component: Home
        },
        {
            path: '/hello',
            name: 'hello',
            component: Hello,
        },
        {
            path: '/users',
            name: 'users.index',
            component: UsersIndex,
        },
        {
            path: '/users/:id/edit',
            name: 'users.edit',
            component: UsersEdit,
        },
    ],
});

複製代碼

咱們在 routes 末尾新增了 users.edit 路由。

下一步, 咱們須要在 resources/assets/js/views/UsersEdit.vue 中建立 UsersEdit 組件。代碼以下:

<template>
  <div>
      <form @submit.prevent="onSubmit($event)">
        <div class="form-group">
            <label for="user_name">Name</label>
            <input id="user_name" v-model="user.name" />
        </div>
        <div class="form-group">
            <label for="user_email">Email</label>
            <input id="user_email" type="email" v-model="user.email" />
        </div>
        <div class="form-group">
            <button type="submit">Update</button>
        </div>
    </form>
  </div>
</template>
<script>
export default {
  data() {
    return {
      user: {
        id: null,
        name: "",
        email: ""
      }
    };
  },
  methods: {
    onSubmit(event) {
        // @todo form submit event
    }
  },
  created() {
      // @todo load user details
  }
};
</script>

複製代碼

首先看下 template 部分:咱們在 div 中書寫 <form> ,由於稍後咱們只須要在加載了 user 數據後展現 <form>

<form> 有一個默認的 @submit 事件,咱們定義了一個 onSubmit() 方法去處理它。最後須要提一下在 <input> 元素上的 v-model 屬性,它和 data.users 對象一一對應。咱們爲 idname,和 email 設置了默認值。

如今你打開 /users/1/edit 應該看到一個空白的表單:

咱們準備編輯已經存在的用戶,因此下一步會說明怎麼獲取路由中動態的  :id ,在  UsersEdit.vue 中加載用戶數據。

使用專用的模塊獲取用戶詳情

在咱們在組件中加載用戶數據以前,咱們先定義一個額外的專用模塊去處理 /api/users 的資源,包括查詢全部用戶,查詢單個用戶和更新用戶。

第一步,建立一個新的文件夾來放置請求後端的模塊。你能夠用任意方式來建立這些文件。下面咱們展現了在 Nix 下的命令:

mkdir -p resources/assets/js/api/
touch resources/assets/js/api/users.js

複製代碼

users.js 模塊會暴露一些用來處理 /api/users 資源的方法。這個模塊會盡量的簡單,但以後你能夠在請求以前或者以後隨意處理數據。這個文件用做可複用的API操做的存儲庫:

import axios from 'axios';

export default {
    all() {
        return axios.get('/api/users');
    },
    find(id) {
        return axios.get(`/api/users/${id}`);
    },
    update(id, data) {
        return axios.put(`/api/users/${id}`, data);
    },
};

複製代碼

如今咱們可使用一樣的模塊去獲取全部用戶,查詢和更新單個用戶:

// 獲取全部用戶
client.all().then((data) => mapData);

// 查詢單個用戶
client.find(userId);

複製代碼

目前爲止,all() 方法不支持分頁,須要你本身去實現分頁,而後使用新的 all() 替換 UsersIndex.vue 組件中的方法。

在 UsersEdit 組件中加載用戶數據

如今咱們有了一個可複用但很簡陋的api客戶端,當編輯頁面生成以後咱們使用它來獲取用戶數據。

最初,咱們在組件中添加了 created() 方法,如今咱們能夠在它裏面獲取用戶的數據:

// UsersEdit.vue Component
<script>
import api from '../api/users';

export default {
  // ...
  created() {
      api.find(this.$route.params.id).then((response) => {
        this.loaded = true;
        this.user = response.data.data;
      });
  }
}
</script>

複製代碼

在 created() 中,users.js 客戶端使用 find() 方法返回了一個 Promise 對象。在 Promise 的回調中,咱們設置了 loaded 屬性(還沒有建立)並設置了the user 屬性。

如今往 data 中添加 loaded 屬性,默認值爲 false :

data() {
  return {
    loaded: false,
    user: {
      id: null,
      name: "",
      email: ""
    }
  };
},

複製代碼

因爲咱們的組件在 created() 中加載數據,因此在組件加載數據時顯示「加載」的提示消息:

<div v-if="! loaded">Loading...</div>
<form @submit.prevent="onSubmit($event)" v-else>
<!-- ... -->
</form>

複製代碼

刷新頁面,你會看到一個簡單的 Loading... 信息:

而後用戶數據會顯示在表單中:

API速度很快,若是你要肯定 loading 提示正常工做,你須要使用 setTimeout 去延遲設置 user 屬性:

api.find(this.$route.params.id).then((response) => {
    setTimeout(() => {
      this.loaded = true;
      this.user = response.data.data;
    }, 5000);
});

複製代碼

上面的代碼會顯示 loading 消息5秒,而後設置 loaded 和 user 屬性。

更新用戶

咱們將完成 onSubmit() 方法,並用過 PUT /api/users/{user} 更新用戶。

咱們先完善 onSubmit() ,以後會轉到後端處理數據庫的更新:

onSubmit(event) {
  this.saving = true;

  api.update(this.user.id, {
      name: this.user.name,
      email: this.user.email,
  }).then((response) => {
      this.message = 'User updated';
      setTimeout(() => this.message = null, 2000);
      this.user = response.data.data;
  }).catch(error => {
      console.log(error)
  }).then(_ => this.saving = false);
},

複製代碼

咱們經過用戶 ID 調用 api.update() 方法,傳入從綁定表單中獲取的 name 和 email 的值。

而後咱們在 Promise 上連接一個回調方法,在 API 成功執行以後設置成功提示信息,並設置最新的用戶數據。2000 毫秒後咱們置空提示信息,這一樣會隱藏模板中的消息。

目前爲止,咱們只是單純的抓取全部錯誤並輸出到控制檯。將來,咱們會回頭重寫錯誤(服務端錯誤或者驗證錯誤)處理,可是如今,咱們略過這一部分,專一在請求成功後的處理。

咱們經過 this.saving 來肯定咱們是否在更新用戶信息。咱們給模板添加了 :disabled 屬性,來避免重複提交,它能保證當咱們在更新數據時提交按鈕是禁止狀態:

<div class="form-group">
  <button type="submit" :disabled="saving">Update</button>
</div>

複製代碼

咱們在 catch 以後另外連接了一個 then(),一旦 API 請求結束,就將 this.saving 設爲 false 。咱們須要重置這個屬性爲 false ,來確保咱們能夠再次提交數據。咱們最後的 then() 使用了 _ 來表示這裏有一個變量,但咱們並不須要使用。你也能夠定義一個使用空括號的箭頭函數:

.then(() => this.saving = false);

複製代碼

咱們在 data() 中新添了了兩個屬性:

data() {
  return {
    message: null,
    loaded: false,
    saving: false,
    user: {
      id: null,
      name: "",
      email: ""
    }
  };
},

複製代碼

下一步,咱們來修改 <template> 來展現 message :

<template>
  <div>
      <div v-if="message" class="alert">{{ message }}</div>
      <div v-if="! loaded">Loading...</div>
      <form @submit.prevent="onSubmit($event)" v-else>
        <div class="form-group">
            <label for="user_name">Name</label>
            <input id="user_name" v-model="user.name" />
        </div>
        <div class="form-group">
            <label for="user_email">Email</label>
            <input id="user_email" type="email" v-model="user.email" />
        </div>
        <div class="form-group">
            <button type="submit" :disabled="saving">Update</button>
        </div>
    </form>
  </div>
</template>

複製代碼

最後,咱們在 UsersEdit.vue 文件底部爲 alert 信息添加一些樣式:

<style lang="scss" scoped>
$red: lighten(red, 30%);
$darkRed: darken($red, 50%);
.form-group label {
  display: block;
}
.alert {
    background: $red;
    color: $darkRed;
    padding: 1rem;
    margin-bottom: 1rem;
    width: 50%;
    border: 1px solid $darkRed;
    border-radius: 5px;
}
</style>

複製代碼

如今,前端組件已經修改完畢,它能夠處理表單的提交,並能在 API 成功執行以後同步更新模板。如今咱們須要轉到後端來完成剩下的部分。

在 API 後端更新用戶

咱們準備在 User 資源控制器上定義一個 update 方法來鏈接全部部分。咱們在服務端進行數據驗證。但咱們暫時不會和前端對接。

第一步,咱們在 routes/api.php 中定義新的路由 PUT /api/users/{user}

Route::namespace('Api')->group(function () {
    Route::get('/users', 'UsersController@index');
    Route::get('/users/{user}', 'UsersController@show');
    Route::put('/users/{user}', 'UsersController@update');
});

複製代碼

下一步,UsersController@update 方法會使用 request 對象來驗證數據,並返回咱們要更新的數據。把下面的方法添加到 app/Http/Controllers/Api/UsersController.php 中:

public function update(User $user, Request $request)
{
    $data = $request->validate([
        'name' => 'required',
        'email' => 'required|email',
    ]);

    $user->update($data);

    return new UserResource($user);
}

複製代碼

像 show() 方法那樣,咱們使用隱式的模型綁定從數據庫中加載用戶數據。數據驗證後,更新用戶模型,並新建一個 UserResource ,返回更新過的模型。

成功的面向後端的請求會返回更新過的用戶的數據(JSON格式),而後咱們用它更新 Vue 組件中的  this.user 屬性。

{
  "data": {
    "id": 1,
    "name":"Miguel Boyle",
    "email":"hirthe.joel@example.org"
  }
}

複製代碼

導航到編輯頁面

咱們一直在直接請求 /users/:id/edit 頁面, 可是,咱們沒有在界面的任何地方添加路由。 在看到我是如何作到這一點以前,不妨本身嘗試找出如何動態地導航到編輯頁面。

這是我在 第二部分 建立了 UsersIndex.vue 模板,併爲 /users 索引頁上列出了每一個用戶添加編輯連接的方式:

<ul v-if="users">
    <li v-for="{ id, name, email } in users">
        <strong>Name:</strong> {{ name }},
        <strong>Email:</strong> {{ email }} |
        <router-link :to="{ name: 'users.edit', params: { id } }">Edit</router-link>
    </li>
</ul>

複製代碼

咱們從新構造循環中的  user 對象,以提供 idname 和 email 屬性。咱們使用  <router-link/> 組件來引入咱們的命名的  users.edit 路由,使用 params 傳遞  id 參數。

爲了更好地可視化 <router-link> 屬性,面是咱們以前添加的 app.js 文件中的路由定義:

{
  path: '/users/:id/edit',
  name: 'users.edit',
  component: UsersEdit,
},

複製代碼

若是您刷新應用程序或訪問  /users 端點,您將看到以下內容:

將他們放在一塊兒

若是你如今想編輯一個用戶,在後臺須要保存它並返回一個200的狀態碼來表示響應成功。 在 PUT 成功請求以後你應該會在兩秒鐘內看到如下內容:

你能夠在下面看到完整的UsersEdit.vue組件內容:

<template>
  <div>
      <div v-if="message" class="alert">{{ message }}</div>
      <div v-if="! loaded">Loading...</div>
      <form @submit.prevent="onSubmit($event)" v-else>
        <div class="form-group">
            <label for="user_name">Name</label>
            <input id="user_name" v-model="user.name" />
        </div>
        <div class="form-group">
            <label for="user_email">Email</label>
            <input id="user_email" type="email" v-model="user.email" />
        </div>
        <div class="form-group">
            <button type="submit" :disabled="saving">Update</button>
        </div>
    </form>
  </div>
</template>
<script>
import api from '../api/users';

export default {
  data() {
    return {
      message: null,
      loaded: false,
      saving: false,
      user: {
        id: null,
        name: "",
        email: ""
      }
    };
  },
  methods: {
    onSubmit(event) {
        this.saving = true;

        api.update(this.user.id, {
            name: this.user.name,
            email: this.user.email,
        }).then((response) => {
            this.message = 'User updated';
            setTimeout(() => this.message = null, 10000);
            this.user = response.data.data;
        }).catch(error => {
            console.log(error)
        }).then(_ => this.saving = false);
    }
  },
  created() {
      api.find(this.$route.params.id).then((response) => {
          setTimeout(() => {
            this.loaded = true;
            this.user = response.data.data;
          }, 5000);
      });
  }
};
</script>
<style lang="scss" scoped>
$red: lighten(red, 30%);
$darkRed: darken($red, 50%);
.form-group label {
  display: block;
}
.alert {
    background: $red;
    color: $darkRed;
    padding: 1rem;
    margin-bottom: 1rem;
    width: 50%;
    border: 1px solid $darkRed;
    border-radius: 5px;
}
</style>

複製代碼

做業

用戶信息更新成功後,咱們僅僅只是在兩秒鐘後重置該消息。 修改成已設置消息,並將用戶重定向回先前的位置(如, /users 頁)。

其次,在表單底部添加一個 返回取消 按鈕,以放棄更新,並導航回上一頁。

若是您認爲不嚴謹,請在 UsersEdit 組件向 API 發送無效請求時顯示驗證錯誤。 成功提交表單後,清除錯誤消息。

下一步是什麼

隨着用戶的更新,咱們將注意力轉移到刪除用戶上。刪除用戶將有助於演示成功刪除後以編程方式進行導航。如今,咱們將定義一個全局404頁面,由於咱們具備用於編輯用戶的動態路由。

若是您準備好了,請繼續第五部分.

相關文章
相關標籤/搜索