I have an angular module where I set a few strings. I'd also like to set an array of strings in the template as well.
application.html.erb
<script type="text/javascript">
angular.module('userFromServer', [])
.service('currentUser', function() {
<% if logged_in %>
this.name = '<%= #User.name %>';
this.friends = '<%= #User.profile.friends_by_uuid %>';
<% end %>
})
</script>
controller
def friends_by_uuid
self.friends.map{|x| "puser_#{x.uuid}"} # also tried adding .to_json
end
However the output appears to have some escaping issues.
"["puser_589b07ee-b8f1-4214-941d-0ce0b7a6703b", "puser_ec7d2918-d514-4c42-91fc-641ed2958fcd"]"
var desired_output = "["puser_589b07ee-b8f1-4214-941d-0ce0b7a6703b", "puser_ec7d2918-d514-4c42-91fc-641ed2958fcd"]
How can I render an array of strings in a rails template?
You should not put logics in views or controllers: Is it bad to have fat views in rails? Fat models are expected. So the solution is chunk the array in models / using helper functions, feed it to controller, and finally use in views. Since no idea what exactly you want, this is the most I can do.
For example:
# controller
def index
#ids = MyModel.split(params[:ids]) # suppose params[:ids] are the data coming in
end
# view
<% #ids.each do |id| %>
# ...
<% end %>
For ruby to process strings, many posts may help. For example, here.
Related
In Rails 3.1 it is not possible to access controller instance variables in an asset js.erb or coffee.erb file using syntax such as <%= #foo %>, where #foo is set in the controller. So then the question is what are the best ways for passing controller variables to CoffeeScript or JavaScript assets.
This question has kind of been asked in multiple convoluted forms on the forum, but my point in asking it again is to have a place where all recommendations are gathered together, and the code supplied is simple and readable. Also note that I'm specifically referring to assets and not view response files.
a couple of ways I have done this in the past
put the data in hidden fields, access the data in js/coffee
# single value
<%= hidden_field_tag "foo_name", #foo.name, { :id => "foo-name" } %>
$('#foo-name').val();
# when the 'value' has multiple attributes
<%= hidden_field_tag "foo", #foo.id, { :id => "foo", "data-first-name" => #foo.first_name, "data-last-name" => #foo.last_name } %>
$foo = $('#foo')
console.log $foo.val()
console.log $foo.data("firstName")
console.log $foo.data("lastName")
another option: load data into js data structure in erb, access it from js/coffee
<% content_for(:head) do %>
<script>
window.App = window.App || {};
window.App.Data = window.App.Data || {};
window.App.Data.fooList = [
<% #list.each do |foo| %>
<%= foo.to_json %>,
<% end %>
];
</script>
<% end %>
# coffee
for foo in window.App.Data.fooList
console.log "#{foo.id}, #{foo.first_name} #{foo.last_name}"
I am not a big fan of constructing javascript data from ruby in erb like this, something about it just feels wrong - it can be effective though
and another option: make an ajax call and get the data on-demand from the server
I am also interested in other ideas and approaches
There is a really nice rail cast and quite recent (feb. 2012) about this specific topic:
#324 Passing Data to JavaScript
It shows 3 ways: a script tag, a data attribute, and the Gon gem.
I think house covered all the available techniques. I would only mention that using an AJAX call is best used when you have a large volume of data, dynamic data or combination of both.
Rather than use a hidden field I chose to add a data attribute to the container div which jquery can pick up.
<div class="searchResults" data-query="<%= #q %>"></div>
then the jquery to access it
url: "/search/get_results?search[q]=" + $(".searchResults").data("query") + "&page=" + p
I feel this is the cleanest way to pass data to javascript. After having found no way to pass a variable to a coffee script file with the rails asset pipeline from a controller. This is the method I now use. Can't wait till someone does set up the controller way with rails that will be the best.
In the controller:
#foo_attr = { "data-foo-1" => 1, "data-foo-2" => 2 }
In the view (HAML):
#foo{#foo_attr}
In the CoffeeScript asset:
$("#foo").data("foo-1")
$("#foo").data("foo-2")
In situations where your javascript data gets out of hand, using the gon gem is still the preferred way to go in rails, even in 2015. After setting up gon, you are able to pass data to your javascript files by simply assigning the data to the gon object in rails.
(Gemfile)
gem 'gon'
(controller)
def index
gon.products = Product.all
(layouts)
<%= include_gon %>
(public/javascripts/your_js_can_be_here.js)
alert(gon.products[0]['id');
(html source automatically produced)
<script>
window.gon = {};
gon.products = [{"created_at":"2015", "updated_at":"2015, "id":1, "etc":"etc"}];
You can read more verbose implementation details on Gon or the two other rails-javascript channels from Ryan Bate's screencast.
http://railscasts.com/episodes/324-passing-data-to-javascript
You can edit and add variables to the params array in the controller then access them in the response.js.erb. Here's an example with params[:value]:
def vote
value = params[:type] == "up" ? 1 : -1
params[:value] = value
#public_comment = PublicComment.find(params[:id])
have_voted = #public_comment.evaluators_for(:pub_votes_up) << #public_comment.evaluators_for(:pub_votes_down)
unless have_voted.include?(#current_user) # vote
#public_comment.add_or_update_evaluation(:"pub_votes_#{params[:type]}", value, #current_user)
else # unvote
#public_comment.delete_evaluation(:"pub_votes_#{params[:type]}", #current_user)
params[:value] = 0
end
respond_to do |format|
format.js # vote.js.erb
end
end
And here's an example accompanying response.js.erb
button = $('<%= ".pub#{params[:type]}_#{params[:id]}" %>')
label = button.find('strong')
<% comment = PublicComment.find(params[:id]) %>
label.html('<%= comment.reputation_for(:"pub_votes_#{params[:type]}").to_i %>')
<% if params[:value] == 1 %>
button.addClass('btn-success')
<% elsif params[:value] == -1 %>
button.addClass('btn-danger')
<% else %>
if button.hasClass('btn-success') { button.removeClass('btn-success') }
if button.hasClass('btn-danger') { button.removeClass('btn-danger') }
<% end %>
I'm trying to share my Mustache templates in Rails across the server and the client on the lines of this Railscast.
All is well, except that I'm unable to figure out where and how to put the definition of a lambda function on the server side.
Let's say my html.erb looks like:
<% if params['client_side'].nil? %>
<%= render 'template', :mustache => #post %>
<% else %>
<script type="text/template" id="template">
<%= render 'template' %>
</script>
<% end %>
The Mustache handler looks like this (exactly as in the Railscast):
module MustacheTemplateHandler
def self.call(template)
if template.locals.include? 'mustache'
"Mustache.render(#{template.source.inspect}, mustache).html_safe"
else
"#{template.source.inspect}.html_safe"
end
end
end
ActionView::Template.register_template_handler(:mustache, MustacheTemplateHandler)
Now for the following template:
<h1>{{title}}</h1>
<div>
{{#marked}}{{content}}{{/marked}}
</div>
the lambda marked is easy to handle in JavaScript, but how can I define it in my Rails code to render content using Redcarpet?
Update
Since posting this, I have tried to expand on the idea of helper functions in the screencast. I now have
<% if params['client_side'].nil? %>
<%= render 'template', :mustache => process(#post) %>
<% else %>
...
The process is defined in ApplicationHelper as
def process(obj)
{
marked: lambda {|text| markdown(Mustache.render(text))}
}
end
This has two problems:
text inside the (Ruby) lambda function is indeed '{{content}}', but Mustache.render(text) fails to do anything with it — it's returning an empty string.
The above code will now only render the marked field and I haven't been able to find a way to retain the other (unprocessed) attributes of the author object (e.g. title). obj.attributes seems like a promising start, but I don't know how to combine the processed response for marked with the other attributes even if #1 above worked.
I got this working myself. The process method in ApplicationHelper now looks like this (using the new lambda syntax):
def process(obj)
obj['marked'] = ->(text) { markdown(Mustache.render(text, obj)) }
obj.attributes
end
This will now catch all invocations of marked in any template.
Hey guys
I'm new to rails. I made this small test code for learning helper in rails:
apps/helpers/home_helper.rb
module HomeHelper
def show(var)
yield var
end
end
apps/views/home/index.html.rb
<%= show('hello world')%>
When I navigate to the url localhost:3000/home/index I got nothing in the html source
What did I do wrong?
There are few things to note here:
module HomeHelper
def show(var)
yield var
end
end
Firstly you're using yield which will pass control to the block given to the method. However, you then call the method without a block:
<%= show('hello world') %>
If you would have had a block it would have looked something like this:
<%= show('hello world') do |v| %>
<%= v %>
<% end %>
This would have output 'hello world' as you expected.
Most like you meant:
module HomeHelper
def show(var)
var
end
end
This returns the value you're passing in and will output it to the response stream.
While block helpers can often to be useful for drying up your code most of the time you want a partial with a layout.
why are you doing yield? Go with simple return:
def show(var)
var
end
Remove the yield. yield is meant for blocks - did you mean return? (it is optional).
Another (arguably better) option is to set the display text in your controller.
home_controller.rb
HomeController < ActionController
# Other controller code...
def index
#text = "Hello, world!"
end
end
index.html.erb
<%= #text %>
In my app I have projects.
Whenever a project_id is present in the PARAMS I would like to inject the following:
<script>
project_id = 123123;
</script>
Any suggestions on how to do this without having to touch multiple views/pages ?
Thank you
Put it in the layout, that way it can show up on all your pages:
# application.html.erb
...
<% if !params[:project_id].blank? %>
<script>
project_id = <%= params[:project_id] %>;
</script>
<% end %>
...
Better yet, if you have a common _javascript partial that gets loaded in all of your layouts, put your code in that partial.
When writing a helper for printing javascript that can be used from both other helpers and views, I stumbled upon the following problem:
def javascript(print_tag = false, &block)
content_for(:javascript) do
if print_tag
javascript_tag(&block) # does not work
javascript_tag { block.call } # does work
else
capture(&block)
end
end
end
This helper should be called with javascript { "alert('hurray'); }.
In the first alternative - which I expected to work - the Rails javascript_tag helper renders an empty <script type="text/javascript"> //<![CDATA[ //]]> </script> tag.
The second alternative, however, works as expected.
What's going on there? How can that be different?
You say you are doing this on your views, right?
<%= javascript { "alert('hurray');" } %>
But for content_tag(&block) to work, you should call javascript the way content_tag is intended to be used in views, which is:
<% javascript do %>
alert('hurray');
<% end %>
content_tag's behavior is different depending on where it's called from, see the function block_called_from_erb? in the source code. In the first case this function returns true because the block does come from an erb (and then it's concated, you don't want that!), in the second returns false (you re-created the block from scratch) and content_tag simply returns the string content, which is what you want.
# ./action_view/helpers/javascript_helper.rb
tag = content_tag(:script, javascript_cdata_section(content), html_options.merge(:type => Mime::JS))
if block_called_from_erb?(block)
concat(tag)
else
tag
end