How to use an angularjs resource for rails/RESTful routes? - ruby-on-rails

I've got an angularjs app embedded in a rails app. I'm using the RESTful methods provided by rails for a resource named "Task." Here are the routes I'm dealing with:
GET /api/v1/tasks.json
POST /api/v1/tasks.json
GET /api/v1/tasks/:id.json
PUT /api/v1/tasks/:id.json
DELETE /api/v1/tasks/:id.json
I've got an angularjs-resource for the Task item where the GET for all of the tasks works fine. (Code is in coffee script)
App.factory "Task", ["$resource", "$cookies", ($resource, $cookies) ->
$resource "/api/v1/tasks:slash:type.json?api_token=:api_token",
api_token: $cookies.api_token
type: '#id'
,
all:
method: "GET"
isArray: true
create:
method: "POST"
update:
method: "PUT"
params:
slash: "/"
remove:
method: "DELETE"
]
As you can sort of see here, I'm trying to insert a slash for the PUT method to get the format /api/v1/tasks/:id.json. Unfortunately, angular puts this in as a %2f rather than a slash. I'm not particularly excited about the quality of this code, since added the slash parameter makes it less readable.
Here's my angularjs controller using this resource:
taskController = App.controller 'TasksController', ($rootScope, $scope, $http, $cookies, Task) ->
$scope.message = $cookies.api_token
$scope.tasks = Task.all()
$scope.selectedTask = null
$scope.editTask = (task) ->
$scope.selectedTask = task
$scope.editDescription = task.description
$rootScope.date = new Date(task.dueDate)
console.log($rootScope.dt)
$scope.taskModal = true
$scope.saveTask = ->
$scope.taskModal = false
$scope.selectedTask.$update()
# Task.update({taskID: $scope.selectedTask.id, task: $scope.selectedTask})
console.log 'Implement saving...'
$scope.tasks = Task.all()
$scope.cancelTask = ->
$scope.taskModal = false
$scope.taskModalOptions = {
dialogFade: true
}
taskController.$inject = ['$rootScope', '$scope', '$http', '$cookies', 'Task']
Basically, my question is does anyone have an example of how do reproduce the traditional rails/RESTful URL formats with an angularjs resource? I've looked at several StackOverflow questions and can't seem to find a good answer. Any help is appreciated.

Have a look at restangular, its specifically designed to make HTTP verb requests to any REST api simple & easy.
https://github.com/mgonto/restangular

I had the problem with this and the nesting of the JSON data. The following code seems to work for me:
contacts.factory('Contacts', ['$resource',
function($resource) {
function nestData(data, headersGetter) {
return JSON.stringify({
contact: data
});
};
return $resource('/api/v1/contacts/:id.json', {
id: '#id'
}, {
'index': {
method: 'GET',
isArray: true
},
'show': {
method: 'GET',
isArray: false
},
'create': {
method: 'POST',
transformRequest: nestData
},
'save': {
method: 'PUT',
transformRequest: nestData
},
'destroy': {
method: 'DELETE'
}
}); // Note the full endpoint address
}
]);
In my controller I'm able to use:
function createContact(data) {
Contacts.save({
name: data.name,
phone: data.phone
});
}
Please give it a try and let me know

Related

Passing nested parameters to an ajax get request

In a Rails 5.1 app (without jQuery) how can I pass nested params via a GET ajax request?
I have the following
Rails.ajax({
url: select.getAttribute('data-url') + "?xxx",
type: "GET"
});
If I replace xxx with, for instance, pippo=pluto, in my controller
params[:name] #=> "pluto"
However, in my controller, I need to be able to access a nested param as below.
params[:user][:name] #=> "pluto"
It seems a simple problem but I cannot find a solution.
Here my JS
document.addEventListener('turbolinks:load', function() {
var select = document.querySelector("select[name='user[name]']")
if(select.options[select.selectedIndex].value) {
Rails.ajax({
url: select.getAttribute('data-url'),
type: "GET",
data: {
user: {
name: select.options[select.selectedIndex].value
}
}
});
}
});
Which produces (user[:name] is always selected)
{"object Object"=>nil, "controller"=>"steps", "action"=>"index"} permitted: false>
The query string works fine (but is ugly)
Rails.ajax({
url: select.getAttribute('data-url') + '?user[name]=' + select.options[select.selectedIndex].value,
type: "GET"
});
SIDE QUESTION: To avoid the ajax request in the first place is there an alternative way to automatically trigger the request of the select below when the page is loaded? Currently, it is triggered only when the selected option changes
<%= f.select :user, MyUsers.all,
{ data: { remote: true, url: duplicate_users_path } } %>
use data option in ajax (recommended)
Rails.ajax({
url: select.getAttribute('data-url'),
type: 'GET',
data: {
users: {
pippo: 'pluto',
pippo2: 'pluto2'
}
}
});
or query string as array
Rails.ajax({
url: select.getAttribute('data-url') + '?users[pippo]=pluto&users[pippo2]=pluto2',
type: 'GET'
});

