Upload
-
View
663
Download
0
Embed Size (px)
DESCRIPTION
サーバサイドで動的にhtml生成していたりjQueryをガッツし使っている既存プロジェクトにAngularJSを部分的につっこんでみた @jsCafe21
Citation preview
2014/07/27 @jsCafe21
サーバサイドで動的にhtml生成していたりjQueryをガッツし使っている既存プロジェクトにAngularJSを部分的につっこんでみた
名前:佐藤俊輔
twitter : @ushisantoasobu
所属 : 株式会社ジークレスト
経歴 : 主にフロントエンドエンジニア
担当 : 現在はポケットランド(スマホweb)というアバターサービス
自己紹介
その前の発表がVue.jsで、そこでおそらくAngularがディスられるので省略
Angularとは
「AngularってSPAつくるためのものでしょ?」
自分の勝手な思い込み
- Rebuild 27- mozaic.fm #3 (こちらはAngularのみを70分くらい)
「ハイブリッド」もありのようだ
- step1 : データバインディングで効率化- step2 : 共通モジュールをディレクティブ化- step3 : 中規模程度の新機能をSPAっぽく
担当プロジェクトでのAngular導入ステップ
- step1 : データバインディングで効率化 ✓
- step2 : 共通モジュールをディレクティブ化 ✓
- step3 : 中規模程度の新機能をSPAっぽく
担当プロジェクトでのAngular導入ステップ
データバインディング
<p>{{message}}</p>
$scope.message = “hoge”;
.html
.js
使っているところ = xhrで表示更新するところ(初期表示のhtmlについてはJavaのvelocityで生成している)
- ゲームのポイントやランキング- アイテム所持数- 報酬アイテムなどなど...結構ある
- 同じデータを表示する箇所が複数あっても安心- データのオブジェクトさえつっこめばいいので楽
(もうjQueryではやりたくない)
特にいいと思ったところ
同じデータを複数のDOMで表示することが多々ある
<div class=”point_total”>×{{targetRewardPoint}}</div>
...
<div class=”point_total”>×{{targetRewardPoint}}</div>
...
<div class=”result_point_total”>×{{targetRewardPoint}}</div>
.html
<div class=”point_total”>×{{targetRewardPoint}}</div>
...
<div class=”point_total”>×{{targetRewardPoint}}</div>
...
<div class=”result_point_total”>×{{targetRewardPoint}}</div>
$scope.targetRewardPoint = data.point;
.html
.js
もちろんこれだけでOK
{" "bannerDataList":null," "boxLevelBonusAchieveLevel":1," "boxLevelBonusList":[]," "completeFlg":false," "completePoint":0," "countRewardDataList":null," "currentPoint":327," "currentRank":27910," "deliveryCount":0," "deliveryCountRewardDataList":null," "orderPoint":0," "pointRewardDataList":[" " {" " " "categoryName":"アクセサリ/手系"," " " "count":1," " " "dataItemFlg":false," " " "imageUrl":"shop/clothes/acceh/acceh_10824494_shop.png"," " " "itemId":10824494," " " "itemName":"ふしぎな実"," " " "itemRare":0," " " "rewardPoint":300" " }" ]," "restRewardCount":55," "resultCd":0," "token":null}
あるAPIでこのようなjsonが返ってくるとする
{" "bannerDataList":null," "boxLevelBonusAchieveLevel":1," "boxLevelBonusList":[]," "completeFlg":false," "completePoint":0," "countRewardDataList":null," "currentPoint":327," "currentRank":27910," "deliveryCount":0," "deliveryCountRewardDataList":null," "orderPoint":0," "pointRewardDataList":[" " {" " " "categoryName":"アクセサリ/手系"," " " "count":1," " " "dataItemFlg":false," " " "imageUrl":"shop/clothes/acceh/acceh_10824494_shop.png"," " " "itemId":10824494," " " "itemName":"ふしぎな実"," " " "itemRare":0," " " "rewardPoint":300" " }" ]," "restRewardCount":55," "resultCd":0," "token":null}
枠部分(報酬アイテム情報)を表示したい
<div>" <div>" <img src="http://img.atgames.jp/sp/update/2014/04/icon_point.png">" " <span id=”reward_span_reward_point”></span>達成
" </div>" <div><img id=”reward_img_reward_url”></div>" <div id=”reward_div_reward_itemname”></div>" <div id=”reward_div_reward_itemcount”></div></div>
var reward = data.pointRewardData[0];jQuery(“#reward_span_reward_point”).html(reward.rewardPoint);jQuery(“#reward_img_reward_url”).attr('src', reward.imageUrl);jQuery(“#reward_div_reward_itemname”).html(“<em>” + reward.categoryName +
“</em><br>” + reward.itemName);jQuery(“#reward_div_reward_itemcount”).html(“×” + reward.count);
.html
.js
これまで自分がjQueryで書いてた方法(もっとキレイに書ける?汗)
AngularJSで書いた方法。ロジック側の記述が楽
<div>" <div>" " <img src="http://img.atgames.jp/sp/update/2014/04/icon_point.png">" " <span>{{pointRewardData.rewardPoint}}</span>達成
" </div>" <div><img src="http://img.atgames.jp/{{pointRewardData.imageUrl}}"></div>" <div><em>{{pointRewardData.categoryName}}</em><br>{{pointRewardData.itemName}}</div>" <div ng-show="pointRewardData.count > 1">×{{pointRewardData.count}}</div></div>
$scope.pointRewardDataList = pointRewardDataList;
.html
.js
負の遺産もつくってしまった、、、恥ずかしいけど書く
ページ表示時にサーバサイドで動的に値を埋め込んでいる、でもその後xhrでも値を更新したい、といった箇所
<!-- 初期表示にvelocityで値を埋め込みたい -->
<p>$!{rank}</p>
<!-- でもangularでも書きたい(どんどん更新していくので) -->
<p>{{rank}}</p>
.html
.js
$scope.rank = 0;
どうする?
<p>{{rank}}</p> <script>var setupAngular = function(){" var _scope = angular.element(ngCtrl).scope();" _scope.$apply(function(){" " _scope.rank = $!{rank};" });};</script>
.html
.js
$scope.rank = 0; angular.element(document).ready(function() {" if(setupAngular && typeof setupAngular === "function"){" " setupAngular(); //本体htmlのグローバルメソッドにアクセス" }});
html側のスクリプトにvelocityで吐き出される値を書き出して、、、みたいなことをやってる。しかも不要にグローバル変数をつくってしまってる
- 簡単にいえば「カスタムタグ」or「カスタム属性」
ディレクティブ
サービス内の複数のページで共通で使用している機能をディレクティブ化する
ディレクティブ化する前
イベント毎にソースをコピペして、値の一部を変えることで対応していた(惰性)
<canvas id=” canvas_100522”></canvas>
<script>(function(){" var self = {}, canvas, stage, exportRoot," POS_X = 0, POS_Y = 0, SCALE = 0.55; //※ここ調整する"" self.init = function() {" " canvas = document.getElementById("canvas_100522");" " df100522_images = df100522_images||{}; " " var len = df100522.properties.manifest.length;" " for(var i = 0; i < len; i++){" " " var url = df100522.properties.manifest[i].src;" " " url = window.IMAGE_DOMAIN + "/high/roomfurniture/deco/" + url;" " " df100522.properties.manifest[i].src = url;" " } " " var loader = new createjs.LoadQueue(false);" " loader.addEventListener("fileload", self.handleFileLoad);" " loader.addEventListener("complete", self.handleComplete);" " loader.loadManifest(df100522.properties.manifest);" } " self.handleFileLoad = function(evt) { "" if (evt.item.type == "image") { df100522_images[evt.item.id] = evt.result; }" } " self.handleComplete = function() { "" //ここは省略" } " window.selfytown.df100522_shop = self;}());</script>
.html
ディレクティブ化する
.directive('df', function() {" return {" " restrict: 'E'," " transclude: true," " scope: {" " " furnitureid: '@'," " " category: '@'," " " posx: '@'," " " posy: '@'," " " scale: '@'" " }," " template:" " " '<canvas id="js_canvas_dynamic_furniture_{{furnitureid}}"></canvas>'," " link:function($scope, $element){ " " " var canvas," " " " len," " " " url," " " " loader," " " " exportRoot," " " " stage;" " "" " " canvas = $element[0]; " " " len = window["df" + $scope.furnitureid].properties.manifest.length;" " " for(i = 0; i < len; i++){" " " " url = window["df" + $scope.furnitureid].properties.manifest[i].src;" " " " url = window.IMAGE_DOMAIN + "/high/roomfurniture/" + $scope.category + "/" + url;" " " " window["df" + $scope.furnitureid].properties.manifest[i].src = url;" " " } " " " //CreateJSまわりの処理は省略" " }," " replace: true" };});
.js
ディレクティブ化した後
<df furnitureid=”100522” category=”deco” posX=”0” posY=”0” scale=”0.55”></df>
.html
以後これだけでOK(でもやっぱ黒魔術的な匂いがする)
- ディレクティブの使いどころについては @konpyuさんのスライドがわかりやすかった http://www.slideshare.net/KonYuichi/0601-angular-note
- mozaic.fm #3 でもゲストでお話されています!
- 中規模程度の機能をSPAっぽくつくりたい
SPAっぽくつくりたい
イベントページのタブボタン押下時の画面の切り替えを今はページ読み込みになっているけどAPIだけ返してもらっての表示の切り替えにしたい
- アプリっぽい挙動にしたい = サクサク感
やりたいこと
- フロントまわりでの処理が多くなるので 機種やOSによっては逆に描画が遅くなるかも?- htmlをキャッシュさせることでSPAを実現するけど htmlの更新の仕組みをうまくつくらないと- 既存のワークフロー(webコーダー、サーバサイド) から見直さないといけない
懸念事項
- jQueryも使える(angular.element)- 動的に表示を更新してく箇所がたくさんあるときは jQueryではもう書けない、 でも必ずしもAngularでなくてもよい?
まとめや補足
- 改めて振り返ると、、初期学習コストは高いかも- html自体がテンプレートになっているので webコーダーもみやすい- でも急に見知らぬカスタムタグつくるとビックリ されて迷惑がられる
まとめや補足
- Angularっぽくないこのスライドの色合いは、 当初はW杯直前に発表する予定だったため ブラジルカラーっぽくしてるだけ
まとめや補足
ご清聴ありがとうございます!