Render html from helper in RoR - ruby-on-rails

I have a method in a helper, here is the code:
def top_menu_output(parent = Category.where(parent_id: nil))
parent.each do |p|
content_tag(:li) do
parent_id = p.id
if Category.where(parent_id: parent_id).exists?
link_to(p.title, p.page_name, class: "parent")
content_tag(:ul, class: "unstyled") do
subparent = Category.where(parent_id: parent_id)
content_tag(:li) do
top_menu_output(subparent)
end
end
elsif
link_to(p.title, p.page_name)
end
end
end
end
and I call the method in a view like this
<%top_menu_output%>
but it renders nothing. What do I do to render all the links and li's? Thank you for your answers.
P.S. If I put the code right in the view it works just fine, but the view is obviously not the right place for the method.
P.P.S. If I call the method like this <%=top_menu_output%> it renders all the stuff from my db CATEGORY ID: 1, PARENT_ID: NIL, PAGE_NAME: "", KEYWORDS: "", DESCRIPTION: "", SEO_TEXT_LEFT: "", SEO_TEXT_RIGHT: "", CREATED_AT: "2014-04-19 22:08:55", UPDATED_AT: "2014-04-19 22:08:55"...

The return value of each is the collection that is iterated over, so you are generating a whole bunch of html but then returning something else
You could change that to
parent.collect do |p|
content_tag(:li) do
...
end
end.join
This would collect the html generated by each iteration through the block, concatenate them all and return that. As pointed out in the comments, you also need to use <%= or you'll never see any output

Thanks Frederick, changing each to collect is almost what I want. But it is also necessary to use concat and + so the properly working method is:
def top_menu_output(parent = Category.where(parent_id: nil))
parent.collect do |p|
content_tag(:li) do
parent_id = p.id
if Category.where(parent_id: parent_id).exists?
link_to(p.title, p.page_name, class: "parent") +
content_tag(:ul, class: "unstyled js-menu") do
subparent = Category.where(parent_id: parent_id)
top_menu_output(subparent)
end
elsif
concat link_to(p.title, p.page_name)
end
end
end.join.html_safe
end

Related

Ruby on Rails Custom Helper outputting HTML nested list

as in title I'm trying to create helper that does that but I'm struggling. I'm getting errors or simply empty list like this:
And I want to achieve this:
There is to much logic to simply put this code in view. A results is a hash where the key is a website id and value is either an array of bookmarks ids or just bookmark id.
My code:
module WebsitesHelper
def present_search_results(results)
content_tag(:ul, class: "websites-list") do
results.each do |key, value|
website = Website.find(key)
concat(content_tag(:li, website.url, class: "website-#{key}") do
bookmarks = website.bookmarks.select do |b|
if value.is_a?(Array)
value.include?(b.id)
else
value = b.id
end
end
content_tag(:ul, nil, id: "website-#{key}") do
bookmarks.each do |b|
content_tag(:li, b.title)
end
end
end)
end
end
end
end
If you want to stick with helpers, then something like this could help:
def present_search_results(results)
content_tag(:ul, class: "websites-list") do
results.map do |website_id, bookmarks|
bookmarks = [bookmarks] unless bookmarks.is_a?(Array)
content_tag(:li, class: "website-#{website_id}") do
website = Website.find(website_id)
concat(website.url)
concat(
content_tag(:ul, class: "bookmarks-list") do
bookmarks.map do |bookmark_id|
bookmark = Bookmark.find(bookmark_id)
content_tag(:li, bookmark.title)
end.reduce(:+)
end
)
end
end.reduce(:+)
end
end
But, in my opinion, that code is not easy to read, so you could use plain html instead, like this:
def present_search_results(results)
list = "<ul class='websites-list'>"
results.each do |(website_id, bookmarks)|
bookmarks = [bookmarks] unless bookmarks.is_a?(Array)
website = Website.find(website_id)
list += "<li class='website-#{website_id}'>#{website}"
list += "<ul class='bookmarks-list'>"
bookmarks.each do |bookmark_id|
bookmark = Bookmark.find(bookmark_id)
list += "<li>#{bookmark.title}</li>"
end
list += "</ul></li>"
end
list += "</ul>"
list.html_safe
end
I like this one better, since it is easier to read. But both with output the list you want.

Passing params in a helper

