Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I save an Angular form to my ruby on rails backend?

I'm new to Angular. I've tried everything I know how and Google searches have surprisingly few tutorials on this particular question. Here's the last code I tried:

index.html

<form ng-submit="addArticle(articles)">
<input type="text" id="title" ng-model="newPost.title">
<input type="text" id="body" ng-model="newPost.body">
<input type="submit" value="Submit">
</form>

articles controller

app.controller('ArticlesCtrl', function($scope, Article) {
  $scope.articles   = Article.query();
  $scope.newPost     = Article.save();
});

articles service (rails backend)

app.factory('Article', function($resource) {
  return $resource('http://localhost:3000/articles');
});

I can retrieve data just fine. But I can't submit any new data to the rails backend. On page load, the rails server error is:

Started POST "/articles" for 127.0.0.1 at 2015-02-08 18:26:29 -0800
Processing by ArticlesController#create as HTML
Completed 400 Bad Request in 0ms

ActionController::ParameterMissing (param is missing or the value is empty: article):
  app/controllers/articles_controller.rb:57:in `article_params'
  app/controllers/articles_controller.rb:21:in `create'

Pressing the submit button does nothing at all. The form basically does not work and the page is looking for a submission as soon as it loads.

I understand what the error says, that it's not receiving the parameters from the form. What I don't understand is what that should look like in my controller and/or form.

What am I doing wrong and how do I fix this?

like image 231
Proto Avatar asked Feb 09 '15 02:02

Proto


1 Answers

Angular has a feature called services which acts as a model for the application. It's where I'm communicating with my Rails backend:

services/article.js

app.factory('Article', function($resource) {
  return $resource('http://localhost:3000/articles/:id', { id: '@id'},
  {
   'update': { method: 'PUT'}
  });
});

Even though the :id is specified on the end, it works just as well for going straight to the /articles path. The id will only be used where provided.

The rest of the work goes into the controller:

controllers/articles.js

app.controller('NewPostCtrl', function($scope, Article) {
  $scope.newPost  = new Article();

  $scope.save = function() {
    Article.save({ article: $scope.article }, function() {
      // Optional function. Clear html form, redirect or whatever.
    });
  };

});

Originally, I assumed that the save() function that's made available through $resources was somewhat automatic. It is, but I was using it wrong. The default save() function can take up to four parameters, but only appears to require the data being passed to the database. Here, it knows to send a POST request to my backend.

views/articles/index.html

<form name="form" ng-submit="save()">
    <input type="text" id="title" ng-model="article.title">
    <input type="text" id="body" ng-model="article.body">
    <input type="submit" value="Submit">
</form>

After getting the service setup properly, the rest was easy. In the controller, it's required to create a new instance of the resource (in this case, a new article). I created a new $scope variable that contains the function which invokes the save method I created in the service.

Keep in mind that the methods created in the service can be named whatever you want. The importance of them is the type of HTTP request being sent. This is especially true for any RESTful app, as the route for GET requests is the same as for POST requests.

Below is the first solution I found. Thanks again for the responses. They were helpful in my experiments to learn how this worked!

Original Solution: I finally fixed it, so I'll post my particular solution. However, I only went this route through lack of information how to execute this through an angular service. Ideally, a service would handle this kind of http request. Also note that when using $resource in services, it comes with a few functions one of which is save(). However, this also didn't work out for me.

  • Info on $http: https://docs.angularjs.org/api/ng/service/$http
  • Info on $resource: https://docs.angularjs.org/api/ngResource/service/$resource
  • Tutorial on Services and Factories (highly useful): http://viralpatel.net/blogs/angularjs-service-factory-tutorial/

articles.js controller

app.controller('FormCtrl', function($scope, $http) {
$scope.addPost = function() {
$scope.article = {
  'article': {
    'title'  : $scope.article.title,
    'body'   : $scope.article.body
  }
};

// Why can't I use Article.save() method from $resource?
    $http({
        method: 'POST',
        url: 'http://localhost:3000/articles',
    data: $scope.article
    });
};

});

Since Rails is the backend, sending a POST request to the /articles path invokes the #create method. This was a simpler solution for me to understand than what I was trying before.

To understand using services: the $resource gives you access to the save() function. However, I still haven't demystified how to use it in this scenario. I went with $http because it's function was clear.

Sean Hill has a recommendation which is the second time I've seen today. It may be helpful to anyone else wrestling with this issue. If I come across a solution which uses services, I'll update this.

Thank you all for your help.

like image 54
Proto Avatar answered Sep 20 '22 14:09

Proto