In my application, users pick train stations from an autocompleting text field. Since there aren't many train stations (~100), I'd prefer for the field to autocomplete based on the contents of a client-side array rather than querying the server each time the user types a character (the goal is to make the autocomplete more responsive).
My question is how to do this in Rails in a maintainable, non-hacky way. My current solution is just putting this line at the bottom of index.html.erb:
<%= javascript_tag "var stations = #{Station.all.map {|s| s.name}.to_json}" %>
but this isn't great (for one thing, I have to remember the "stations" variable name while I'm working on application.js).
You can make a dynamically generated Javascript view file similar to how I show here.
Basically if you have a controller action (such as a javascripts controller stations action). It might look like this.
# in controllers/javascripts_controller.rb
def stations
#stations = Station.all
end
you can then make a JavaScript view file like this...
// in views/javascripts/stations.js.erb
var stations = <%= #stations.map(&:name).to_json %>;
// ...
That javascript file can contain whatever javascript you want, so you can likely move some of application.js code there. You can then include this just like any other javascript file.
<%= javascript_include_tag 'stations' %>
This assumes it's under the 'javascripts' path which happens to be the case if you have a javascripts controller. Otherwise you'll have to pass the full path.
This does have the downside by making one more request to the Rails app, but you can likely add page caching to that js action to optimize that.
In your application.js, on document ready, do an ajax call to fill a local array with the values.
document.observe("dom:loaded", function() {
var stations;
new Ajax.Request(url, {
method: 'get',
onSuccess: function(transport) {
stations = transport.responseText;
}
});
});
My example might not work perfectly (as I use jQuery through jrails now), but it should give the right impression.
Related
I have a Rails app that uses javascript (Backbone) to show user specific data on each users profile page /views/users/show.html.erb. I do this by passing <%= #user.id %> as a data parameter to Backbone's fetch function, however, the only way I know how to get the <%= #user.id %> into Backbone's fetch function is by embedding the javascript in each views/users/show.html.erb page, which therefore allows Backbone to load different user specific info for each views/users/show.html.erb page. Although this works, it seems like the wrong way to do it, as I have read that I should not embed javascript like this. Furthermore, I am going to have to do it a lot, because I wish to display a lot of different kinds of data, more than you see below. So the show.html.erb page will be filled with javascript to make the app work the way I wish.
Question: how might I get #user.id into Backbone's fetch function for each user's show page without embedding javascript in the way that I've done. In the comments, someone suggest I use app/assets/javascripts/user.js, but I don't know how to get <%= #user.id %> into that file. #user.id is readily available in show.html.erb
<script type='text/javascript'>
$(document).ready(function() {
app.collections.awardCollection.fetch({ data: $.param({ user_id: <%= #user.id %> }) }).complete(function(){
app.views.awardCollection = new app.Views.awardCollection({ collection : app.collections.awardCollection});
app.views.awardCollection.render()
});
});
</script>
In order to understand how the views works, is that you can add as many extensions to a view as you want, and they will be parsed by the right library.
If you create a view like
my_view.haml.erb
It will be first parsed with ruby (erb), and then with haml, and will end in a html page.
You can create many views for js, usually you want to archive that when you do ajax, so you can end having a js view like:
my_view.js.erb
First ruby will be parsed (all the <% %> tags), that will end as plain text, and then the server will serve the .js file. But that's usually a common task for ajax.
If you have to render a html page where you want to put some js and you need some ruby code on it, what I usually do is to put the data in the html content with a hidden div.
You can put in any view (even on your layout if you want it to be globally available), something like:
<div id="user_id" style="display: none;"><%= #user.id %></div>
And then on a js (or coffeescript or whatever) you can just check the content of that div:
<script type="text/javascript">
var user_id = $("#user_id").html();
</div>
that's really useful when you want to debug or create tests for your js files, since its plain js and won't throw syntax errors.
I see the comment of Luís Ramalho and Gon is a good option, but I recommend use the following approaches:
If the from the variable is not going to change, print it with <%= %> under .js.erb files located in app/assets/javascripts (note that it will be cached until you restart your app)
If you need server variables the best way is to use Ajax
You can define functions on .js files on app/assets/javascripts and call those functions from the views
If you really don't want any Javascript code in the view, you can create the functions on a .js on app/assets/javascripts (corresponding to the view, for order), and use events and/or store the variables in hidden fields (or even use the data attribute from HTML5)
I have a simple controller that was created through a scaffold and has a "Show" function.
In the view that was created through the scaffold, I have an image that appears only on a certain condition.
I want to set or evaluate the condition in the controller and send it to the view.
I am not sure how to do that or whether this is the correct approach.
The condition should generally be dealt with in the view file using erb, or haml. If you update your question with the condition, then I'll see about updating my answer to reflect it. For now, I'll use a common condition.
Say you only want to show an image if an object is featured. Let's imagine there is a featured field in your object that acts as a flag (1,0).
If this object is say an Article, we can then check the condition in the view file. The controller would obtain the article from the model:
-# articles_controller show action
#article = Article.find(params[:id])
..
-# views/articles/show.html.erb
<% if #article.featured? %>
// show image here
<% end %>
Remember this is an example condition that is not necessarily correct. It is just to illustrate my initial approach.
I wouldn't suggest you use javascript to hide/show depending on this condition, because you are then putting your logic in javascript, when it can be easily managed from within your view files.
If the condition is complex, you would then move it to the model, and perform something like:
if #article.some_complex_condition?
..rather than having that complex condition in your controller file. This allows you to reuse the condition away from the specific controller and makes it more testable.
If you just want to show and hide an image based on a certain condition, than you can do that with JQuery. You shouldn't put anything in the controller that is view-centric.
You can also get the id of whatever data element is in 'show' and pass it to the JavaScript.
JQuery has show() and hide() methods that would work for you. Here's the documentation on the hide method: http://api.jquery.com/hide/
Basically, if you had a certain id for your image, you'd do something like this:
$(document).ready(function() {
$("#myImage").hide();
if (some_condition === true) {
$("#myImage").show();
}
});
You can put that code in your application.js file.
I whipped up a simple JsFiddle demonstrating a way to show and hide with buttons:
http://jsfiddle.net/phillipkregg/92RDS/
Of course, the code may be different depending on what you are trying to do.
If you need to get the 'id' of the object in the 'show' view, than you can put a script tag at the bottom of your show view like this:
<script type="text/javascript">
var my_show_object = <%= #some_object.id %> //this will be the specific id of whatever the object is that you passed from the controller
alert(my_show_object); //this helps with debugging to see if you have the right id
</script>
If you need more than the id, and you want to get the entire Rails object and represent it as Javascript - just do this at the bottom of your rails 'show' view:
<script type="text/javascript">
var my_object = <%= #your_rails_object.to_json %>;
console.log(my_object); //This code allows you to look in the console and see exactly what your object looks like.
</script>
Just like the last one, #your_rails_object is the variable that you have in your show view in the controller. This will return it as a json object so that you can get the id or whatever properties it has.
Also, if you are just learning Rails, I would recommend this online book: http://ruby.railstutorial.org/ruby-on-rails-tutorial-book
That's how I learned Rails - the book is excellent and free.
This is the problem I'm having. I have these filters and they each have their own url which are generated by a helper method filter_url(filter_name). I want to update these filter urls asynchronously (AJAX) when the url changes (which also happens asynchronously and is pushed with HTML5's History API). However, I can't figure out how to update these filter urls because I need to know the name of the filter I am generating the url for (which is client-side in the data-name DOM attribute) but filter_url is server-side. A sure-fire solution would be to move the filter_url logic client-side, but then I would have to maintain both client-side and server-side logic for handling filter urls which ain't DRY at all. So with that said, is what I'm trying to do possible or am I approaching it the wrong way? Thank you a thousand honey bunches of oats!
Perhaps SO should add some gist type of functionality for source code where you can split it by files. Maybe this might have better readability: https://gist.github.com/4c91435aefde9ad5846f. But I will also paste my code here in case my gist expires.
_filters.html.haml
%ul#filters
- #filters.each do |filter|
%li
%a{:"data-name" => filter.name, :href => filter_url(#url_filters.add_to_url(filter.name))}
filters.js.coffee
$('#filters li a').live 'click', ->
history.pushState(null, "", #href) # This changes the url (not using hash bangs)
$.getScript(#href) # This will call index.js.coffee
filters_controller.rb
class FiltersController < ApplicationController
def index
#url_filters = URLFilters.parse(request.fullpath)
end
end
index.html.haml
= javascript_include_tag :filters
= render "filters"
index.js.coffee
$('#filters li a').each ->
# This is the part I'm having trouble with. The code below obviously won't work but hopefully it'll give you an idea of what I'm trying to do
# But basically I want to update each url filter url to account for the new url when the url changes
$(this).attr('href', '<%= #url_filters.add_to_url($(this).data("name")) %>')
Alright, so I think I found a somewhat elegant solution to this.
I changed index.js.coffee to
# Render the partial and get a DOM fragme
filters = $('<%= escape_javascript(render("filters")) %>')
# Replace all filter urls from the ones in the DOM fragment
$('a.filter-url', filters).each ->
$('#' + #id).attr('href', #href) # Replace the href by selecting the id which is unique
and then also changing the partial _filters.js.coffee to
%ul#filters
- #filters.each do |filter|
%li
%a{:id => "#{filter.name}-#{filter.value}", :href => filter_url(#url_filters.add_to_url(filter.name))}
So what I'm doing now is rendering the filters partial and creating a DOM fragment from that and using jQuery to select all the filter urls within that DOM fragment. Then, I am replacing the DOM fragment urls with the ones in the current view. Seems to work well, but I'm open to any other ideas! Hopefully this will help others who run into a similar scenario.
Say I have a list of users in the left column, generated by <%= current_user.clients %> and the second column is empty by default.
However, when the user clicks on one of the links, the second column becomes populated with the projects associated with that user - without the entire page being reloaded (i.e. using AJAX).
I would also like to continue the process, so when they click on a project from that user, the third column is populated with other things (e.g. the name of images, etc.).
How do I accomplish this in Rails?
I assume you are using Rails 3 and jQuery (I'm not well-versed in prototype). It's easy to switch jQuery for prototype in Rails 3: https://github.com/rails/jquery-ujs
For the link:
Something
Using JavaScript and jQuery, write a function that sucks in links of class first_column_link (please rename to something more reasonable, by the way):
$(function() {
$('.first_column_link').bind('click', function() {
$.getJSON('/clients/' + $(this).attr('data-client-id'), function(data) {
// Populate the second column using the response in data
});
});
});
This doesn't work on browsers that don't support or have otherwise disabled JavaScript. Gracefully degrading would likely be a good idea, but without more context, I can't advise you how to best do it.
<%= link_to_remote current_user.clients, go_to_controller_path %>
Proceed from there.
go_to_controller_path routes to an action which renders javascript to update the 2nd column (probably with a partial).
I am learning Ruby on Rails, and I am very confused on how the controller-model-view relationship works for my application.
What I have now is a table full of comments (posts) users have made. What I want to do is let users click on a comment to see more information in a separate panel (ie, other database fields that weren't initially shown, for example the user_id of the person who posted the comment).
In my _post.html.erb, I have something like:
<div class="post" id="<%= post.post_id %>" onclick = ?? >
<p>post.text</p></div>
What should go in onclick? I need a way for the onclick to call a helper/controller method which can load more information, and then put that in another div on a page (I've tried variations of using the controller and helper to call javascript which inserts html into the site, but that seems messier than it should be). From what I understand, I should create some kind of partial _postdetails.html.erb file that handles the actual displaying of the html, but I have no idea how to specific where that partial would go in the page.
Any help would be appreciated, thanks!
You can achieve what you want either by using Rails helpers or by writing the AJAX calls yourself.
Personally I manually write all my AJAX calls using jQuery.
You can also use Prototype which ships with Rails.
That being said you can do.
In your JS file :
$("div.some-class").click(function()
{
$.ajax(
{
url:"url/to/controller/action",
type:<GET>/<POST>,
data://If you wish to sent any payload
});
});
In your controller :
def some_action
#some computation
render :update do |page|
page["id_of_div_to_be_refreshed"].replace_html :partial => "some_partial"
end
end