I have a partial view called '_comment.erb', and it may be called by parent many times(e.g. in a loop). The '_comment.erb' looks like:
<script>
function aaa() {}
</script>
<%= comment.content %>
<%=link_to_function 'Do', 'aaa()' %>
You can see if the '_comment.erb' be called many times, that the javascript function 'aaa' will be re-defined many times. I hope it can be define only once, but I don't want to move it to parent view.
I hope there is a method, say 'run_once', and I can use it like this:
<%= run_once do %>
<script>
function aaa() {}
</script>
<% end %>
<%= comment.content %>
<%=link_to_function 'Do', 'aaa()' %>
No matter how many time I call the '_comment.erb', the code inside 'run_once' will be run only once. What shall I do?
This is probably not the best solution to your problem, but creating a helper that will yield only once isn't too much trouble:
Helper
def output_once(name, &block)
#output_once_blocks ||= []
unless #output_once_blocks.include?(name)
#output_once_blocks << name
concat(capture(block), block.binding)
end
end
Usage
<% output_once :define_aaa do %>
Your stuff here
<% end %>
<% if !#once_flag && #once_flag=1 %>
<script>
function aaa() {}
</script>
<% end %>
Test to see if the function has already been defined. If not, define/run it
<script type="text/javascript">
/*<![CDATA[*/
if (!aaa) {
function aaa() {}
}
/*]]>*/
</script>
Try something like this
In your layout create a hidden textfield called 'flag' and make it default value as 0
(make sure you dont include this in your partial)
in your partial script
check that value and if it is != 1 execute your function
This is not a solid solution, but since your logic needs some flag to trace the number of executions this will be a workaround
cheers,
sameera
Hmm, if you want to define stuff once, You can just define another partail.
Define javascript in and render _comment.erb partial multiple times here. It should take care of both issues.
As far as I know, there no function like "run_once" available so you either have to write it yourself or handle your code another way. For javascript just define it in your application.js file and then you can call it whenever you want. If it is ruby code then put it in your application helpers and for HTML it really should be a partial.
Related
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.
How can I set ID for body tag in Rails?
<body id="login">
I don't find any of the solutions particularly elegant or flexible. Add a body_class method to application_helper.rb:
def body_class
[controller_name, action_name].join('-')
end
..in layout:
<body class="<%= body_class %>">
#e.g output: <body class="articles-show">
You can then refine the above helper to dynamically inject other identifiers such as modules.
You should use content_for tag here.
In your application layout:
<html>
...
<%= yield :body || "<body>" %>
...
</body>
</html>
And then from any view you can call this:
<% content_for :body do %>
<body id='login'>
<% end %>
That's it :)
You can set a variable #body_id in your action inside a controller or in your view and you can use it in your layout.
So for example if you have an action index you can add this code in your controller:
def index
#body_id = "myid"
end
or in your view index.html.erb as:
<% #body_id = "myid" %>
Then in your layout, I suppose application.html.erb you can add:
<body<%= " id=#{#body_id}" if #body_id %>> # no quotes around #{} are needed ;)
Be careful! The following will not work in rails 3.1
<%= yield :body || "<body>" %>
It might look like it works but if you view source you'll notice your body tag is missing unless you override it. Most browsers will work around a missing body tag.
The correct way to do is as follows
<%= content_for?(:body) ? yield(:body) : raw("<body>") %>
Be careful, you shouldn't be setting view data in your controllers, as some of the above posts have recommended. It breaks the MVC pattern, and creates needless objects that won't be used anywhere but HTML versions of your pages.
Another thing to be aware of is increased risk when you assign your body tag inside a logic block - why risk the page not outputting your body tag (breaking the page) if you don't have to? The example above:
<%= yield :body || "<body>" %>
Someone may not entirely understand your meaning in a child partial, and you're just increasing the duplication of the body tag, meaning you have more places to look if somebody doesn't terminate the tag, etc. There's no need for this risk. A more sensible solution is to yield just the class. HAML will allow you to supply optional class elements, eg:
%body{ id: yield(:body_id), class: yield(:body_class) }
And then set those values in your view:
- content_for(:body_class) { 'dashboard business' }
I'd love a solution that allows for multiple partials to add classes to the one yield block, but haven't found a non-hacky way to do this yet.
<body id="<%= #bodyid %>">
and in your controller, you can set this
#bodyid = "login"
U didnt really explain what exactly u need to do then, my first guess is:
<body id="<%= #body_id %>">
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
I am fairly new to rails so I apologize if I am using the wrong terminology.
I have a model Menuitem that I would like to display the contents of in a layout. How does one go about passing an instance variable into a layout?
I was looking for a layout helper of some sort but I was unable to find anything. I was also looking at defining the instance variable in the application controller to access it in the layout, would this work? If so what is the best way to go about doing it?
Thanks!
The usual way of passing variables up from the view into the parent layout is to use the content_for method. (This answer is a copy + paste from a similar answer I posted at this question)
The normal view content gets rendered automatically into the yield call without an argument in the layout. But you can also put other placeholder content in by using yield with a symbol argument, and specifying that content from the view with content_for.
app/views/layouts/posts_layout.html.erb
<html>
<head>
<title>My awesome site</title>
</head>
<body>
<div id="someMenuStructureHere">
<%= yield(:menu_items) %> <!-- display content passed from view for menu_items -->
</div>
<%= yield %> <!-- display main view content -->
</body>
</html>
app/views/posts/index.html.erb
<%= content_for :menu_items, some_helper_to_generate_menu %>
<h1>Here is you page content</h1>
Two things I would note. First, you probably don't want to be doing this query every time you render any page in your application. You definitely want to cache your MenuItems. Second, it might be helpful to put a convenience method on MenuItems class to cache this value. So, if I define a method
def MenuItem.all_for_menu
##all_for_menu ||= MenuItem.find(:all) #returns value if exists, or initializes it
end
I can call MenuItem.all_for_menu in my layout and get all the menu items. When ever you add a new one or edit one, you'd have to invalidate that.
Another caching approach would be to put the data in a partial and cache that fragment using the standard caching call:
<% cache(:controller => "menu_items",
:action => "list",
:action_suffix => "all_menu_items") do %>
<%= render :partial => "menu", :collection => MenuItem.all_for_menu %>
<% end %>
You can then expire that fragment by calling:
expire_fragment(:controller => "menu_items", :action => "list", :action_suffix => "all_menu_items")
Any instance variables defined in the controllers are auto-magically available in your views. If you are expecting an instance variable in your layout for all actions, you may want to consider defining the instance variable in a before_filter or encapsulating it in a controller method and using helper_method to make it accessible in your views.
It really depends on what you want to do with the model. I'll just guess, and you tell me what you need different to understand better how to do this. This code would work only if your MenuItem model has a field named name.
In the controller:
# Use whatever action you are currently displaying
def index
#menu_items = MenuItem.all
end
In the index.html.erb view file:
<ul id="menu">
<% #menu_items.each do |menu_item| %>
<%= h menu_item.name %>
<% end %>
</ul>
Obviously if this was a real menu, there would be hyperlinks there too :)
items_controller.rb (or something)
def show
#menu_item = MenuItem.find(params[:id])
end
In the view show.html.erb:
<%= #menu_item.name %>