ITプロパートナーズの新卒採用サービス「intee」のエンジニアの五藤です。
intee.jp
inteeの技術スタックは、バックエンドがPHP (CakePHP3)、フロントエンドがVue.jsを採用しています。
ユーザーが見る画面はもちろんの事、弊社スタッフが利用する管理画面に関しても、新しく実装する画面に関してはほとんどVueを使って管理画面を構築しています。
「何で管理画面のフロントエンドをリッチにする必要が?」
って思われるかもしれませんが、管理オペレーションが複雑になってくると、
一度に複数のユーザーに対して操作したり、複数のレコードを追加する際にいちいち画面遷移したくない
複雑なリレーションのデータ登録を、モーダルなどを用いて効率的にやりたい
などなど、オペレーション最適化の面で、リッチなフロントエンドを提供するメリットは意外と多く、営業サイドから 「この画面をVueを使ってサクサク使えるようにしてほしい」 という要望を受けることもしばしばだったります。
そんな中で、UI/コンポーネント ライブラリとして愛用しているのが、Element です!
Element - The world's most popular Vue UI framework
各種フォーム部品やモーダルやアラート、簡単なグラフ表示などの、リッチな画面を作るのに必要なコンポーネント が揃っているニクいやつです。しかも、Vueのコンポーネント として実装されているため、
< el-tag type = "success" > Elementに用意されてるめっちゃきれいなタグUI</ el-tag>
という感じに、HTMLライクな書き方で簡単にコンポーネント を配置できるのも嬉しいところです。
今回は、その中でも、管理画面の画面設計の中核を占める <el-table> の使い方や、実装上のポイントについてまとめます!
( element-ui公式 より引用 )
簡単に言うと、管理画面でよくある、「データ一覧をテーブル形式で表示する」事ができるコンポーネント です。
これだけ書くと凄くシンプルなので、elementのtableのすごい所をガンガン紹介します!
el-tableを使うと捗るポイント
データ投入が簡単で、いい感じに表示してくれる
データ設定の方法は簡単で、配列形式のデータをVueのdata属性に格納して、<el-table> のプロパティとして指定するだけ。
(データ側)
data: {
tableData: [{
date: '2016-05-03' ,
name: 'Tom' ,
address: 'No. 189, Grove St, Los Angeles'
} , {
date: '2016-05-02' ,
name: 'Tom' ,
address: 'No. 189, Grove St, Los Angeles'
} , {
date: '2016-05-04' ,
name: 'Tom' ,
address: 'No. 189, Grove St, Los Angeles'
} , {
date: '2016-05-01' ,
name: 'Tom' ,
address: 'No. 189, Grove St, Los Angeles'
}]
}
(コンポーネント )
< el-table : data = "tableData" >
これだけで、tableDataプロパティに格納した配列データを、<el-table>内で使用することが出来ますし、el-dataの値を切り替えれば、リアルタイムでテーブル上の値も更新されます。
また、dataに格納する値は、
< el-table : data = "tableData" >
< el-table -column
prop= "date"
label = "Date"
width = "180" >
</ el-table -column>
< el-table -column
prop= "name"
label = "Name"
width = "180" >
</ el-table -column>
< el-table >
という感じで、table側で自由に各カラムを抽出、表示できます。 dataに設定された全カラムが勝手に出力されるのではなく、コンポーネント 側で出したい情報を自由にピックできる のがポイントで、実際の想定シーンとしては、
dataプロパティには、サーバーサイド側の参照API をまるっと格納させ、<el-table>側で、必要なデータを必要な形でレイアウトする
というやり方で、 汎用的なAjax の戻り値を、そのまま<el-table>にぶちこめる のが実装上は凄く楽だったりします。
各カラムの表示内容が、タグベースで柔軟に設定できる
各カラムに表示する値は、HTML形式で動的に設定可能で、更に言うと、他のelementのコンポーネント を配置することも出来ます。
[いろんなタグを配置してみるとこんな感じ]
いろんなタグを配置してみた
こんな感じで、画像やアイコン、タグ、ボタンなどを各カラムにポチポチ配置して、可読性の高い画面をデザイナー抜きで簡単に作ることが出来ちゃいます。
もちろんボタンに導線を作ることもできるので、
ボタンを押すとチェックリストにチェックが付く
ボタンを押すとモーダルを表示してデータ編集ができる
といった処理を組むことも簡単です。
ちなみにモーダルもelementで用意されています。
http://element.eleme.io/#/en-US/component/dialog
いたれりつくせりですね。
・・・どうですか?管理画面に<el-table>使いたくなりませんか?
実際に使う上でのtips
<el-table>の素晴らしさがわかったところで、実際に作っていく上でのポイントをいくつか書いていきます。
基本的なデータフロー
前項でも述べましたが、<el-table>に格納するデータは、Ajax の戻り値をそのまま格納するのがわかりやすいので、 「データを取得する共通処理」を一つの共通メソッドにまとめておき、表示・更新したいタイミングで呼び出す のが基本的な実装パターンになります。以下はデータ読み込み処理のコード例です。
getModels:function (){
var that = this
this .isLoading = true
$.ajax({
url:this .apiUrl,
type:'GET' ,
data:this .condition
} ).done(function (data){
that.tableData = data.models
that.isLoading = false
} )
} ,
いくつかポイントを上げると、
API を呼び出すタイミングでローディング用のプロパティをONにし、読み込み後にOFFにすることでローディングを実装
ローディングステータスは<el-table v-loading="loading"> という感じで、v-loadingディレクティブにbindすることで、プロパティと同期できます。
検索クエリとページネーションはプロパティにまとめておくと、そのまま渡せる(検索ボックス側でthis.conditionを編集する作りにすると、検索側と読み込み側が疎結合 になるので扱いやすい
という感じです。
データ表示の基本2タイプ
表示カラムは<el-table-column> を使うといいましたが、2つの記法があります。
el-table-column::prop で簡単カラム指定
< el-table -column prop= "full_name" label = "氏名" >
</ el-table -column>
propプロパティに直接Objectのキーを指定すると、文字列としてデータが表示されます。
template slot-scope="scope" で細かく指定
もう少しリッチに各カラムのデータを書きたい場合は、このように書きます。
< el-table -column label = "タグ一覧" >
< template slot- scope = "scope" >
< el-tag v- for = "tag in scope.row.tags" :key= "tag.id" type = "success" > {{tag.name}}< el-tag>
</ temlate>
</ el-table -column>
ポイントとしては
<template slot-scope="scope">
で囲んだ中に、そのカラムで表示したいHTMLを配置
scope.row.[カラム名 ] で各レコードの情報が参照できる
です。このやり方を使うと、カラムの中で自由にレコード情報を表示ことができるため、
先程あげたような
画像を貼り付けたり
カラム内で改行して複数の情報を表示したり
アクションボタンを配置したり
とかなり応用が効くようになります。
テーブルの行番号を取得したい。
行番号は scope.$index で取得できます。
< el-table -column width = "50" fixed>
< template slot- scope = "scope" label = "No" >
{{ scope.$index}}
</ template >
</ el-table -column>
発展パターンとして、ページネーションを実装している場合のレコード番号は、
< el-table -column width = "50" fixed>
< template slot- scope = "scope" label = "No" >
{{ getRecordSequence(scope.$index)}}
</ template >
</ el-table -column>
というふうに外部メソッドにscope.$index
を投げて、
getRecordSequence(index){
if (!this .paginate.page || !this .paginate.perPage){
return index + 1
}
return (this .paginate.page - 1) * this .paginate.perPage + index + 1
} ,
こんな感じで、ページネーションの値と組み合わせて算出したりもします。
ページネーション
<el-table>
はページングのための特別な仕組みを持たないため、
dataプロパティに「ページ情報」と「総ページ数」を用意しておき、API 更新のタイミングで同期する
画面側のページング表示、操作は<el-paginate>
コンポーネント を別途配置して、こちらもAPI 更新と紐付ける
というやり方で実装します。
データ構造
data:{
condition:{
page: 1,
} ,
pageCount: 1,
} ,
methods: {
getModels:function (){
var that = this
$.ajax({
url:this .apiUrl,
type:'GET' ,
data:this .condition
} ).done(function (data){
that.tableData = data.models
that.isLoading = false
that.pageCount = data.paginate.pageCount
that.paginate = data.paginate
} )
} ,
}
ページネーション部品
< el-pagination
@current-change = "getModels"
:current-page.sync= "condition.page"
:page-count= "pageCount" >
</ el-pagination>
共通化 のパターン
いろいろな画面で<el-table>を使うようになってくると、必要な処理を共通化 したい!と思う人もいるかも知れません。結論からいうと、共通化 はcomponentよりもmixinで行うのが個人的にはおすすめです。
mixin側で、
API との通信処理
取得したレコード一覧や検索条件、ページネーションを保持するためのdataオブジェクト
を共通化 して持っておくと、このパターンのテーブル表示を量産する上では役に立ちます。
こんな感じでしょうか。
var dataTableMixin = {
data:{
getUrl: '/get' ,
models: [] ,
condition:{
page: 1,
} ,
pageCount: 1,
isLoading: false
} ,
mounted:function (){
this .getModels()
} ,
methods:{
changeCurrentPage: function (page){
this .condition.page = page
this .getModels()
} ,
getModels:function (){
var that = this
this .isLoading = true
$.ajax({
url:this .getUrl,
type:'GET' ,
data:this .condition
} ).done(function (data){
that.models = data.models
that.isLoading = false
that.pageCount = data.paginate.pageCount
that.paginate = data.paginate
that.afterGet()
} )
} ,
afterGet:function (){
return
} ,
}
}
いかがでしたでしょうか?ぜひ管理画面もリッチな <el-table> をご活用ください!