FormBuilder helper method with block capture not working in ERB - ruby-on-rails

I have a custom FormBuilder that has a group method that acts as a wrapper around each label/input/hint/error combo. The group method itself is very simple, but when I try to use it from ERB like I would with "fields_for" or similar, it does not render properly.
def group(**options, &)
options[:class] = class_names(options[:class] || "flex flex-col mt-4", options.delete(:classes))
content_tag(:div, capture(&), **options)
end
In the ViewComponent that is using the form helper I can do the following just fine
def call
#form.group(**#group) do
concat #form.label(:tags, #label.delete(:text), **#label)
concat #form.text_field(:tags, **#system_arguments)
end
end
But if I try to write that in an ERB partial, it either does not render the wrapper from group at all, or it only renders the text_field and not the label
<%= #form.group(**#group) do %>
<%= #form.label(:tags, #label.delete(:text), **#label) %>
<%= #form.text_field(:tags, **#system_arguments) %>
<% end %>
Not sure what I'm missing to get the ERB version to work properly...

There is a fix, but it's not released yet:
https://github.com/ViewComponent/view_component/pull/1650
# Gemfile
gem "view_component", github: "ViewComponent/view_component"
# config/application.rb
config.view_component.capture_compatibility_patch_enabled = true
https://viewcomponent.org/known_issues.html#compatibility-with-rails-form-helpers

Related

Variable scope in child template view in ERB

I'm trying to declare defaults in my layout that can be overridden in the view.
This is my layout:
<%# Variables defaults -%>
<%
#cssFiles = []
#jsFiles = []
-%>
<%# Variables overrides -%>
<%= yield :layoutVariables -%>
And my view:
<% content_for :layoutVariables do -%>
<%
#cssFiles.push 'bootstrap'
#jsFiles.push 'bootstrap'
-%>
<% end -%>
I know this can be done by declaring the variables in the controller but I'd like to know whether the scope can extend from layout->view.
As I see you are trying to collect some assets files inside the view. It is not very good option.
I recommend to make new module
config/initializers/frontend.rb:
Module Frontend
##fe = Array.new()
def self.getfe
##fe
end
def self.add_css(css)
##fe << css
end
end
And now you can simply add your css file anywhere to this array just simply call:
Frontend::add_css "yourfile"
And get your array back simply call
Frontend::getfe
This mechanism with class methods works in views also
Good luck!

Using Mustache lambda functions in ERB

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.

Ruby On Rails image_path

link_to(image_tag("icons/#{icon_name}.png"),url_or_object,options) I was trying use it like that but when I enter the project I'm seeing it like this http://prntscr.com/329yzz I can't see the image please help me,I am beginner in Ruby too
Please Help me
You are getting raw html output.
Use html_safe as shown below:
EDIT
As per OP's comments, html_safe was required in project_title_links method to convert the link returned from link_to_icon into an HTML safe string(DOM ready):
def link_to_icon(icon_name, url_or_object,options={})
link_to(image_tag("icons/#{icon_name}.png", ),url_or_object,options)
end
def project_title_links(project)
content_tag :h1 do [project.title, link_to_icon('show',project) ].join(' ').html_safe end
end
link_to takes an optional block, if you need anything more complicated than text:
link_to url_or_object, options do
image_tag("icons/#{icon_name}.png")
end
Since those method is being used within the views, link_to is using capture to evaluate the block. Hence it can be used like:
<%= link_to url_or_object, options do %>
<div class='wrapper'>
<label>Some text</label>
<%= image_tag("icons/#{icon_name}.png") %>
</div>
<% end %>

ERB -- content_for_with_default

I am trying to reduce the repetitive code with the following pattern in an ERB template:
<% if content_for(some_key) %>
<%= yield(some_key) %>
<% else %>
Some default values here
<% end %>
I've tried defining the following method in ApplicationHelper but understandably it's not working as expected;
def content_for_with_default(key, &block)
if content_for?(key)
yield(key)
else
block.call
end
end
Here's how I'm trying to use it:
<%= content_for_with_default(some_key) do %>
Some default values here
<% end %>
How can I write the content_for_with_default helper so that it has the intended effect?
Your helper should be like this:
def content_for_with_default(key, &block)
if content_for?(key)
content_for(key)
else
capture(&block)
end
end
EDIT: difference between capture(&block) and block.call
After the erb file is compiled, the block will be some ruby code like this:
');#output_buffer.append= content_for_with_default('some_key') do #output_buffer.safe_concat('
');
#output_buffer.safe_concat(' Some default values here
'); end
You see, the strings within the block are concatenated to the output_buffer and safe_concate returns the whole output_buffer.
As a result, block.call also returns the whole output_buffer. However, capture(&block) creates a new buffer before calling the block and only returns the content of the block.

how to generate tag cloud with acts_as_taggable_on_steroids

I have the tag functionality working ok but can't generate a tag_cloud
in my controller:
def tag_cloud
#tags = Article.tag_counts # returns all the tags used
end
in the view:
<% tag_cloud Article.tag_counts.sort { |x, y| x.name <=> y.name }, %w(x-small small normal large x-large) do |tag, css_class| %>
<%= link_to tag.name, tag_url( :tag => tag.name ), :class => css_class %>
<% end %>
I always get a undefined method error for tag_cloud
You can't call controller methods from the view. Try putting it in a model or passing it to the view from the controller.
If this isn't helpful enough, try editing the question and including more details such as the full definition of tag_cloud, explain why you're setting #tag but not using it, etc.
That code doesn't look like it'll do all you want, but to remedy the undefined method error, the proper place for auxiliary methods for views is in the helper, so move the method tag_cloud there instead.
You'll find it in app/helpers/controllername_helper.rb.
tag_cloud defined in module TagsHelper. You need to include it in corresponding helper:
module ApplicationHelper
include TagsHelper
end
Also there is no need in controllers tag_cloud
sergeykish.com is correct, you just need to include the helper in /app/helpers/application_helper.rb
module ApplicationHelper
include TagsHelper
end

Resources