Rails 7 and Tailwind dynamic classes - ruby-on-rails

I have a Rails 7 app with tailwind where I'm doing something like this:
#list = [{name: "Some", width: "3/12"}, {name: "other", width: "6/12"}]
# In the view
<%= render 'mypartial': list: #list %>
# In the partial
<% list.each do |item|%>
<div class="w-<%= item[:width] %>">
<%= item[:name] %>
</div>
<% end %>
The view generates the proper HTML (ex: <div class="w-6/12">), but the classes are not recognized in the browser. If I hard code them without passing the variable, everything works fine. Am I doing something wrong or missing something?

In case someone has the same issue, this is from the doc.
## Class names must be spelled out
For Tailwind to work, your class names need to be spelled out. They can't be programmatically composed. So no "text-gray-#{grade}", only "text-gray-500".
Atm, I added a list of dynamic variables in tailwind.config.js and it works ok, but you need to make sure all the dynamic variables are there.
purge: {
safelist: [
w-1/12,
....
],
},

Having dynamically created classes wont' work. An example:
# application_helper.rb
def badge(text, color)
tag.div text, class: "… bg-#{color}-500 …"
end
# show.html.erb
<%= badge('Completed', 'green') %>
This won't generate the bg-green-500 class in the build because the build process only scans the html.erb files as is, not when they are processed. So it never sees the bg-green-500 class.

You can also add the dynamic classes as a comment in your templates, if you know what options you need. I am trying this approach, since that way my dynamic styles are right next to where I use them.
Here Slim code:
/ To force tailwind to recognize the dynamic styles to apply
/ .hidden.pl-2.pl-4.pl-6
Despite the comment, meaning no render to HTML, Tailwind still picks it up nicely.

If you can figure an exhaustive list of the dynamic classes you're generating, then you could whitelist them in your tailwind.config.js file. To illustrate, let's say you have an erb file containing:
link_to 'Show', project_path, class: "text-#{color}-700"
You'd just need to list your dynamically generated values, such as:
module.exports = {
purge: {
safelist: [
'text-green-700',
'text-red-700',
],
},
}

Related

How to interpolate ruby inside of an interpolated bit of ruby in ERB