Redirect from Rails controller after Angular POST

I have a normal Rails app (no SPA) and on one page only I'm using Angular.
At the end I'm sending a POST to the server with the form data (and it is working) and as the final step of the POST I'd like to redirect to another page.
Rails controller is like this:
def save
data = params[:data]
respond_to do |format|
format.js { render :js => "window.location.href = '/products';" }
end
end
But nothing is happening.
My Angular post is simple:
app.service('httpService', ['$http', function($http) {
this.post = function(data) {
$http({
method : 'POST',
url : '/cart/save',
data : { data: data },
headers : {'Content-Type': 'application/json'}
});
};
}]);
Calling service just like this, no promises:
this.save = function(ids) {
httpService.post(ids);
};
Any ideas? Thanks.
EDIT:
I have tried also change the mime-type to 'application/javascript', but no success:
app.service('httpService', ['$http', function($http) {
this.post = function(data) {
$http({
method : 'POST',
url : '/cart/save',
data : { data: data },
headers : {'Content-Type': 'application/javascript'}
});
};
}]);
Ok, so the problem was that the $http is sending an asynchronous request, thus the page cannot be redirected.
The solution was to send jQuery synchronous $.ajax(..., async: false);.

How to fetch index data in Angular using rails server

I am starting with Angularjs + rails backend and trying to fetch users data from the server - equivalent to to controller/index action in rails.
I have followed several tutorials and found this code as the most clear one....
questions
1. How to properly link angular module into views ?
2. How to fetch data using typeahead and sample data in this post.
here is plunker version of the code
Code is as follows:
views
<div ng-app='users'>
<div class='container-fluid' ng-controller="UsersIndexCtrl">
<pre>Model: {{result | json}}</pre>
<input type="text" ng-model="result" typeahead="suggestion for suggestion in users($viewValue)">
</div>
</div>
controller
<script>
// ['ui.bootstrap', 'ngResource'])
var app = angular.module('users', ['ui.bootstrap', 'ngResource']);
// factory - resources users
// equivalent to rails users/index
app.factory('Users', function($resource) {
return $resource('/users.json', {}, {
index: { method: 'GET', isArray: true}
});
});
// factory - user resource
// equivalent to users show, update
app.factory('User', function($resource) {
return $resource('/users/:user_id.json', {}, {
show: { method: 'GET' },
update: { method: 'PUT' }
});
});
// controller - users/ index
// equivalent to rails controller rails/index
var UsersIndexCtrl = function($scope, users) {
$scope.users = users;
};
</script>
and I am stack here, as I am getting this error:
Error: Unknown provider: usersProvider <- users
The goal of this code is to use typehead and provide data to the user.
My '/users.json' url is as follows:
[{"full_name":"Lia Cartwright","id":1,"image_url":"no-icon.jpg"},{"full_name":"Hilton Turner","id":2,"image_url":"no-icon.jpg"},{"full_name":"Aubrey Barrows","id":3,"image_url":"no-icon.jpg"},{"full_name":"Donnie Kris","id":4,"image_url":"no-icon.jpg"},{"full_name":"Eryn Rath","id":5,"image_url":"no-icon.jpg"},{"full_name":"Caden Fay","id":6,"image_url":"no-icon.jpg"},{"full_name":"Arlie Tromp","id":7,"image_url":"no-icon.jpg"},{"full_name":"Rico Klein","id":8,"image_url":"no-icon.jpg"},{"full_name":"Gudrun Dare","id":9,"image_url":"no-icon.jpg"},{"full_name":"Nathan Langworth","id":10,"image_url":"no-icon.jpg"},{"full_name":"Deanna Stroman","id":11,"image_url":"no-icon.jpg"},{"full_name":"Shania Stroman","id":12,"image_url":"no-icon.jpg"},{"full_name":"Lupe Harvey","id":13,"image_url":"no-icon.jpg"},{"full_name":"Constance Armstrong","id":14,"image_url":"no-icon.jpg"},{"full_name":"Reagan Tremblay","id":15,"image_url":"no-icon.jpg"},{"full_name":"Murray Sipes","id":16,"image_url":"no-icon.jpg"},{"full_name":"Dandre Klocko","id":17,"image_url":"no-icon.jpg"},{"full_name":"Haylee Monahan","id":18,"image_url":"no-icon.jpg"},{"full_name":"Florence Harber","id":19,"image_url":"no-icon.jpg"},{"full_name":"Norberto Hoppe","id":20,"image_url":"no-icon.jpg"}]
You must inject Users not users, it's case sensitive.
Side notes:
No need to create two models, replace:
app.factory('Users', function($resource) {
return $resource('/users.json', {}, {
index: { method: 'GET', isArray: true}
});
});
app.factory('User', function($resource) {
return $resource('/users/:user_id.json', {}, {
show: { method: 'GET' },
update: { method: 'PUT' }
});
});
with:
app.factory('User', function($resource) {
return $resource("users/:id", { id: '#id' }, {
index: { method: 'GET', isArray: true, responseType: 'json' },
show: { method: 'GET', responseType: 'json' },
update: { method: 'PUT', responseType: 'json' }
});
})
Other thing, in your controller, do:
var UsersIndexCtrl = function($scope, User) {
$scope.users = User.index();
};
Last thing, consider using: Angular Rails resource

