Building web apps with Rails4 and AngularJS in 15 minutes

While learning AngularJS to make a single page app using Rails4, I found some good videos and blogs. However, I did not find any simple example for CRUD operations that made me easily understand the integration between Rails4 and AngularJS. So in this tutorial post, I explain how to create basic CRUD operation using Rails4 and AngularJS.

Here is my git repository for the complete code Github

Create rails project


$ rails new rails4_crud_with_angularjs

Create User model


$ rails g model user

file db/migrate/[timestamp]_create_users.rb


class CreateUsers < ActiveRecord::Migration
 def change
   create_table :users do |t|
     t.string :first_name
     t.string :last_name
     t.string :email
     t.string :phone
     t.timestamps
   end
 end
end


$ rake db:migrate

app/model/user.rb


class User < ActiveRecord::Base
 validates :first_name, :last_name, :email, presence: true
end

Create Users controller


$ rails g controller users

Create the CRUD operation in users controller and send JSON response. The code sample is here

Add angular gem

In Gemfile add these two gems.


gem 'angularjs-rails'
gem 'angular-ui-bootstrap-rails' #for bootstrap UI


$ bundle install

Setup layout

Adding ng-app and ng-view indicates that we have an AngularJS app in the page.


<html ng-app="myapplication">
 <head>
   <title>Rails4CrudWithAngularjs</title>
   <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
   <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
   <%= csrf_meta_tags %>
 </head>
 <body>
   <div class="container" ng-view>
     <%= yield %>
   </div>
 </body>
</html>

Create an angular controller

First let’s create a directory for our controllers. You can name it whatever you want.

$ mkdir -p app/assets/javascripts/angular/controllers

Now create users_controllers.js file. Here I have used the same naming convention as Rails.

// app/assets/javascripts/angular/controllers/users_controllers.js
var myApp = angular.module('myapplication', ['ngRoute', 'ngResource']);

‘myapplication’ is ng-app name.

Add Factory

Factory is the angular provider and you can learn more about it here. It basically interacts with the rails server and processes the json response.

myApp.factory('Users', ['$resource',function($resource){
 return $resource('/users.json', {},{
 query: { method: 'GET', isArray: true },
 create: { method: 'POST' }
 })
}]);

myApp.factory('User', ['$resource', function($resource){
 return $resource('/users/:id.json', {}, {
 show: { method: 'GET' },
 update: { method: 'PUT', params: {id: '@id'} },
 delete: { method: 'DELETE', params: {id: '@id'} }
 });
}]);

‘Users’ factory is used for getting the collection of users and creating users. ‘User’ factory is used to get the user details, update the user or delete the user.

Add Routes

Angular routes are used for deep-linking URLs to controllers and views (HTML partials). It watches $location.url() and tries to map the path to an existing route definition.

myApp.config([
 '$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
 $routeProvider.when('/users',{
    templateUrl: '/templates/users/index.html',
    controller: 'UserListCtr'
 });
 $routeProvider.when('/users/new', {
   templateUrl: '/templates/users/new.html',
   controller: 'UserAddCtr'
 });
 $routeProvider.when('/users/:id/edit', {
   templateUrl: '/templates/users/edit.html',
   controller: "UserUpdateCtr"
 });
 $routeProvider.otherwise({
   redirectTo: '/users'
 });
 }
]);

In the code above, I have added the controllers UserListCtr, UserAddCtr, UserUpdateCtr for listing users and to create and update users.

Add Angular templates

Now we need to add templates. I have stored them in public/templates.

If we create a file public/templates/users/index.html with some arbitrary content, we should be able to see it in the browser. Here is a sample template for users.

 CRUD Actions

Now our setup is done and we are ready for processing CRUD operation.

Index Action:

myApp.controller("UserListCtr", ['$scope', '$resource', 'Users', 'User', '$location', function($scope, $resource, Users, User, $location) {
  $scope.users = Users.query(); //it's getting user collection
}]);

‘UserListCtr’ this controller listing users. you can check index.html here I am not explaining index template it’s straight forward angular template, you can read more about it here.

Create Action:

myApp.controller("UserAddCtr", ['$scope', '$resource', 'Users', '$location', function($scope, $resource, Users, $location) {
  $scope.save = function () {
    if ($scope.userForm.$valid){
      Users.create({user: $scope.user}, function(){
      $location.path('/');
    }, function(error){
      console.log(error)
    });
  }
 }
}]);