I have, on my Miniature model view page the following code
<%= content_setmini_links_with_quantity(#miniature) %>
It refers to this miniatures_helper method
def content_setmini_links_with_quantity(miniature)
miniature.reverse_contents.map{|content| content_setmini_link_with_quantity(content)}.join(' ').html_safe
end
Which in turn references this miniatures_helper method
def content_setmini_link_with_quantity(content)
string = (tag "td"), (link_to top_pic(content.setmini_id), content.setmini), (link_to content.setmini.name, content.setmini)
string << " x#{content.quantity}" if content.quantity.present?
return string
end
This is supposed to reference the final miniatures_helper method
def top_pic
#miniature = Miniature.find(params[:setmini_id])
if #miniature.collections.first.photo != nil
image_tag(#miniature.collections.first.photo.url(:icon), :retina => true, :class => "curvediconminiset")
else
image_tag("https://system/stock/blank.gif", :retina => true, :class => "curvediconminiset")
end
end
but I can't work out how to call top_pic correctly.
As far as I can see in content_setmini_link_with_quantity(content) the only param available is content. Content.setmini_id is the param I want to use to find the #miniature to display but I can't get it to work.
Any help much appreciated.
You are calling
top_pic(content.setmini_id)
But your top_pic method definition does not have a parameter defined.
Change the definition and first line of top_pic to
def top_pic(setmini_id)
#miniature = Miniature.find(setmini_id)
and forget about using params in a helper.

Times loop does not work

I wrote a simple helper for my rails application:
def calendar_build
5.times do
whole_cal
end
end
def whole_cal
content_tag(:div, :class => "row") do
small_cal + big_cal
end
end
def small_cal
content_tag(:div, :class => "col-xs-2 token-text") do
concat(content_tag(:p, "15.20"))
end
end
def big_cal
content_tag(:div, :class => "col-xs-10 weite_cal") do
concat(content_tag(:input,"", class: "form-control input-sm pa"))
end
end
How you can see i try to generate 5 whole_cal:
def calendar_build
5.times do
whole_cal
end
end
But in my view this only displays 5 why? Thanks
The return value of the loop is 5.
1.9.3p194 :056 > 5.times {}
=> 5
You should probably do
def calendar_build
whole_cals = []
5.times do
whole_cals << whole_cal
end
whole_cals
end
And iterate the array in your view
Couldn't you do whole_cal*5? String multiplication works in general.
You have to add the generated html code to a variable like result, then put result as last line in your method to return the concatenated code
something like:
result = ""
5.times do
result += html code
end
result
and depending on your html code, you should replace the last line with result.html_safe

passing a block into create method in Ruby

I want to write a method which will work like this one
def create_new_car(salon)
Car.create name: salon.offer.name, description: salon.offer.description, photo: salon.offer.photo, dealer_id: salon.dealer_id
end
but i want to keep it DRY. is there a way to pass those attributes by, i dont know, iterating through array of attributes by passing a block?
You can pass a block to create:
def create_new_car(salon)
Car.create do |car|
car.name = salon.offer.name
car.description = salon.offer.description
car.photo = salon.offer.photo
car.dealer_id = salon.dealer_id
end
end
You could also set some attributes as the first parameter and then pass a block:
Car.create(name: salon.offer.name) do |car|
car.description = salon.offer.description
#...
end
You can implement any logic you want inside that block to assign the Car properties, like this:
attributes = ["name", "description", "photo", "dealer_id"]
Car.create do |car|
attributes.each { |a| car.send( "#{a}=", salon.offer.send(a) )
end
Please try this
array_of_attrbiutes = [{name: salon.offer.name...}, {name: }, {}...]
def create_new_car(array_of_attributes)
Car.create array_of_attributes
end
end
Please see https://github.com/rails/rails/blob/b15ce4a006756a0b6cacfb9593d88c9a7dfd8eb0/activerecord/lib/active_record/associations/collection_proxy.rb#L259

blocks in views and refactor in rails

I have notes attribute in Product model with text "something, something else".
In views I wanted see:
<div>
<span>Something</span>
<span>Something else</span>
</div>
Also I have working code, but I want refactor with decorator(draper) or maybe use helpers.
%div
- product.notes.split(/,/).each do |e|
%span= e.strip.capitalize
In decorator:
def notes_list
model.notes.split(/,/).each do |e|
h.content_tag(:span, e.strip.capitalize)
end
end
In views:
%div
= product.notes_list
(or analog in helpers:
def notes_list(product)
product.notes.split(/,/).each do |element|
content_tag(:span, element.strip.capitalize)
end
end
call:
%div
= notes_list(product)
)
But this returns
<div>
"
["something", " something else"]
"
</div>
What is wrong?
your notes_list is returning product.notes.split(/,/)
Try
def notes_list(product)
result = product.notes.split(/,/).inject([]) do |result, element|
result << content_tag(:span, element.strip.mb_chars.capitalize)
end
result.join("\n")
end

Resources