Parameters null in Web API ajax Call

This is the first time i'm working with Web API. i'm trying to call a web api through a jquery ajax call. ajax call hits the web api action successfully but the string parameter "xx" is always null.
Ajax call
var x = "chamara";
$.ajax({
type: 'POST',
url: 'http://localhost:1557/api/values/mytest',
data: '{"xx":"' + x + '"}',
dataType: 'json',
});
Web Api action.
[AcceptVerbs("GET", "POST")]
public void mytest([FromBody]string xx)
{
string a = xx;
}
web api routes configuration.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { action = "get", id = RouteParameter.Optional }
);
Try this:
var x = "chamara";
$.ajax({
type: 'POST',
url: 'http://localhost:1557/api/values/mytest',
data: { '' : x },
dataType: 'json',
});
I encountered the same thing this morning. I'm not sure why and I feel like there should be a better way, but it worked for me.
Alternatively, see this SO question where the solutions suggest setting the contentType to application/json.
I wrote this simple code in JQuery to solve all my Microsoft .NET WebApi woes:
$.webApi = function (method, url, data) {
return $.ajax(url, {
type: method,
data: typeof (data) === "object" ? JSON.stringify(data) : data,
dataType: "json",
contentType: "application/json"
})
};
I call it like this:
$.webApi("POST", "http://url", {object:data});
Makes everything nice and simple. And I don't have to remember all the settings every time.

Url pathname issue in Ajax Post

In development I make an Ajax post which works in development. However when I put it on the Test server it doesn't work because IIS has assigned the application a subfolder, and this is missing in my development environment.
I have found work around (see below) but I am the first to admit this should not be the solution, as I have to remember to call a function for the url everytime I make an Ajax call.
There must be a better way.
However the code will show you what I am fixing;
function OperationsManagerFlagClickFunc(userId) {
$.ajax({
url: GetUrl("/Users/UpdateOperationsManagerFlag"),
type: "POST",
data: { "userId": userId },
success: function (data) { }
});
}
function GetUrl(path) {
var pathArray = window.location.pathname.split('/');
if (pathArray[1] === "ITOC")
return "/ITOC" + path;
else
return path;
}
If you have your javascript in .aspx file, you can generate url like this:
function OperationsManagerFlagClickFunc(userId) {
$.ajax({
url: "<%= Url.Action("UpdateOperationsManagerFlag","User") %>",
type: "POST",
data: { "userId": userId },
success: function (data) { }
});
}
Why not have a variable defined separately, like siteUrl, that will hold your site's url, with different values on the 2 servers?
Then just do:
url: siteUrl + "/Users/UpdateOperationsManagerFlag"

Resources