Consuming a rails json api with angularjs - ruby-on-rails

I'm new to angularjs/clientjs and would like to consume a rails json api with angularjs. After some research I wrote the ff: code but when I visit http://localhost:3000/users I get plain json. Angularjs is not being called to render a view.
How can I render an angularjs view that formats and shows the data a rails json api returns?
rails/routes
app::Application.routes.draw do
get 'main/index' => 'main#index'
resources :users, defaults: {format: :json}
end
rails/users_controller.rb
def index
#users = User.all
end
rails/main_controller.rb
def index
# blank
end
rails/application layout
..
<html ng-app='gold'>
..
<div ng-view>
<%= yield %>
</div>
..
app/assets/templates/main/index.html
app/assets/templates/users/index.html
app/assets/javascripts/main.js
var myApp = angular.module('gold', ['ngRoute', 'ngResource']);
myApp.config(function($routeProvider, $locationProvider, $httpProvider) {
console.log("in router")
$httpProvider.defaults.headers.common['X-CSRF-Token'] =
$('meta[name=csrf-token]').attr('content');
$locationProvider.html5Mode(true);
$routeProvider.
when('/users', {
templateUrl: '../assets/users/index.html',
controller: 'UsersController'
}).when('/main/index', {
templateUrl: '../assets/main/index.html',
controller: 'MainController'
}).otherwise({
redirectTo: '/'
});
});
app/assets/javascripts/services/UserService.js
myApp.factory('UserService', ['$resource', function($resource) {
console.log("in user service")
return $resource('/users/:id', {id: '#id'}, {
index: { method: 'GET', isArray: true },
create: { method: 'POST' },
show: { method: 'GET' },
update: { method: 'PUT', params: {id: '#id'} }
//delete: { method: 'DELETE', params: {id: '#id'} }
});
}]);
app/assets/javascripts/controllers/UsersController.js
myApp.controller('UsersController', ['$scope', 'UserService', function($scope, UserService) {
console.log("in user controller")
$scope.users = UserService.query();
}]);

