2

I'm a newbie in AngularJS and I've faced an issue when I try to make a POST request with AngularJS and it POSTs no parameters with it. I use Sinatra as a RESTful interface.

That's how my Sinatra backend looks:

post '/layer/:layer_id' do
  @layer = PageLayer.where(id: params[:layer_id]).first
  @layer.content = params[:content]
  @layer.save
end

If try to POST with Postman chrome extension - it works! Sinatra saves the content properly. So I'm sure that the backend works as it should.

That's how my angular test code looks:

TestCtrl = ($scope, $routeParams, $http, $resource) ->
    $scope.layer = []

    Layer = $resource('/layer/:id', {id:'@id'})

$scope.layer = Layer.get {id: $routeParams.layerId}, ->
    console.log "Got you!"

$scope.saveContent = ->
    $scope.layer.$save()
    console.log "Saved!"

angular.module('appDirectives', []).directive "test", ->
    return (scope, element, attrs) ->
        element.bind "blur", ->
            console.log("blur!")
            scope.saveContent()

And HTML-code:

<div>Content: {{layer.content}}</div>

<div>
    <form>
        <input type="text" test ng-model="layer.content">
    </form>
</div>

So, the only question is: What's wrong? Why I can make correct request with Postman but not with angularJS? Angular returns empty "content" so Sinatra saves it as "" every time.

I've also attached a structure of a layer:

g {id: 27245, page_id: 2302, external_id: 26518, original_upload: null…}
content: "dfgdfg"
external_id: 26518
id: 27245
layerNumber: 8
page_id: 2302

How can I log what exactly angular POSTs?

artem_golovin
  • 154
  • 2
  • 15
  • Can you try `$save` without parameters.Since you already have the object. – Chandermani Aug 14 '13 at 10:02
  • tried. It sends request to /layer/ instead of /layer/:layerId so Sinatra gives us a error. But I tried to slightly change the controller so it posts with empty save() and to the right address. But it still POSTs empty request... How can I log what exactly angular POSTs? – artem_golovin Aug 14 '13 at 10:03
  • See the object `$scope.layer` and see if it has property `layer_id` and it is set to correct value inside debugger. And also verify there are other properties available on layer and try the method $save without parameters. – Chandermani Aug 14 '13 at 10:10
  • I changed the controller in a way that is saves with empty save(). Like that: http://jsfiddle.net/mwGs3/ But still no result... I've checked in the logs that it posts to the right address. – artem_golovin Aug 14 '13 at 10:14
  • Can you put the structure of the layer object in the question. The layer object should have a property layer_id? – Chandermani Aug 14 '13 at 10:20
  • I've put it. And I slightly changed the controller so it takes layer id – artem_golovin Aug 14 '13 at 10:25
  • Not sure what the issue is. You can also try the save method available on `Layer` resource and pass in the full object. – Chandermani Aug 14 '13 at 10:42

3 Answers3

4

Hey this is the exact problem I was having, and the answer now seems so obvious. I knew Angular was sending json, but no matter what I tried it wasn't working. This led me in the right direction, but as for parsing json I had to write

ng_params = JSON.parse(request.body.read)

I had to change 'string' to 'read'. Maybe I have a newer version of the json gem or something. My full save process is like this:

post '/api/v1/test' do
  ng_params = JSON.parse(request.body.read)
  @foo = Foo.new(ng_params)
  if @foo.save
    puts "Page Saved"
    content_type :json
    rabl :foos, format: "json"
  end
end

I use rabl to format the json to have control over what json data Sinatra sends back (no emails or passwords please)

My Angular code is just this (have not yet implemented put, patch or delete, nor auto update of data just yet. You still have to refresh the page to see the new post.) And to be clear, I have a table named 'foos', where the ActiveRecord model is 'Foo', and one column named 'anything' (other than timestamps and id, which I make sure are always there).

// app declaration
var app = angular.module("App", ['ngResource']);

// data service
app.factory('Foo', ['$resource', function($resource) {
  return $resource('/api/v1/test/:id', {id: '@id'});
}]);

// the controller
app.controller('Controller', function($scope, Foo) {

  $scope.foos = Foo.query();

  $scope.create = function(anything) {
    Foo.save({anything: anything}, function(foo){
      $scope.foos.push(foo);
    });
  };
});

Then in my markup the form looks like this, where the important thing is the call to 'create' with 'anything' as the argument in 'ng-submit'. If you have more than one column in your table you call 'create' with more than one argument ex. 'create(anything, bar)'.

<h3>Add something new</h3>
<form ng-submit="create(anything)">
  <input ng-model="anything" type="text">
  <button type="submit">Do it</button>
</form>

While displaying the data is

<li ng-repeat="foo in foos">
  <p>{{foo.anything}}</p>
</li>
user462238
  • 43
  • 5
  • Yes, I also use body.read in all the new requests. Also there is a nice way to update_attributes if you symbolize keys. Like that: `@received_app = JSON.parse(request.body.read).symbolize_keys; @app.update_attributes! @received_app.permit(@allowed_attrs)` – artem_golovin Nov 01 '13 at 10:59
  • cool, thanks. I'm new to angular and this whole sending json back and forth, so that helps. – user462238 Nov 04 '13 at 01:39
2

This link solved the problem. Just add

gem 'rack-parser'

to your Gemfile and add this code to config.ru. Will work like a charm.

require 'rack/parser'
use Rack::Parser, content_types: {
  'application/json' => Proc.new {|body| JSON.parse body }
}
David Vass
  • 135
  • 7
1

All right, I've solved the issue. Somehow Sinatra was not properly getting POSTs from Angular and was not automatically putting them into params. So if we parse the request manually - it works. Like that:

post '/layer/:layer_id' do
  @updated_layer = JSON.parse(request.body.string)
  @layer = PageLayer.where(id: params[:layer_id]).first
  @layer.content = @updated_layer['content']
  @layer.save 
end 
artem_golovin
  • 154
  • 2
  • 15