Reputation: 275
New to AngularJS. I have some trouble getting the RESTful thing working between angular and rails. I setup a simple rest service and get the post fine. Binding work etc. But when I $update back .. what gets sent to server is the entire post object. I need to be able to filter this down to just certain attributes. Additionally what gets posted is not wrapped inside of params[:post]
which is the typical rails approach.
See below:
# angular
app.factory "Post", ["$resource", ($resource) ->
$resource "/posts_api/:id", {id: "@id"}, {update: {method: "PUT"}}
]
app.controller "PageEditCtrl", ($scope, Post) ->
$scope.post = Post.get(
id: 32723
, ->
$scope.post.title = $scope.post.title + "!"
$scope.post.$update({id: $scope.post.id})
)
......
# in rails Post.rb class
attr_accessible :title, :body, :user_id
# in rails posts_api_controller.rb
class PostsApiController < ApplicationController
respond_to :json
before_filter :authenticate_user!
# **** HERE'S THE PROBLEM:::: 2 issues with updating
# 1) angular is passing *entire post object* with attributes that are *not in attr_accesible*
# 2) angular is not passing the post in the typical rails fashion params[:post] ..instead just as params
def update
respond_with current_user.posts.update(params[:id], params) # would like to have params[:post] instead here
end
end
Upvotes: 2
Views: 1689
Reputation: 13939
Or there is the excellent rails-resource-angular gem that already implements the RESTful architecture, and uses promises. Furthermore, the post request will be wrapped correctly so you can use params[:post].
angular.module('book.services', ['rails']);
angular.module('book.services').factory('Book',
['railsResourceFactory', function (railsResourceFactory) {
return railsResourceFactory({
url: '/books',
name: 'book'
});
}]);
Book.query({ title: title }).then(function (results) {
$scope.books = results;
$scope.searching = false;
}, function (error) {
// do something about the error
$scope.searching = false;
});
Book.get(1234).then(function (book) {
new Book({title: "Awesomeness", author: 123}).create()
.then(function(book){
$scope.book = book // Rails generated an ID for us, and everything else we put in our model
})
About the attr_accessible stuff, you might want to have a look at this post even if you're not using rails 4.
Because, if you want to filter before sending the post request, somewhat you'll have to write a javascript version of the params.require().permit(). Or maybe I didn't understand the question very well...?
Upvotes: 0
Reputation: 872
For issue #2, here's how I have my AngularJS app set up:
railsResources =
index:
method: "GET"
isArray: true
show:
method: "GET"
new:
params:
verb: "new"
method: "GET"
create:
method: "POST"
edit:
params:
verb: "edit"
method: "GET"
update:
method: "PUT"
destroy:
params:
command: "remove"
method: "DELETE"
app.factory "Post", ["$resource", ($resource) ->
$resource "/posts_api/:id/:verb", {id: "@id"}, railsResources
]
That should allow you to communicate to Rails as you'd expect.
I think I remember seeing your example in the Angular docs and thinking it was a contrived example. I think you should separate your functions. And you don't need to pass in the id when you're using a resource generated object:
$scope.post = Post.get(id: 32723)
$scope.addBang = ->
$scope.post.title = $scope.post.title + "!"
$scope.post.$update()
For issue #1, think in terms of a Rails form. If you want to avoid passing in restricted params, you just leave them out of the form. However, that's easier said than done if you want to use Angular's built in model/resource interface.
So I'd recommend creating a Rails Model method that accepts the full params[:post] hash and returns a hash of only accessible methods.
By doing this you can keep it DRY and avoid having to adjust your JS if your model changes.
Edit: Or, better yet, as I just learned, use the Rails Model to generate the hash before you deliver it to AngularJS.
like so:
post = Post.find(123)
post = { name: post.title, content: post.content }
Upvotes: 1