I think I know what you are trying, as I'm working on similar problem.
Once Rails sends json, that is it. You can't ask it to render other
html/erb and use it to interpret the json data. see this answer on
How can we use rails routes with angularjs in a DRY way?
To achieve both Rails + Angular routing at the same time (render Rails pages with URL, json data with ajax/api calls), I have setup a #view model contains Angular page url for the request + json data.
For a normal HTML request: default.html.erb translates the #view variable to .js using a "jsonService.js.erb", and render the angular page in #view.
For ajax/api calls: Setup routes using angular.config as you did for render page, and add resolve: fetchData(controller#action) to fetch the json data from Rails controllers and use it in the angular page.
Rails Controller:
respond_to do |format|
format.json { #view }
format.html { render 'layouts/default' }
end
default.html.erb
<script id="jsonService"><%= render "layouts/json_service.js" %></script>
<div ng-include ="content.view.template"></div>
Angular
You can use what you got for angular.config. Just remember to use ng-href or ng-click for the links in json/api calls.
if you have dynamic routing in the angular.config, remember to call $scope.$digest after you replaced the html content.

Related

Rails Ajax not displaying the result

I have created a new controller, that in future will be responsible only for translating strings for different objects.
Here is the code of the controller:
# frozen_string_literal: true
class Api::TranslationsController < AuthenticatedController
def translate_string
#translated_string = GoogleTranslator.translate('hello', 'de')
render json: {translated_string: #translated_string.to_json}
end
helper_method :translate_string
private
def translations_params
params.permit(
:id,
:translated_string
)
end
end
It is placed in directory controllers/api/translations_controller. Here is the part of routes:
namespace :api do
resource :translations do
member do
get 'translate_string'
end
end
end
And here is part of my html.erb to call JS function:
<%= image_tag "google-icon.png", id: "google_icon", onclick: "test_transl()",
remote: true%>
And here is my JS code, currently only with Ajax:
<script>
function test_transl(){
$.ajax({
type: "GET",
url: "/api/translations/translate_string",
dataType: "json",
success:function (result){
console.log(result)
}
})
}
</script>
I expect this ajax code to translate the world 'hello' on German and get value #translated_string - 'hallo' in console but nothing happens except the fact, that the error I got is 'statusText: "parsererror"'. What may be wrong?
I finally found what was wrong with my code. The problem was with fact that I didn't pass sessions token in ajax. So now my ajax code will look like this:
$.ajax({
type: "GET",
headers: {
"Authorization": "Bearer " + window.sessionToken
},
url: "/api/translations/translate_string",
dataType: "json",
success: function(result){
console.log(result);
},
error: function (result){
console.log(result, this.error)
}
})
And def from controller like this:
def translate_string
#translated_string = GoogleTranslator.translate('hello', 'de')
render json: #translated_string.to_json
end
The output in console is: "hallo".

Passing in rails routes to AJAX url value

I have an app where a user has a portfolio that has many positions and each position has many movements. So the url for an associated movement index page for a particular position looks like: portfolio_position_movements. I have an index page with and the controller action looks like
def index
#movements = #position.movements.all
respond_to do |format|
format.html
format.json { render json: #movements}
end
end
My ajax call in my movements.js file is this:
var loadData = function(){
$.ajax({
type: 'GET',
contentType: 'application/json; charset=utf-8',
url: ?,
dataType: 'json',
success: function(data){
drawBarPlot(data);
},
failure: function(result){
error();
}
});
};
How can I pass in a dynamic route path so this will work with the movement index on any position object?
You can use erb tags in js files, for me i did it as the following:
#edit.js.erb
$modal = $('.modal'),
$modalBody = $('.modal .modal-body'),
$modalHeading = $('.modal .modal-heading');
$modalHeading.html("Edit <%= #student.full_name.titleize %>'s information");
$modalBody.html("<%= escape_javascript(render 'edit_student') %>");
$modal.modal();
Note: the file extension is .js.erb so rails can process it. I was calling a modal form and the edit method in students_controller.rb was:
def edit
#student = Student.find(params[:id])
respond_to do |format|
format.html # edit.html.erb
format.js # edit.js.erb
format.json { render json: #student }
end
end
Edit:
You can embed the JS code inside html.erb and use rails routes like:
<script>
var loadData = function(){
$.ajax({
type: 'GET',
contentType: 'application/json; charset=utf-8',
url: <%= my_ajax_path %>,
dataType: 'json',
success: function(data){
drawBarPlot(data);
},
failure: function(result){
error();
}
});
};
</script>
What is my_ajax_path?
Is a rails route defined in routes.rb for example i need a list of all available sections that students can apply to using ajax so i did the following:
1- defined a method in students_controllers.rb like this one:
def available_sections
batch_id = (params[:batch_id].nil? || params[:batch_id].empty?) ? 0 : params[:batch_id].to_i
if batch_id == 0
#sections = [].insert(0, "Select Section")
else
batch = Batch.find(params[:batch_id])
# map to name and id for use in our options_for_select
#sections = batch.sections.map{|a| [a.section_name, a.id]}
end
end
2- added a route to it in routes.rb
resources :students do
collection do
get :available_sections
post :create_multiple
end
end
3- Inside new.html.erb:
<script type="text/javascript">
$(document).ready(function() {
$('.student_section_id').hide();
$('#student_batch').change(function() {
$.ajax({
url: "/students/available_sections",
data: {
batch_id : $('#student_batch').val()
},
dataType: "script",
success: function () {
if (+$('#student_batch').val() > 0)
{
$('.student_section_id').fadeIn();
}
else
{
$('.student_section_id').fadeOut();
}
}
});
});
});
</script>
Forget about that messy code :D as it was my first steps but you get the point, and for this line url: "/students/available_sections" it should be using rails routes you can get it by calling rails routes from the command line to get a list of all your application routes

send data from view to controller in rails

I want to send some data from view to controller in rails through ajax.
I have the following code in
app/view/static/home.html.erb
<script type="text/javascript">
var dummy = "testtext";
$('#finish').click(function() {
$.ajax( {
url: '/finish',
type: 'POST',
data: dummy,
dataType: 'text'
});
});
</script>
<body>
<%= button_to "Finish", {:action => 'finish', :controller => 'static'}, :method => :post, id => 'finish' %>
</body>
in
app/view/static/finish.html.erb
<p><%= #data %></p>
app/controller/static_controller.rb
def finish
#data = params[:data]
end
in routes.rb
post 'finish' => 'static#finish'
My understanding is that on button click the ajax script will be executed and rails action will store the data passed from view. This doesn't seem to work. I'm not sure if my understanding of the flow is right.
Because you are calling params[:data] in the controller, you need to specify that {data: dummy} in the AJAX data section
<script type="text/javascript">
var dummy = "testtext";
$('#finish').click(function() {
$.ajax( {
url: '/finish',
type: 'POST',
data: {data: dummy},
dataType: 'text'
});
});
</script>
Also you might want to respond to your AJAX call in your controller using the following
def finish
#data = params[:data]
respond_to do |format|
format.json { insert_your_code_here }
end
end

Ajax:success not working in rails app

Within a rails 4 app, I am using a link_to to send an upvote on posts via json.
Here is what I have in my posts controller:
def upvote
#post = Post.find(params[:id])
#post.liked_by current_user
respond_to do |format|
format.html {redirect_to :back }
format.json { render json: { count: #post.get_upvotes.size } }
end
end
Here is what I have in my view
<%= link_to like_post_path(post), method: :put, class: 'vote', remote: true, data: { type: :json } do %>
<%= image_tag('vote.png') %>
<%= content_tag :span, post.get_upvotes.size %>
<% end %>
<script>
$(document)
.on('ajax:send', '.vote', function () { $(this).addClass('loading'); })
.on('ajax:complete', '.vote', function () { $(this).removeClass('loading'); })
.on('ajax:error', '.vote', function(e, xhr, status, error) { console.log(status); console.log(error); })
.on('ajax:success', '.vote', function (e, data, status, xhr) {
$(this).find("span").html(data.count);
$(this).find("img").attr("src", '<%= asset_path 'voted.png' %>');
});
</script>
When I click on the link, the vote goes through as a JSON request, I see this in my log:
Processing by PostsController#upvote as JSON
But for some reason, my snipped of javascript is not working. Neither the counter or the icon update. How can I fix this? Does this have to do with turbolinks, does it have to do with where I am placing the javascript?
In Rails you can perform a similar task by having a JavaScript response. Add in your respond_to a format.js similar to format.html then have a view upvote.js.erb that looks like:
(function() {
var postId = "#post-<%= #post.id %>";
$(postId).find(".vote").find("span").text("<%= #post.get_upvotes.size %>");
$(postId).find(".vote").find("img").attr("src", "<%= asset_path "voted.png" %>");
})();
I changed your call to .html to .text since you're not actually setting any HTML inside the element, there is no reason to call .html.
This post also assumes there is some mechanism to identify the post the vote link belongs to (in the example the parent post element has an ID of "post-#" where # is the ID of the post object).
EDIT
Two changes I'd make if I were working on this project. First I would attach the voted.png path to the .vote element as a data attribute. data-voted-image-src="<%= asset_path "voted.png" %>". Next, I would never pass a number in the return as there is no reason to do so. When the vote is clicked you can handle everything on the front end by assuming the request is successful. Which saves all this potential nastiness. While I realize that changing from what you current have to adding the data attribute isn't a huge leap I just find it more semantic than having it in the JavaScript.
The click action on the vote link then becomes:
// Assume all posts have a class 'post'
// I'm also using 'one' because once they vote they don't need to anymore
$(".post").find(".vote").one("click", function(e) {
var count = parseInt($(this).find("span").text()),
votedSrc = $(this).data("voted-image-src");
$(this).find("img").attr("src", votedSrc);
$(this).find("span").text(count + 1);
});
Now no response from the server is necessary, and you can change your JSON response to {success: true} or something simple.
jQuery is the default rails javascript library. The default rails javascript library used to be prototype, so old tutorials/docs use it. This is what the ajax looks like with jQuery:
app/controllers/static_pages_controller.rb:
class StaticPagesController < ApplicationController
def show_link
end
def upvote
respond_to do |format|
format.json {render json: {"count" => "10"} }
end
end
end
app/views/static_pages/show_link.html:
<div>Here is an ajax link:</div>
<%= link_to(
"Click me",
'/static_pages/upvote',
'remote' => true, #Submit request with ajax, and put text/javascript on front of Accept header
data: { type: :json }) #Put application/json on front of Accept header
%>
<div>Upvotes:</div>
<div id="upvotes">3</div>
<script>
$(document).ready( function() {
$(this).ajaxSuccess( function(event, jqXHR, ajaxInfo, data) {
//var js_obj = JSON.parse(jqXHR.responseText);
//$('#upvotes').html(js_obj["count"]);
//Apparently, the fourth argument to the handler, data,
//already contains the js_obj created from parsing the
//json string contained in the response.
$('#upvotes').html(data["count"]);
});
});
</script>
config/routes.rb:
Test1::Application.routes.draw do
get 'static_pages/show_link'
get 'static_pages/upvote'
...
end
url to enter in browser:
http://localhost:3000/static_pages/show_link
See jquery docs here:
http://api.jquery.com/ajaxSuccess/
Response to comment:
You could also do the following in your controller:
def upvote
#upvotes = 2 #Set an #variable to the number of upvotes
respond_to do |format|
format.js {} #By default renders app/views/static_pages/upvote.js.erb
end
end
Then:
app/views/static_pages/upvote.js.erb:
$('#upvotes').html(<%= #upvotes %>)

Ajax on Ror, Networkerror

I'm working in a simple increment fonction who is not working. In my console I have this error "NetworkError: 404 Not Found - ..../test/increment_nbr" and I'm not able to fix it.
Html:
<button id="incr">AJAX2</button>
<script>
$("#incr").on("click", function(e) {
e.preventDefault();
$.ajax({
url: "/test/increment_nbr",
dataType: "json",
type: "POST",
success: function(data) {
}
});
})
</script>
My controller:
respond_to :html, :json
def increment_nbr
#user = User.find(params[:id])
#user.increment! :nbr_set
render json: { nbr: #user.nbr_set }.to_json
end
Routes:
resources :test do
member do
put :increment_nbr
post :increment_nbr
end
end
Your routing is incorrect - you've defined your two increment_nbr routes as member routes, meaning they operate on an instance of your parent test route and would generate URLs like /test/2/increment_nbr.
See http://guides.rubyonrails.org/routing.html#adding-more-restful-actions for more information

Resources