Re: [問題] AngularJs 和 rails

作者: ducksteven (鴨七‧林新)   2014-04-17 22:04:33
我最近在做 Rails + Angular CRUD ,
我是 Rails 寫很久最近開始寫 Angular,
我想我可以分享一些經驗。
首先你要把 Angular CRUD 和 Rails CRUD 分開來看,
Angular 的資料不管怎麼改,都是存在前端的 JavaScript 的記憶體裡面,
沒有後送到 Server 的話 Server 都不知道。
Angular 做為一個前端 framework 是不需要管後端怎麼做的,
在拿資料的時候是用 JSON 拿沒錯,但存回 server 的時候還是得用 Ajax 打回去。
具體的做法是這樣:
// in controller
$http.post("/articles", { title: "New Article 1", content: "..." })
.then(function(response) { /* 存好了 */ });
打開 Chrome 的 Inspector 的 Network ,你會看到有個 request 過去。
那麼,既然都用 Ajax 存資料到後端了,取得資料當然也用 Ajax 做:
$http.get("/articles/1")
.then(function(response) {
$scope.article = response;
}
後端出 JSON 用 respond_with 你應該不陌生。
到這裡為止,你可以參考 Angular 官方的 Tutorial "PhoneCat"
http://docs.angularjs.org/tutorial/step_00
它會一步一步教到用 $resource 來跟後端 RESTful Server 存取資料。
不過呢,預設的 Angular $http 是沒辦法接上 Rails 的
原因有二,一個是 $http 少送一個 header 叫 X-Requested-With=XMLHttpRequest
大部份的 Ajax Library 都有送,就它沒送,所以它認不出 POST by Ajax。
所以要去偷改 $httpProvider:
app.config([
'$httpProvider',
function($httpProvider) {
$httpProvider.defaults.headers
.common["X-Requested-With"] = "XMLHttpRequest";
}
]);
這樣 Rails 就能正確解出 POST by Ajax 了。
第二個是 CSRF Token 的傳法
Rails 一般的表單做法是藏在 Form 裡面有個 hidden input,
Rails 傳統的 Ajax 用 UJS 會自動讀 meta 裡面的 CSRF Token
但 Angular 是用 Header 傳,並且取第一次連線時的 cookie['XSRF-TOKEN'],
於是你要 hack 一下 controller 讓它送出這個 cookie
並且讓 Rails 去認 Angular 傳來的 Token in Header
詳細:http://stackoverflow.com/a/15761835
最後是關於 $resource 的一些 caveats
$resource 跟 restangular 我都用過,
最後是覺得後者的 DSL 太難記,前者就單純是 CRUD ,比較好學。
首先是 Rails 的 "Update" 通常是 PATCH 或 PUT,
所以官方文件裡面的範例的 article.$save 在 Rails 不太實用(它是送 POST)
在 $resource 裡面要這樣定義(其實文件裡面也有提到):
var Article = $resource("/articles/:id", {
// default options
id: "@id" // 表示自動取用 Article class 的 instance 的 "id" property
}, {
// custom actions
update: {
method: "PUT" // 定義 article.$update 送 PUT request
}
});
至於存的時候是這樣存:
// 先更新 JavaScript 物件的資料
$scope.article.title = "New Title"; // 或是 ng-model 綁到 input
$scope.article.content = "New Content";
// 戳 "update" button 的時候
$scope.article.$update
.then(function(article) {
// Rails 傳回 show.json
// 然後一個一個欄位更新
// 不要直接 $scope.article = article,不然會變成不是 $resource 的物件
for (var property in article) {
if (article.hasOwnProperty(property)) {
$scope.article[property] = article[property];
}
}, function(response) {
// 儲存失敗,Rails 直接 render :json => { :errors => @article.errors }
$scope.errors = response.data.errors;
// 然後你在 view 裡面用 ng-if 去 render errors
});
如果是 nested 的場合是這樣:
var Comment = $resource("/articles/:articleId/comments/:id", {
id: "@id",
articleId: $scope.article.id
}, {
// 略
});
也配合 ng-route 或 Angular UI-Router 可以從 #/ 裡面取得 article id 。
custom member action 的做法是這樣(以「把某噗靜音」為例):
var Plurk = $resource("/plurks/:id/:action", {
id: "@id",
action: "" // 預設把 action 填空字串
}, {
mute: {
method: "POST",
params: {
action: "mute" // 把 action 填入 "mute"
}
}
});
這樣子 plurk.$mute() 就會送出 POST /plurks/123/mute 了。
但 $resource 如果要對 collection 做 custom action 會很難處理,
因為 $resource 不像 Rails 的 routes.rb 可以定義 collection 和 member action,
而且話說回來 Rails 的 router 處理 collection action 本來就是跟 id 多載,
所以我看到 Stack Overflow 上面有人的做法是這樣:
// 以「把所有的噗都設為已讀」(MAAR) 為例
var Plurk = $resource("/plurks/:id:collectionAction/:memberAction", {
id: "@id",
collectionAction: "",
memberAction: ""
}, {
maar: {
method: "POST",
params: {
id: "",
collectionAction: "maar"
}
}
});
這樣很醜,但似乎很多人都是這樣寫的,不過我實際上沒寫過就是了。
※ 引述《lisaweng (piglet)》之銘言:
: 最近在學AngularJs 不知道各位大大有沒有學過
: 後端是用rails寫的
: 面臨到的問題是
: 一開始view那邊我都是用angularJs從rails拿data
: 把.JSON的資料存到變數
: 然後再顯示在網頁上
: 可是發現這樣好像就沒怎麼到two way data binding
: 所以請問這樣我在做的時候也是要在AngularJS建資料嗎
: 例如說假設我現在在做一個部落格
: 我現在的做法是
: 每當我在點"Create Article" 我的文章就會存在rails那邊
: 然後當我要顯示那個文章的時候。我必須去讀那個文章的.JSON
: 可是這樣好像不太效率
: 是不是我應該要改成我每一次點Create Article的時候
: 要把資料同時存在rails和js呢?
: 然後讀的時候可以直接從js讀?
: 謝謝!
作者: sdlong (sdlong)   2014-04-18 03:19:00
Xdite: Rails 跟 AngularJS 是無法整合的,別白費力氣 XD
作者: turtleknight (turtle)   2014-04-18 10:38:00
真多學問...還好我可以用websocket...
作者: LetDogDay (推動世界讓狗日~~)   2014-04-19 20:07:00
為什麼不能整合?
作者: Rplus (R+) (9527)   2014-04-20 01:53:00
作者: a83294 (馬岱)   2014-04-21 15:28:00
ember會不會比較好
作者: lisaweng (piglet)   2014-04-29 21:38:00
一直忘記來推文 謝謝你噢 :D

Links booklink

Contact Us: admin [ a t ] ucptt.com