‘UserAddCtr’ this controller create user. you can check new.html here. Users.create() calling users controller create action. create() action we defined in ‘Users’ factory.

Update Action:

myApp.controller("UserUpdateCtr", ['$scope', '$resource', 'User', '$location', '$routeParams', function($scope, $resource, User, $location, $routeParams) {
   $scope.user = User.get({id: $routeParams.id})
   $scope.update = function(){
     if ($scope.userForm.$valid){
       User.update($scope.user,function(){
         $location.path('/');
       }, function(error) {
         console.log(error)
      });
     }
   };
}]);

‘UserUpdateCtr’ this controller update the user. you can check edit.html here. Users.update() calling users controller update action. update() action we defined in ‘User’ factory.

Delete Action:

For delete user I am not creating separate angular controller. I am writing deleteUser event in ‘UserListCtr’  controller.


myApp.controller("UserListCtr", ['$scope', '$http', '$resource', 'Users', 'User', '$location', function($scope, $http, $resource, Users, User, $location) {

  $scope.users = Users.query();

  $scope.deleteUser = function (userId) {
    if (confirm("Are you sure you want to delete this user?")){
      User.delete({ id: userId }, function(){
        $scope.users = Users.query();   // after delete user get users collection.
        $location.path('/');
      });
    }
  };
}]);

User.delete() calling users controller destroy action. delete() action we defined in ‘User’ factory.

In  public/templates/users/index.html for adding ‘Remove’ link


<a href="" ng-click="deleteUser(user.id)">Remove</a>

Remember href should be blank, if you add href=”#” it will call default route in your application.

I hope this blog helps those are started development in Rails + AngularJS.

Advertisements
This entry was posted in Javascript, Ruby on Rails, Tutorials and tagged , . Bookmark the permalink.

16 Responses to Building web apps with Rails4 and AngularJS in 15 minutes

  1. Kapli says:

    I am getting redirect to same users url when trying to create new controller e.g Emplyees
    I have done same things for an another controller Emplyees.
    can anyone help me to solve this issue.?

  2. Pavan says:

    Hi, I am trying to learn the angularjs with Rails. This example from github works perfectly fine in local but when I deploy onto Heroku, I am getting a blank screen. Looks like the templates are not getting injected. Any assistance will be greatly appreciated.

  3. Fabian Rios says:

    I don´t get all the addresses in the controller it has anything to do with anything and confuse me of how to create a proper controller for the operations, can you please explain it and make it simple

    • Can you elaborate your problem. with example so it will help me to provide solution.

      • Fabian Rios says:

        Thanks, you got this in the controller
        def user_params
        unless params[“user”][“addresses”].blank?
        params[“user”][“addresses_attributes”] = params[“user”][“addresses”]
        params[“user”].delete(“addresses”)
        end
        params.fetch(:user, {}).permit(:first_name, :last_name, :email, :phone,
        :addresses_attributes => [:id, :street1, :street2, :city, :state, :country, :zipcode, :_destroy, :user_id])
        end
        I don’t get how this works for example if user only have to params :name and :email how to rewrite def user_params

  4. @fabian above code I write for nested form, In that user has many addresses.
    In Rails when we are using nested_form gem we need addresses_attributes. it’s just mapping I am doing in def user_params. because I am getting params in params[“user”][“addresses”] so I converting in params[“user”][“addresses_attributes”].

    just for readable code in app/assets/javascripts/angular/controller/users_controller.js line 62. instead of addresses_attributes I used addresses.

    if you have only :name and :email for user then no need to add that code. like below

    def user_params
    params.fetch(:user, {}).permit(:name, :email)
    end

  5. neilbo21 says:

    Reblogged this on Neil Estandarte (neilbo21) and commented:
    An approach for Angular and Rails CRUD

  6. John Snow says:

    What about authentication? Can we use device gem?

  7. John Snow says:

    Can we use device gem for auth?

  8. Prarthana says:

    I am using ruby 2.3.1, when I am trying to execute your code using bundle install, it fails due to json 1.8.1. I am not even able to install the json 1.8.1 gem.

  9. Mahesh Sasidharan says:

    in the Gemfile.lock we need to update JSON 1.8.1 to 1.8.3, for compatibility.
    Other than everything worked smooth. Thanks a ton.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s