render partial view recursively in rails 5 - ruby-on-rails-5

I'm new to ruby on rails and I'm facing a problem rendering nested questions.
What I want to achieve is rendering the question and check if it have children question then render the children questions as well.
there is no limit on the nesting levels, so I have to use recursion method to achieve this and this is what I came up with.
# view file code
<% #questions.each do |q| %>
<%= render partial: "shared/question_block", locals: {q: q} %>
<% if have_children_questions?(q.id) == 'true' %>
<%= print_children_questions( get_children_ids(q.id) ) %>
<% end %>
<% end %>
and here is the helper functions I created
def have_children_questions?(id)
children = Question.get_children(id)
if !children.empty?
'true'
else
'false'
end
end
def get_children_ids(id)
ids = Question.where(parent: id).pluck(:id)
end
def print_children_questions(ids)
ids.each do |id|
q = Question.find(id)
render partial: "shared/question_block", locals: {q: q}
if have_children_questions?(id)
print_children_questions( get_children_ids(id) )
end
end
end
print_children_questions method returning the ids instead of the partial view, what I'm doing wrong?
is there is a better solution
Thanks in advance

Related

Rails form - search engine

I try to create simple search engine but I meet some problmes. I have several search_field in my form and if either is empty should returns all objects. Otherwise when it has any content it should be selected by that content. Below is my sample form:
<%= form_for :product, url: products_path, method: :get do |form| %>
<%= form.search_field :brand %>
<%= form.search_field :model %>
<%= form.search_field :price_from %>
<%= form.search_field :price_to %>
<%= form.submit 'Submit' %>
<% end %>
my model method:
def self.search(search)
where(brand: search[:brand]).where(model: search[:model]).where("price >= ?", search[:price_from]).where("price <= ?", search[:price_to])
end
But the above piece of code is wrong because if I leave some field empty it is treated directly as empty string instead of ignore this field and final result is not correct.
Summary this form should work similarly to filter on online store
You'd could do something like this
def self.search(search)
results = all
results = results.where(brand: search[:brand]) if search[:brand]
results = results.where(model: search[:model]) if search[:model]
results = results.where("price >= ?", search[:price_from]) if search[:price_from]
results = results.where("price <= ?", search[:price_to]) if search[:price_to]
return results
end
Good luck.

How to Pass Hash parameter of Boolean attribute from view?

