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.
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.?
@kapil check your location path after create Emplyees. it’s should be your #/emplyees.
Or you can share your code so I will help you.
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.
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.
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
@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
Reblogged this on Neil Estandarte (neilbo21) and commented:
An approach for Angular and Rails CRUD
kiran chaudhari
Thanks alot. This tutorial help me alot.
What about authentication? Can we use device gem?
Hi John Snow,
Sorry for late reply, Yes we can use device gem. I am Using device gem in my project also
Can we use device gem for auth?
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.
Hi Prarthana,
Sorry for late reply. I was used ruby 2.1.1 for this code. Please check this github issue https://github.com/flori/json/issues/229. json-1.8.1.gem Incompatibility with Ruby 2.2.0.
Please install ‘json 1.8.3’ and or ‘json 1.8.3’ in Gemfile and try to bundle install. This will resolve your issue.
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.
I am trying to run this app. So far I’ve succeeded only in rendering index html. When i click on “Create a user”, URL on browser changes to “http://localhost:3000/#!/users#%2Fusers%2Fnew”, but no form is getting rendered.
Since, This is my first trial on angular + rails combination. I doubt i might have missed something?
angular verison i am using is 1.6.2. Can this cause the problem