Accessing a Ruby on Rails object in a Google Maps javascript - javascript

I am trying to access a field in a Ruby on Rails object from my view inside some JavaScript. Here is the relevant code in my view pick.html.erb:
var directionDisplay;
var start = <%= #route.from %>;
var end = <%= #route.to %>;
From and to are text fields that hold zipcodes. Here is my relevant Route controller code:
class RoutesController < ApplicationController
# GET /routes/1/pick
def pick
#routes = Route.find(params[:id])
end
I read somewhere that I need to use ActiveSupport::JSON in order to access an object field...Do I need to do this instead? If so, how do I install it and could you possibly give me an example on how to get started? I've been searching for examples everywhere the past few days and I can't find any.
Let me know if you need any more code and thank you in advance for any reply!

The file name should be (according to convention) pick.js.erb and not pick.html.erb based on the fact that you have JavaScript code included.
If you want to include a partial inside this one, you can use the escape_javascript helper to render the partial.
If you have more than one possible file to be rendered from the pick action, you should look into using respond_to such as:
#route = Route.find(params[:id])
respond_to do |format|
format.html
format.js { render :js => #route }
end

Related

Rails 5.2. Rendering a js.erb partial from a helper method

I have a model called Question, and it has action create;
My goal is to display a flash message instantly, using a helper method (show_alert for example) when the instance is not valid.
question_controller.rb
def create
question = Question.new(question_params)
if question.save then
redirect_to show_question_path(question.id)
else
show_alert(:warning, question.errors)
end
end
application_controller.rb
helper_method :show_alert
def show_alert(type, message)
#type = type; #msg = message
respond_to do |format|
format.js { render :template => 'alert.js.erb'}
end
end
alert.js.erb
var div = $('<div></div>').addClass(`alert alert-${#type}`)
$('<ul></ul>').append( $('<li></li>').html(#msg)
div.append(ul)
$('#alerts').html(div)
But instead of displaying the flash, I get only the partial's code on the white screen.
see the screenshot
Since I've used respond_to I got another error: ActionController::UnknownFormat
I need the snippet of code in alert.js.erb to be executed, in order to render the flash, I think the trick is somewhere in the render function, but two hours of googling were just a waste of time.
Please help! Thank you in advance
ActionController::UnknownFormat error is showing up because the browser is sending HTML request to Rails server, but the respond_to block has only specified what to do in case of a javascript request from web server.
You will need to add a little bit of Ajax to achieve what you want. See this tutorial on Ajax. https://www.tutorialspoint.com/ruby-on-rails/rails-and-ajax.htm
Ajax will send a js request to browser in the background (i.e the browser will not refresh or show any signs of loading). This js request will be sent to Rails server and it will return the .js.erb file containing the script back to the browser. Now since this script was returned as a response to Ajax request by browser, the browser will already know that it is javascript that needs to be executed.
If you do not wish to implement Ajax, you have the alternate of doing something like this in your create controller:-
def create
question = Question.new(question_params)
if question.save then
redirect_to show_question_path(question.id)
else
redirect_to new_question_path(error: question.errors) #new_question_path is the action that displays the question form to the user
end
end
and then you can initialize an error variable in the action that displays the question form. e.g.
def new
#error=params[:error]
#rest of the code...
end
And then in somewhere in your new.html.erb (or whatever the html.erb file name is)
<script>
<% if #error %>
var div = $('<div></div>').addClass(`alert alert-<%= #type %>`)
$('<ul></ul>').append( $('<li></li>').html(<%= #msg %>)
div.append(ul)
$('#alerts').html(div)
<% end %>
// you might need to tweak the variable names in controller or the above code
</script>
(This code above may not be perfect. its just to give u an idea)
However this approach will not be as quick and beautiful as ajax because when the user will submit their question, the entire page will load again to display the error warning.
By default, all output from helpers is escaped. To show the HTMl as-is, you need to use the html_safe method (https://apidock.com/rails/v4.2.1/String/html_safe). See Using helpers in a view escapes the html?
I cannot be sure this without seeing your alert.js.erb but it could be that you need to use escape_javascript in your alert.js.erb
Something like (and I haven't tested this out) in your alert.js.erb
$('<%= escape_javascript("#{type} - #{msg}") %>').appendTo("#alert")
You can read more about it on Rails Guides - Working With Javascript in Rails
Hope this helps!

Accessing JSON information from Rails in d3.js

I'm trying to incorporate d3.js into my project and am having trouble figuring out how to access a JSON object, which is being served from my Ruby on Rails backend.
Here is my Controller code:
class CoursesController < ApplicationController
respond_to :html, :json
def course
#course = Course.find(params[:id])
respond_with #course do |format|
format.html
format.json {render json: #course}
end
end
I've been trying both in the console and in my view to figure out how to access the information. (by the way, if I scrap the respond_with block and just keep the render JSON block, I can see that the JSON object is, in fact, a response object).
Here is what I've been trying (in the view):
<%= javascript_tag do %>
var data = d3.json('<%= #course %>');
console.log(data);
<% end %>
This returns an object, but the object is undefined, so when I call:
dataset[0].course.course_name;
I get the following error:
TypeError: Cannot read property 'course' of undefined
I've also tried something like the following in the console:
var url = '/courses/1';
var dataset = d3.json(url);
dataset[0].course.course_name;
But get the same error.
I'm relatively new to js, so I'm likely making a rookie mistake, but, whatever it is, I can't see it!
Thanks in advance for the help!
The issue is that the d3.json function (https://github.com/mbostock/d3/wiki/Requests#wiki-d3_json) with one parameter returns the request. You would then have to issue that request.
The 'typical' use is to have a second parameter, with a callback function. This is an asynchronous callback, so you can't assign the return value to a var.
Here is what I suggest:
var url = '/courses/1';
d3.json( url, function( error, data ) {
console.log( data );
// do all actions required now that the data is retrieved
} );

Backbone: Pre-fetching collections

I have a backbone application which, upon load, needs to fetch data from four different collections (Rails-->JSON back end).
That's four hits to the server, and I'm guessing there's a better way.
I started out by trying to pass Rails to_json() of the query results into the router initialization in the Rails view such as:
<script type="text/javascript">
$(function() {
window.router = new Backbonedemo.Routers.CalendarsRouter({calendars: [], tasks: <%= #tasks %>});
Backbone.history.start();
});
</script>
but that brought no joy.
So, what's the correct way to run the equivalent of fetch() at startup, without having to hit JSON for each collection I want to collect?
Check out the rabl gem. It allows you to customize your json response to a much greater degree than regular to_json will allow.
Here's a basic way to set up a project where you need to deliver a load of JSON up front:
First, set up your controller to pull data on page load, for examlpe localhost:3000/home would look in the home controller index:
class HomeController < ApplicationController
def index
#user = current_user
render 'user.json' # this line is not actually required most of the time when using backbone and proper ajax/json requests
end
end
Next, set up a rabl template, this takes the place of a view or a partial, and returns JSON to your client. I'm actually going to use a partial, to make loading into the home/index.html view nice and easy:
# views/home/_user.json.rabl
object #user
attributes :id, :first_name, :last_name, :birthdate, :gender, :nickname, :email
node(:avatar_thumb_url) { |u| u.avatar.url :thumb }
node(:roles) { |u| u.roles }
node(:name) { |u| "#{u.first_name} #{u.last_name}".strip }
node(:errors) { |u| u.errors.to_hash if u.errors.present? }
child :awesome_calendars => :calendars do
attributes :id, :date, :description
child :events do
attributes :title, :description
end
end
That's some relatively fancy rabl that will deliver a bunch of json, including a related set of records, all in one JSON object.
In your html view that loads up backbone, you need to pass the controller's object to the partial:
# views/home/index.html.erb
<script type='text/javascript'>
$(function() {
window.router = new Backbonedemo.Routers.CalendarsRouter(<%= render('user.json.rabl', user: #user).html_safe -%>);
Backbone.history.start();
});
</script>
To recap:
controller renders regular html.erb view (the one that starts up backbone)
that view also renders a partial--this partial is a rabl template that returns strictly JSON
backbone takes that JSON and does whatever you want with it.
The beauty of this is that you can set up json.rabl responses for any of your controller actions and have them return a variety of json stuff, all easily controllable. The thing I did above is the "difficult" part where you want to load up stuff from many tables into a single JSON call on your first page load--avoiding multiple AJAX/backbone fetch requests.
Make sense? I hope so... : / let me know if anything is unclear.
I don't know Rails, but see the "bootstrap" example in the Backbone docs:
<script>
Accounts.reset(<%= #accounts.to_json %>);
Projects.reset(<%= #projects.to_json(:collaborators => true) %>);
</script>
Generally, I think you need to create the collection objects, then reset() them with inline JSON data.

javascript and rails 3

Creating an update.js.erb, or edit.js.erb javascript file is connected to the rails 3 actions?
I'm new to rails 3 but know javascript.
If I add respond_to to each action to accept javascript, then will this code be called upon the action?
Thanks
Unable to understand your question, but based on what I understand you might looking for this.
You can render js files using render :js like:
render js: "$('#div_name').some_event;"
In Rails 3 you should set different respond types on the top of your controller class.
Something like this (probably it does not make any sense, but this is a dummy example):
class UsersController < ApplicationController::Base
respond_to :html
respond_to :xml, :json, :except => [ :edit ]
respond_to :js, :only => [:create]
def index
respond_with(User.all)
end
end
Then, you are responding using respond_with, and Rails will recognize type of request based on Accept header or request.format like (/users.json, /users.xml, etc.) and render proper files (index.html.erb, create.js.erb, etc.) depending on format
"Creating an update.js.erb, or edit.js.erb javascript file is connected to the rails 3 actions?" not necessarily, but it should be. conventionally, the file name should be the same as the method name. its a logical relation. but u can specify a file name(if the file name is different the method name) by
respond_to do |format|
format.js { render :action => "different_action" }
end

Cucumber Testing js.erb functions when no html.erb exists

My rails application is using js.erb views instead of html.erb views for specific windows. This works fine in practice, but when I'm using cucumber tests with capybara it gives me an error of
Missing template admin/groups with {:handlers=>[:erb, :rjs, :builder, :rhtml, :rxml], :formats=>[:html], :locale=>[:en, :en]} in view paths
When I click the button pertaining to this particular view. There is no groups.html.erb but there is a groups.js.erb. I want to somehow tell cucumber/capybara to not try to render the groups.html.erb but still render the groups.js.erb. I would prefer not to generate an unnecessary html file to render the same thing the escape javascript below is doing.
groups.js.erb:
$("#admin_content").html("<%= escape_javascript(render :partial => 'groups') %>");
Relevant admin controller method:
def groups
#groups = Group.all
end
You should use proper MIME type handling for your responses. Try this:
def groups
#groups = Group.all
respond_to do |format|
format.js
end
end
eventually you can be more specific about how to respond :
format.js { render layout: false }
more info on respond_to here.
note: if your controller only manages js responses, you can add respond_to :js at the top of the class, and then use respond_with #object in your actions.

Categories