I am using Meta-search Gem to search from table by below controller action. I am Using The Rails version 3.2.9.
class FeedEntriesController < ApplicationController
def index
#search = FeedEntry.search(params[:is_star])
#feed_entries = #search.page(params[:page])
#app_keys = AppKey.all
end
end
feed_entries table contain is_star:boolean attribute. So, I just want to pass the hash parameter is_star == true into the params[:is_star] from view using form_for or link_to . I tried using the below way.
In Views/feed_entries/index.html.erb
<%= link_to "Stared", {:controller => "feed_entries", :action => "index", :is_star => true }%>
but the above way is now worked, So I decided to make use of form_for in the below way,
<%= form_for(#is_star) do |f|%>
<%= f.hidden_field :is_star_is_true %>
<%= f.submit "Search" %>
<% end %>
But, nothing is worked, please someone help me resolve this problem.
true and false when passed as a string is parsed as their truthy value when used in a boolean column. This is also true for 0, 1, '0' and '1'
>> m = Model.new
>> m.active = 'false'
>> m.active? # false
>> m.active = 'true'
>> m.active? # true
Knowing this, you can pass 'true' as the value of the hidden_field
<%= f.hidden_field :is_start, value: 'true' %>
You can pass in the value of the parent in the view where the form is being rendere ultimately with something like <%=params[:is_start] = 1 %> . I am not sure how the layout of the app is setup. Also make sure to attr_accessible :is_start
Update: I may have understood your problem wrong. So try this as well
<%= f.hidden_field :is_star, value: 'true' %>
Or you could have a radio button ?
<%= f.radio_button :is_star, value: 'true' %>

How to create own block helper with two or more nested children?

I'd like to to something nested like that in my views:
<%= helper_a do |ha| %>
Content for a
<%= ha.helper_b do |hb| %>
Content for b
<%= hb.helper_c do |hc| %>
Content for c
... and so on ...
<% end %>
<% end %>
<% end %>
To get for example this:
<tag_a>
Content for a
<tag_b class="child_of_tag_a">
Content for b
<tag_c class="nested_child_of_tag_a child_of_tag_b">
Content for c
</tag_c>
</tag_b>
</tag_a>
This means, each level has access to some information of the level above (that's why they are nested and not completely autonomous methods)
I know how to create a simple helper:
def helper_a(&block)
content = capture(&block)
content_tag :tag_a, content
end
And I know I can pass my arguments to the capture to use them in the view, so something like this to get live up the |ha| of my example
def helper_a(&block)
content = capture(OBJECT_HERE, &block)
content_tag :tag_a, content
end
But where do I define this OBJECT_HERE, especially the class for it, and how can this go on nested with multiple levels capturing each block?
I came up with a couple solutions, but I'm far from being an expert in the Rails templating system.
The first one is using an instance variable :
def helper_a(&block)
with_context(:tag_a) do
content = capture(&block)
content_tag :tag_a, content
end
end
def helper_b(&block)
with_context(:tag_b) do
content = capture(&block)
content_tag :tag_b, content
end
end
def helper_c(&block)
with_context(:tag_c) do
content = capture(&block)
content_tag :tag_c, content
end
end
def with_context(name)
#context ||= []
#context.push(name)
content = yield
#context.pop
content
end
which is used this way :
<%= helper_a do %>
Content for a
<%= helper_b do %>
Content for b
<%= helper_c do %>
Content for c
... and so on ...
<% end %>
<% end %>
<% end %>
And the other solution, which passes the context at each step :
def helper_a(context = [], &block)
context = capture(context.push(:tag_a), &block)
content_tag(:tag_a, content)
end
def helper_b(context = [], &block)
context = capture(context.push(:tag_b), &block)
content_tag(:tag_b, content)
end
def helper_c(context = [], &block)
context = capture(context.push(:tag_c), &block)
content_tag(:tag_c, content)
end
which is used this way :
<%= helper_a do |context| %>
Content for a
<%= helper_b(context) do |context| %>
Content for b
<%= helper_c(context) do |context| %>
Content for c
... and so on ...
<% end %>
<% end %>
<% end %>
But I'd really advise against using either of these solutions if all you're doing is CSS styling and/or Javascript manipulation. It really complicates the helpers, is likely to introduce bugs, etc.
Hope this helps.

Any possible way to set radio_button_tag values by a database set value

I have a radio_button_tag in a form, which holds various values for a persons current availability:
Mike Donnall o Available o Out of office o Vacation
So originally you open the form, and select a value, this then sets the value in the Status table for that Person.
However, there's also functionality to re-open the form and update his present status, perhaps from Vacation to Available.
My question is, is there anyway at all that radio button :checked can be modified to accept a custom method, I have found something in a similar posting, but I want the value foe that radio button to be set to the value in the DB.
My code so far, a stab in the dark perhaps:
View:
<% #people.each do |p| %>
<% #statuses.each do |s| %>
<%= "#{p.name}" %>
<%= "#{s.status_name}" -%><%= radio_button_tag ['person', p.id], ['status',
s.id], checked?(p.id) %>
<% end %>
<% end %>
Helper:
def checked?(person)
#person = person
#status = Status.find_by_sql(['select status_id from statuses where person_id = ?, #person])
if #result
return true
end
As you can see Im a bit lost here, but I understand that the method should return the value of the checkbox that needs to be checked, but Im wondering because its a checked functionality, would it only be limited to being a true or false?
So for a persons.status.id check if its true or false.
It seems from your helper's SQL that you the following relationship setup between People and Statuses:
class Person < ActiveRecord::Base
has_one :status
end
class Status < ActiveRecord::Base
belongs_to :person
end
You can access one given person status like this:
person = Person.first
person_status = person.status
Using that knowledge, your desired view outcome becomes quite simple:
<% #people.each do |p| %>
<p><%= "#{p.name}" -%>
<% #statuses.each do |s| %>
<%= "#{s.status_name}" -%>
<%= radio_button_tag ['person', p.id],
['status', s.id],
(p.status == s) ? true : false %>
<% end %>
<% end %>
You can of course extract the logic to a helper, but that doesn't seem necessary.
On a personal note, this isn't the way I'd present the information to user, it' too heavy on information in one line. I suggest you put the person's name in a p tag, and use a ul tag for the statuses.

How to render all records from a nested set into a real html tree

I'm using the awesome_nested_set plugin in my Rails project. I have two models that look like this (simplified):
class Customer < ActiveRecord::Base
has_many :categories
end
class Category < ActiveRecord::Base
belongs_to :customer
# Columns in the categories table: lft, rgt and parent_id
acts_as_nested_set :scope => :customer_id
validates_presence_of :name
# Further validations...
end
The tree in the database is constructed as expected. All the values of parent_id, lft and rgt are correct. The tree has multiple root nodes (which is of course allowed in awesome_nested_set).
Now, I want to render all categories of a given customer in a correctly sorted tree like structure: for example nested <ul> tags. This wouldn't be too difficult but I need it to be efficient (the less sql queries the better).
Update: Figured out that it is possible to calculate the number of children for any given Node in the tree without further SQL queries: number_of_children = (node.rgt - node.lft - 1)/2. This doesn't solve the problem but it may prove to be helpful.
It would be nice if nested sets had better features out of the box wouldn't it.
The trick as you have discovered is to build the tree from a flat set:
start with a set of all node sorted by lft
the first node is a root add it as the root of the tree move to next node
if it is a child of the previous node (lft between prev.lft and prev.rht) add a child to the tree and move forward one node
otherwise move up the tree one level and repeat test
see below:
def tree_from_set(set) #set must be in order
buf = START_TAG(set[0])
stack = []
stack.push set[0]
set[1..-1].each do |node|
if stack.last.lft < node.lft < stack.last.rgt
if node.leaf? #(node.rgt - node.lft == 1)
buf << NODE_TAG(node)
else
buf << START_TAG(node)
stack.push(node)
end
else#
buf << END_TAG
stack.pop
retry
end
end
buf <<END_TAG
end
def START_TAG(node) #for example
"<li><p>#{node.name}</p><ul>"
end
def NODE_TAG(node)
"<li><p>#{node.name}</p></li>"
end
def END_TAG
"</li></ul>"
end
I answered a similar question for php recently (nested set == modified preorder tree traversal model).
The basic concept is to get the nodes already ordered and with a depth indicator by means of one SQL query. From there it's just a question of rendering the output via loop or recursion, so it should be easy to convert this to ruby.
I'm not familiar with the awesome_nested_set plug in, but it might already contain an option to get the depth annotated, ordered result, as it is a pretty standard operation/need when dealing with nested sets.
Since september 2009 awesome nested set includes a special method to do this:
https://github.com/collectiveidea/awesome_nested_set/commit/9fcaaff3d6b351b11c4b40dc1f3e37f33d0a8cbe
This method is much more efficent than calling level because it doesn't require any additional database queries.
Example: Category.each_with_level(Category.root.self_and_descendants) do |o, level|
You have to recursively render a partial that will call itself. Something like this:
# customers/show.html.erb
<p>Name: <%= #customer.name %></p>
<h3>Categories</h3>
<ul>
<%= render :partial => #customer.categories %>
</ul>
# categories/_category.html.erb
<li>
<%= link_to category.name, category %>
<ul>
<%= render :partial => category.children %>
</ul>
</li>
This is Rails 2.3 code. You'll have to call the routes and name the partial explicitely before that.
_tree.html.eb
#set = Category.root.self_and_descendants
<%= render :partial => 'item', :object => #set[0] %>
_item.html.erb
<% #set.shift %>
<li><%= item.name %>
<% unless item.leaf? %>
<ul>
<%= render :partial => 'item', :collection => #set.select{|i| i.parent_id == item.id} %>
</ul>
<% end %>
</li>
You can also sort their:
<%= render :partial => 'item', :collection => #set.select{|i| i.parent_id == item.id}.sort_by(&:name) %>
but in that case you should REMOVE this line:
<% #set.shift %>
Maybe a bit late but I'd like to share my solution for awesome_nested_set based on closure_tree gem nested hash_tree method:
def build_hash_tree(tree_scope)
tree = ActiveSupport::OrderedHash.new
id_to_hash = {}
tree_scope.each do |ea|
h = id_to_hash[ea.id] = ActiveSupport::OrderedHash.new
(id_to_hash[ea.parent_id] || tree)[ea] = h
end
tree
end
This will work with any scope ordered by lft
Than use helper to render it:
def render_hash_tree(tree)
content_tag :ul do
tree.each_pair do |node, children|
content = node.name
content += render_hash_tree(children) if children.any?
concat content_tag(:li, content.html_safe)
end
end
end
I couldn't get to work the accepted answer because of old version of ruby it was written for, I suppose. Here is the solution working for me:
def tree_from_set(set)
buf = ''
depth = -1
set.each do |node|
if node.depth > depth
buf << "<ul><li>#{node.title}"
else
buf << "</li></ul>" * (depth - node.depth)
buf << "</li><li>#{node.title}"
end
depth = node.depth
end
buf << "</li></ul>" * (depth + 1)
buf.html_safe
end
It's simplified by using the optional depth information.
(Advantage of this approach is that there is no need for the input set to be the whole structure to the leaves.)
More complex solution without depths can be found on github wiki of the gem:
https://github.com/collectiveidea/awesome_nested_set/wiki/How-to-generate-nested-unordered-list-tags-with-one-DB-hit