I'm trying to create a situation where one user makes message templates and another one can plug in values. I'm using the best_in_place gem, which will allow a user to edit the message on the show page.
The problem is this. When I call the message, with the required erb to make the gem work, it treats all of this as a regular string, not as ruby.
This is unclear, I'm sorry.
Here's the code.
#announcement.content = "The <%= best_in_place #announcement, :train %> is arriving in five minutes."
/show.html.erb
<%= #announcement.content %>
I want it to put "The click to set train is arriving in five minutes." and if the user clicks where it says "click to set train," a text field will open for them to edit (this is something the best-in-place gem does).
Instead, it puts "The <%= best_in_place #announcement, :train %> is arriving in five minutes."
I understand why it is doing this, but I don't know how to make it instead interpret the ruby I'm trying to pass in.
Ideas?
Use regular old string interpolation:
#announcement.content = "The #{best_in_place #announcement, :train} is arriving in five minutes."
You can use ERB to render any ERB template string. In this case something like:
<%= ERB.new(#announcement.content).result %>
Although you likely won't have access to all your Rails helpers, etc.
The Rails way to do this:
#announcement.content_type = :arriving
Later:
<%= render(partial: #announcement.content_type)
In _arriving.erb:
The <%= best_in_place #announcement, :train %> is arriving in five minutes.
TL;DR: ERB is not Ruby, and Rails uses both at different times.
You want simple Ruby string interpolation here:
#announcement.content = "The #{best_in_place #announcement, :train} is arriving in five minutes."
This is unclear, I'm sorry.
Not to worry, the Rails framework throws so many different new concepts at you it can be frustrating for newcomers.
Start from this: the Ruby framework builds the answer to the user's browser from a collection of resources Each file is evaluated by an interpreter for its own language. The trick is: look at the extension.
Files ending in .coffee will be compiled into javascript, files ending in .scss will become CSS, and in the same way files ending in .erb will yield HTML.
ERB is a language composed of mostly HTML already, plus a tag that allows you to interpolate Ruby. ERB stands for Embedded Ruby.
What about files ending in .rb, like the file in which you (surely) are evaluating #announcement.content = "The <%= best_in_place[...]" (a controller, I guess)?
Well, that's just pure Ruby :) that's why the ERB interpolation syntax <%= ... > is not recognized.
What you want to do in the controller, is (as you're trying to do) preparing the data for the view. The ruby in the <%= ... > tag in ERB will have access to the controller's instance variables, i.e. the variables with an # in front defined in the controller. But to define those, inside the controller, you should rely on Ruby alone.
Take-home message:
Be aware of which language you are writing in at each moment. For example:
# show.html.erb
<p>Here is ERB, which will be interpreted straight into HTML</p>
<% "Inside the '<% ...' tag is Ruby, but results won't show up in the HTML because there's no '<%='."%>
<% which_language = "Ruby" # Even variable assignments, and comments, do work %>
<%= "Inside the '<%=' tag, you're writing and interpolating #{which_language} :)" %>
I think the fact that I wasn't clear made it hard to answer this question.
What I'm doing is transforming user-inputted text (using a method in the model, called by the controller) to replace certain keywords with erb tags that call the best_in_place plugin. In my view, when presenting this content to another user, I wanted to call this content, which is saved as an attribute in the database, in such a way that it would render correctly for the other user to have the best_in_place functionality active.
Here's what I ended up doing. It is working, but if you have better ideas, please let me know.
In the announcements#create view, the user creates an announcement with certain pre-defined blocks of bracketed text as well as free-input text. For example, they might write "[train] is leaving from [platform] in [time] minutes."
When they hit save, the controller's create action calls the construct_message method from the model. It looks like this:
def construct_message(msg)
msg.gsub! '[train]', '<%= best_in_place #announcement, :train_id, :as => :select, collection: Train::list_trains, place_holder: "Click here to set train." %>' #note: list_trains and list_platforms are methods on the model, not really important...
msg.gsub! '[platform]', '<%= best_in_place #announcement, :platform_id, :as => select, collection: Platform::list_platforms, placeholder: "Click here to set platform." %>'
msg.gsub! '[time]', '<%= best_in_place #announcement, :number_of_minutes, placeholder: "Click here to set." %>'
end
Then, when I want to show that attribute in my view, I'm using render :inline, like this.
on announcements/:id
<p id="notice"><%= notice %></p>
<p>
<strong>Content:</strong>
<% announcement = #announcement %>
<%= render :inline => announcement.content, locals: { :announcement => announcement } %>
</p>
This allows the erb call that I wrote into the attribute to be functional.
Also note that I'm choosing to use a local rather than instance variable here; this is because in announcements#index, I also render this text and the table there uses local variables.

How can I use parameter from rails config file in a coffeescript file

In my custom rails config file I have "MyApp::Application.config.x.retries = 3"
Is there any way to call it in coffesscript like we do in controller or view with "Rails.configuration.x.retries" ?
If you export those config values (from ruby) to javascript, then you should be able to use them in your coffee script file.
For example, in your layout file (ex: application.html.erb) add the following:
<%= javascript_tag do %>
window.retries = <%= MyApp::Application.config.x.retries %>;
<% end %>
Then in your coffee script file, you can access it through window.retries.
I have used solution presented in RailsCast number 324
<%= content_tag "div", id: "link", data: { url: Rails.configuration.x.link } do %><% end %>
I notice you found a solution but for the record, another alternative is to embed ruby into your coffeescript file with erb - returning your config data as json.
For example..
my_app.coffee.erb
class MyApp
#config = <%= MyApp::Application.configuration.to_json %>
Notice I use a coffeescript class which functions more as a namespace. I can call something along the lines of MyApp.config['x']['retries']

How do I use view helpers with ruby-ejs

I have a rails app that's using the ruby-ejs gem to compile js templates which I am then using in my backbone views.
I would like to use some view helpers to create form elements, such as select tags, within my templates. I found some EmbeddedJS View Helpers here but I don't know how to go about using them in my templates.
Is this possible?
Turns out this was not as difficult as I thought.
I simply included the ejs.js and view.js files from the EmbeddedJS project here and I was able to use the select_tag helper using the full namespace.
<%= EJS.Helpers.prototype.select_tag('example', selected_value, choices) %>
There's probably a better way to access the helper method. I'll post an update once I figure that out.
if you are using express I recommend you use my version of ejs view helpers https://github.com/tanema/express-helpers
you can initialize them like this
var helpers = require('express-helpers')(app);
then in your ejs view use the select tag like this
<%
var choices = [
{value: 1,text: 'First Choice' },
{value: 2,text: 'Second Choice'},
{value: 3,text: 'Third Choice'}
]
%>
<%= select_tag('mySelectElement', 2, choices) %>
which creates:
< select id='mySelectElement' value='2' name='mySelectElement'>
< option value='1' >First Choice</option>
< option value='2' selected='selected'>Second Choice</option>
< option value='3'>Third Choice</option>
< /select>

Trouble on using the i18n gem with partial template files

I am using Ruby on Rails 3.1 and I would like to know how to correctly handle internationalization related to partial template files. That is, ...
... in my app/views/users/flag.html.erb file I have:
<%= t('.test_key1') %>
<%= render :partial => "/users/flag_form" %>
... in my app/views/users/_flag_form.html.erb file I have:
<%= t('.test_key2') %>
If in my config/locales/views/users/en.yml file (note: I am organizing files as stated in the official RoR guide) I use
en:
users:
flag:
test_key1: Test 1 text
test_key2: Test 2 text
the Test 1 text is displayed in the "main" template (app/views/users/flag.html.erb) but the Test 2 text isn't for the partial template (app/views/users/_flag_form.html.erb). How could\should I solve this issue so to properly display the Test 2 text?
config/locales/en.yml
en:
users:
flag:
test_key1: Test 1 text
flag_form:
test_key2: Test 2 text
app/views/users/flag.html.erb
<%= t('.test_key1') %>
<%= render :partial => "/users/flag_form" %>
app/views/users/_flag_form.html.erb
<%= t('.test_key2') %>
NB:
Rails path to the view must match YAML path to the symbol. You need to create an entry at YAML file that matches the name of the view. Omit the trailing underscore since it's a partial.
Read more about lazy lookups
One way would be to using scope, instead of "lazy loading" using the full stop.
Something like this should work:
I18n.t :test_key2, :scope => 'users.flag'
or use:
I18n.t "users.flag.test_key2"
Lastly, you could even pass it to the partial as in
<%= render :partial => "/users/flag_form", :locals => { :test_key => t('.test_key1') } %>
You should also checkout the appendix section on this website as it might be listing something that I am missing:
https://web.archive.org/web/20120619002316/http://www.unixgods.org/~tilo/Rails/where_is_Rails_trying_to_lookup_L10N_strings.html
I wrote this. What do you think about it?
def translate_for_partials key, *args
I18n.t("#{params[:controller].gsub('/', '.')}.#{params[:action]}.#{key}", *args)
end
Is that bad to make such a method ?

How can I use content_for to put something in :yield

I am in ruby 1.9.2, rails3.
So My website has some structures,
and I want to put menu in a middle of my webpage.
I am doing something like (within application.html.erb file)
blahblahblah
<div id="menu">
<%= yield :menu %>
<div>
blahblhablah
I have a file menu.html.erb which has menu structure for the site.
What can I do if I want to use a file within ./layout folder to be used to be part of that yield :menu? I was wondering, if I have to use content_for for every controller, and within every functions...
Btw, menu.html.erb will be different for each controller, so thats why I am yielding it.
In conclusion, I just want to include one common shared menu.html.erb pretty much everywhere.
You could do something like this in your views:
<% content_for(:menu) do %>
<%= render :partial => "/layouts/user_menu.html.erb" %>
<% end %>
You could try to combine this with controller.controller_name (not sure this works for Rails3) and load a different menu for each controller automatically.
You might consider watching the railscast on layouts, it's concise and helpful.
Numbers 7 and 8.
http://railscasts.com/episodes?search=layout

Resources