はじめに
『Rails 4で作るドラッグアンドドロップで表示順を変更できるサンプルアプリ(スクリーンキャスト付き)』という記事,読まれたことがある方も多いと思います.
RailsでAjax(via jQuery)を使ったデータの更新,ドラッグアンドドロップの実現,その他UXを向上させるためのいろいろな技などが詰まった最高にありがたい記事です.拝むしかない.
お世話になった方も多いのではないでしょうか.
本稿はjQuery & jQuery UIで実現されていた機能を,AngularJSを用いて実現してみるという内容になってます.
(ざんねんながらスクリーンキャストはありません.眠すぎて撮れませんでした.)
元ネタと大きく異なるところを列挙していくようなものになると思います.
ソースコード
GitHubにおいてます.ごかくにんください.
izumin5210/sortable-table-sandbox
デモ
Herokuにあります.並び替えぐらいしか出来ないようになってます.
https://sortable-table-sandbox-angular.herokuapp.com/
フロントエンドのパッケージについて
元は普通のgemで管理されていましたが,Rails Assetsに変えています.完全に趣味です.
JS/CSSのライブラリってbowerで管理したくなるやん?
(Rails Assetsについては『bowerパッケージをbundlerで管理するRails Assetsを使ってみた』を参考にするといいと思いますです)
また,AngularJSをメインに据えるにあたってjQueryを完全に駆逐してやろうと考えていたのですが…
今回のメインであるui-sortableがjQuery及びjQuery UIに依存してました.ざんねん.
(無理やりjQuery非依存に出来ないかな…)
gem 'jquery-rails'
gem 'jquery-ui-rails'
gem 'bootstrap-sass'
source 'https://rails-assets.org'
gem 'rails-assets-bootstrap-sass-official'
gem 'rails-assets-angularjs'
gem 'rails-assets-angular-resource'
gem 'rails-assets-angular-ui-sortable'
これらに伴い,application.{css.scss,js}
もちょっとずつ変わってます.
CSS
bootstrap-sass-official
に入ってるscss
をimportする方式に変更しています.これも趣味です,
bootstrapのvariablesとか弄り倒してカスタマイズするときはこっちのが捗る.
/*
*= require angularjs/angular-csp
*= require_self
*/
$icon-font-path: "bootstrap-sass-official/";
@import "bootstrap-sass-official/bootstrap-sprockets";
@import "bootstrap-sass-official/bootstrap";
@import 'table-sortable';
body { padding-top: 70px; }
-
_xxx.scss
を読んでほしくなかったからrequire_tree .
は外した -
_table-sortable.scss
はテーブルにスタイル当ててるだけ -
body
のスタイルはbootstrapのnavbar-fixed-top
対策 -
angularjs/angular-csp
はng-cloak
とかをいい感じにしてくれるやつ(AngularJS付属品)
JavaScript
ここは割りと普通.
//= require jquery
//= require jquery-ui
//= require angularjs
//= require angular-resource
//= require angular-ui-sortable
//= require sortable_table_sandbox
//= require_tree .
sortable_table_sandbox.js.coffee
はAngularJSのモジュール定義するやつ
app = angular.module 'SortableTableSandbox', ['ngResource', 'ui.sortable']
app.config ['$httpProvider', ($httpProvider) ->
csrfToken = angular.element(document.querySelectorAll('meta[name=csrf-token]')).attr('content')
$httpProvider.defaults.headers.common['X-CSRF-Token'] = csrfToken
]
CSRF Tokenまわりの設定とかはここでやっちゃうのが楽.
ちょっとごちゃついてるのはjQueryへの依存をなくそうとした涙ぐましい痕跡.
fruits#index をAngularJSで描画
View
angular-ui-sortable
を利用するということで,やっぱりng-repeat
でtable描いたほうが早いかなーという考え方.
ここでFruitsCtrl
を指定する(controller as記法が便利).
また,ui-sortableのオプション(ui-sortable='ctrl.sortableOptions'
)とどのオブジェクトを並び替え対象にするか(ng-model="ctrl.fruits"
)も書く.
h1 Listing fruits
table.table.table-hover.table-sortable[ng-controller="FruitsCtrl as ctrl"]
thead
tr
th Name
th
tbody[ui-sortable='ctrl.sortableOptions' ng-model="ctrl.fruits"]
tr.item[ng-repeat="fruit in ctrl.fruits"]
td
| {{fruit.name}}
td: a[ng-href="/fruits/{{fruit.id}}"] Show
FruitsCtrl
FruitsCtrl
の実装はこんな感じ.
通信周りとかはservice(model)にぶん投げてる(後述).
Viewとの対応は以下のとおり.
-
ctrl.fruits
=>@fruits
(JSだとthis.fruits
) -
ctrl.sortableOptions
=>sortableOptions
(FruitsCtrl
のぷろぱてぃ).
Fruit
のオブジェクトのrow_order_position
をセットした後,Fruit#$sort
を実行するだけで更新される.
もうajaxメソッドのオプションいっぱい書かなくても済むんだ…!
class FruitsCtrl
constructor: (@$scope, @Fruit) ->
@fruits = @Fruit.query()
sortableOptions:
stop: (e, ui) ->
fruit = ui.item.scope().fruit
fruit.row_order_position = ui.item.index()
fruit.$sort()
app = angular.module 'SortableTableSandbox'
app.controller 'FruitsCtrl', [
'$scope'
'Fruit'
FruitsCtrl
]
Fruit
model部分は$resourceサービス(ngResource
)を利用してます.
$resource
のaction
オプションでPATCH
メソッドなsort
アクションを定義.
これを注入したらいい感じにfruit用のResourceクラスが取ってこれる(JS的に正しい表現なのかは置いといて…).
Fruit = ($resource) ->
url = '/fruits/:id.json'
paramsDefaults = id: '@id'
actions =
sort:
url: '/fruits/:id/sort.json'
method: 'PATCH'
$resource(url, paramsDefaults, actions)
app = angular.module 'SortableTableSandbox'
app.factory 'Fruit', [
'$resource'
Fruit
]
まとめ
jQuery版と比較すると,個人的にはとにかくViewがスッキリするなーと感じます.
ただ,JS(Coffee)自体の記述量はぱっと見だと多い気がしちゃうので敷居は高く感じるかも.
ただ実際はそんなことはなく,このあとはfruits_ctrl.js.coffee
やfruit.js.coffee
にいろいろ追加していくようになるので,長い目で見ると(?)こっちのが楽になるかも.
jQueryよりは処理があっちこっち散らばったりしづらい…のかな?
エンジニアの技量次第か…
なおjquery-ui.effect-highlight
については面倒だったので作ってないです.
余裕あるときにでも自分で実装してみようかな.
謝辞
なんといっても元ネタとなる素晴らしい記事を書いてくださった@jnchitoさん.
これ以外の記事だったり書籍(Everyday Rails - RSpecによるRailsテスト入門)だったりコミュニティ(西脇.rb & 神戸.rb)だったりで数え切れないぐらいお世話になってます.
本当にありがとうございます(宣伝してるわけではない).
参考文献
- Rails 4で作るドラッグアンドドロップで表示順を変更できるサンプルアプリ(スクリーンキャスト付き)
- bowerパッケージをbundlerで管理するRails Assetsを使ってみた
- AngularJSリファレンス
使ったライブラリとか
元ネタと大きく違うところ