Initialize Flowbite components on Ruby on Rails turbo:frame-load event - ruby-on-rails

I am using Flowbite dropdown component with flowbite.turbo.js.
The problem is that after the frame is rendered, Flowbite is not reloaded and the dropdown do not open.
How can I re-initiliaze Flowbite components after the turbo:frame-load event?
application.js
import 'flowbite';
import "flowbite/dist/flowbite.turbo.js";
index.html.erb
<%=turbo_frame_tag "example" do %>
<!-- DROPDOWN FLOWBITE THAT DO NOT OPENS -->
<button id="dropdownDefaultButton" data-dropdown-toggle="dropdown" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-4 py-2.5 text-center inline-flex items-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800" type="button">Dropdown button <svg class="w-4 h-4 ml-2" aria-hidden="true" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path></svg></button>
<!-- Dropdown menu -->
<div id="dropdown" class="z-10 hidden bg-white divide-y divide-gray-100 rounded-lg shadow w-44 dark:bg-gray-700">
<ul class="py-2 text-sm text-gray-700 dark:text-gray-200" aria-labelledby="dropdownDefaultButton">
<li>
Dashboard
</li>
</ul>
</div>
<% end %>
example.html.erb
<%=turbo_frame_tag "example", src: url_for(action: :example), loading: 'lazy' do %>
Loading...
<% end %>

Related

how to integrate tailwindui JS in rails app

i'm on rails 7 with esbuild. I'm using tailwindUI. It works properly when using only css components. When a component uses JS it does not work anymore. For example the dropdown menu is open by default and i can't close it. I added require('#tailwindcss/ui')
in tailwind.config.js
Rails 7.0.0.alpha2
ruby 3.0.2p107
"#tailwindcss/ui": "^0.7.2"
Any idea?
I'm not sure if I fully understand your question, but I've experienced the same issue. The way I solved it was by using stimulus js tailwind components to add Javascript to the elements.
I ended up having to add a "hidden" tag to the class for each element to ensure that it wouldn't flash on screen when the page loaded due to the lag time between the HTML and JS rendering.
You can find them here:
https://github.com/excid3/tailwindcss-stimulus-components
Here is an example of the dropdown code.
If you've got stimulus installed you can do the following:
yarn add tailwindcss-stimulus-components
or
npm install tailwindcss-stimulus-components
then simply add the data-controller, data-action and data-target to the elements you need:
<div class="inline-block text-sm px-4 py-2 leading-none rounded no-underline text-gray hover:text-gray-900 hover:bg-white mt-4 lg:mt-0">
<div class="relative" data-controller="dropdown">
<div data-action="click->dropdown#toggle click#window->dropdown#hide" role="button" data-dropdown-target="button" tabindex="0" class="inline-block select-none">
<span class="appearance-none flex items-center inline-block text-gray-700">
<% if current_user %>
<%= image_tag avatar_url_for(current_user), class: "rounded-full h-8 w-8 align-middle" %>
<% end %>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" class="h-4 w-4"><path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"></path></svg>
</span>
</div>
<div data-dropdown-target="menu" class="absolute right-0 mt-2 hidden">
<div class="bg-white shadow rounded border overflow-hidden">
<%= link_to 'Profile', edit_user_registration_path, data: {action: "click->dropdown#toggle"}, class: 'no-underline block pl-8 py-3 text-gray-900 bg-white hover:bg-gray-300 whitespace-nowrap' %>
<%= link_to 'Password', password_path, data: {action: "click->dropdown#toggle"}, class: 'no-underline block px-8 py-3 text-gray-900 bg-white hover:bg-gray-300 whitespace-nowrap' %>
<%= link_to 'Accounts', user_connected_accounts_path, data: {action: "click->dropdown#toggle"}, class: 'no-underline block px-8 py-3 text-gray-900 bg-white hover:bg-gray-300 whitespace-nowrap' %>
<%= link_to 'Billing', subscription_path, data: {action: "click->dropdown#toggle"}, class: 'no-underline block px-8 py-3 text-gray-900 bg-white hover:bg-gray-300 whitespace-nowrap' %>
<%= link_to 'Sign Out', destroy_user_session_path, method: :delete, data: {action: "click->dropdown#toggle"}, class: 'no-underline block px-8 py-3 border-t text-gray-900 bg-white hover:bg-gray-300 whitespace-nowrap' %>
</div>
</div>
</div>
</div>
I got Tailwind UI components that require JavaScript working with Rails 7 on esbuild by adding alpine.js to my app. https://alpinejs.dev/start-here
Alpine.js can be used for animating dropdowns, toggles, navbars, etc... and the TailwindUI documentation uses it in some example code.
you can either use it via the CDN or install as a package.
if you use it as a CDN then just put the current version number in the URL and add the script tag above your code.
<script defer src="https://unpkg.com/alpinejs#3.7.1/dist/cdn.min.js"></script>
<div x-data="{ open: false }">
<button type="button" #click="open = ! open" class="inline-flex items-center px-2.5 py-1.5 border border-transparent text-xs font-medium rounded shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Button text
</button>
<div x-show="open" #click.outside="open = false">Contents...</div>
</div>
I opted to install the npm packages alpinejs and alpine-turbo-drive-adapter so that I didn't have to add the script tag all over my views. In app/javascript/controllers/applications.js I added the import statements.
import { Application } from "#hotwired/stimulus"
import Alpine from 'alpinejs'
import 'alpine-turbo-drive-adapter'
const application = Application.start()
// Configure Stimulus development experience
application.debug = false
window.Stimulus = application
window.Alpine = Alpine
window.Alpine = Alpine
Alpine.start()
export { application }
another thing to note is that you can't just drop the TailwindUI components that require JavaScript into your code like with Bootstrap. You have to checkout the comments in the TailwindUI code and configure Apline.js to do what they recommend.
for example, if you check out the "simple toggle" the comments say what to change to make the toggle enabled/disabled https://tailwindui.com/components/application-ui/forms/toggles
<!-- This example requires Tailwind CSS v2.0+ -->
<!-- Enabled: "bg-indigo-600", Not Enabled: "bg-gray-200" -->
<button type="button" class="bg-gray-200 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" role="switch" aria-checked="false">
<span class="sr-only">Use setting</span>
<!-- Enabled: "translate-x-5", Not Enabled: "translate-x-0" -->
<span aria-hidden="true" class="translate-x-0 pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200"></span>
</button>
if you checkout the TailwindUI documentation https://tailwindui.com/documentation#using-html-and-your-own-js they give an example of how to use alpine.js to make the toggle functionality work. Essentially you just have to toggle the x-data attribute isOn from true to false so that menus, dropdown, etc... open and close.
<span
x-data="{ isOn: false }"
#click="isOn = !isOn"
:aria-checked="isOn"
:class="{'bg-indigo-600': isOn, 'bg-gray-200': !isOn }"
class="bg-gray-200 relative inline-block flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:shadow-outline"
role="checkbox"
tabindex="0"
>
<span
aria-hidden="true"
:class="{'translate-x-5': isOn, 'translate-x-0': !isOn }"
class="translate-x-0 inline-block h-5 w-5 rounded-full bg-white shadow transform transition ease-in-out duration-200"
></span>
</span>
thanks #Marco
the answer was in the doc. When you use html you'll needs to write your custom JS if you want to make it work

Rails 6 Show or Hide a div if the cookie value matches a token in database

I am building a rails 6 application, and have the requirement that I have to show a banner with a "cookie acceptance".
I have built said banner and when they click the accept button a cookie is created with an "acceptance token", saved to the database and displayed in the cookie.
What I am trying to achieve is to hide that banner if the cookie is present, and the cookie token mates record in the database.
I am stuck on how to call the cookie and match the value against the database. Any assistance here would be great.. cookies are brand new to my world!
my cookie create method: - WORKS
def create
#cookie_acceptance = CookieAcceptance.new(cookie_acceptance_params)
params[:ip_address] = request.remote_ip
return unless #cookie_acceptance.save
cookies[:roadze_cookie_acceptance] = {
value: #cookie_acceptance.accept_token,
expires: 1.year.from_now
}
end
My banner:
<div class="fixed bottom-0 inset-x-0 pb-2 sm:pb-5" id="cookieBanner">
<div class="max-w-screen-xl mx-auto px-2 sm:px-6 lg:px-8">
<div class="p-2 rounded-lg bg-blue-600 shadow-lg sm:p-3">
<div class="flex items-center justify-between flex-wrap">
<div class="w-0 flex-1 flex items-center">
<span class="flex p-2 rounded-lg bg-blue-800">
<div class="flex items-center justify-center h-6 w-6 text-white">
<i class="far fa-question fa-1x"></i>
</div>
</span>
<p class="ml-3 font-medium text-white truncate">
<span class="md:hidden">
This site uses cookies to help your experience.
</span>
<span class="hidden md:inline">
Attention: <span class="logo-font font-bold">roadze<span class="text-xs">.io</span></span> uses cookies to help your overall user experience, and to allow us to offer products specific to your region. <%= link_to "View cookie policy", '#', class: "text-white ml-3 hover:underline" %>
</span>
</p>
</div>
<div class="order-3 mt-2 flex-shrink-0 w-full sm:order-2 sm:mt-0 sm:w-auto">
<div class="rounded-md shadow-sm">
<%= form_for(#cookie_acceptance, remote: true) do |ca| %>
<%= ca.hidden_field :ip_address, value: params[:ip_address] %>
<%= ca.submit 'Accept', class: 'flex items-center justify-center px-4 py-2 border border-transparent text-sm leading-5 font-medium rounded-md text-blue-600 bg-white hover:text-blue-500 focus:outline-none focus:shadow-outline transition ease-in-out duration-150' %>
<% end %>
</div>
</div>
<div class="order-2 flex-shrink-0 sm:order-3 sm:ml-2">
<button type="button" class="-mr-1 flex p-2 rounded-md hover:bg-blue-500 focus:outline-none focus:bg-blue-500 transition ease-in-out duration-150" aria-label="Dismiss">
<svg class="h-6 w-6 text-white" stroke="currentColor" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
</div>
</div>
</div>
</div>
You should stop rendering the partial itself like below(notice condition at the end)
<div>
...
<%= render 'global/marketing/cookie_banner', cookie_acceptance: at_symbol cookie_acceptance if cookie_accepted? %>
...
</div>
and you should have helper method in ApplicationHelper or relevant helper
def cookie_accepted?
CookieAcceptance.exists?(accept_token: cookies[:roadze_cookie_acceptance])
end
if this check is required in controllers as well, you can move above method from helper to ApplicationController and mark this method as helper_method. This way you will be able to use cookie_accepted? across views and controllers.
So this may be a bit "hackie", however I achieved my objective by setting the following in my application_controller
before_action :validate_cookie_presnece
def validate_cookie_presnece
return unless cookies.present?
#cookie = CookieAcceptance.find_by(accept_token: cookies[:roadze_cookie_acceptance]).present?
end
all seems to be working well!

Rails 5.2: Bootstrap 4 tab panel not populating properly

I have the following code in my app:
<!-- Nav tabs -->
<div class="row">
<div class="col-md-3">
<ul class="nav md-pills pills-primary flex-column" role="tablist">
<% #users_with_apps = [] %>
<% AppForm.all.order("created_at DESC").each do |app| %>
<% unless #users_with_apps.include? User.find(app.user_id) %>
<% #users_with_apps << User.find(app.user_id) %>
<% end %>
<% end %>
<% #users_with_apps.each do |user| %>
<% apps = [] %>
<% AppForm.all.order("created_at DESC").each do |app| %>
<% if app.user_id == user.id %>
<% apps << AppForm.find(app.id) %>
<% end %>
<% end %>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#<% user.id %>-panel" role="tab">
Member #<%= fixed_digit_number(user.id) %> (<%= apps.count %>)
</a>
</li>
<% end %>
</ul>
</div>
<div class="col-md-9">
<!-- Tab panels -->
<div class="tab-content vertical">
<% #users_with_apps.each do |user| %>
<% apps = [] %>
<% AppForm.all.order("created_at DESC").each do |app| %>
<% if app.user_id == user.id %>
<% apps << AppForm.find(app.id) %>
<% end %>
<% end %>
<!-- Panel 1 -->
<div class="tab-pane fade" id="<% user.id %>-panel" role="tabpanel">
<% apps.each do |app| %>
<%= render partial: "app_forms/app_line", locals: {application: app} %>
<% end %>
</div>
<!-- Panel 1 -->
<% end %>
</div> <!-- tab content -->
</div> <!-- col -->
</div> <!-- row -->
<!-- Nav tabs -->
It renders to look like this:
<!-- Nav tabs -->
<div class="row">
<div class="col-md-3">
<ul class="nav md-pills pills-primary flex-column" role="tablist">
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#2-panel" role="tab">
Member #00002 (1)
</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#1-panel" role="tab">
Member #00001 (13)
</a>
</li>
</ul>
</div>
<div class="col-md-9">
<!-- Tab panels -->
<div class="tab-content vertical">
<!-- Panel 1 -->
<div class="tab-pane fade" id="2-panel" role="tabpanel">
...panel 2 content...
</div>
<!-- Panel 1 -->
<!-- Panel 1 -->
<div class="tab-pane fade" id="1-panel" role="tabpanel">
...panel 3 content...
</div>
<!-- Panel 1 -->
</div> <!-- tab content -->
</div> <!-- col -->
</div> <!-- row -->
<!-- Nav tabs -->
Ideally, this would create a list of users on the left, and (depending on which user was clicked) their applications would populate on the righthand side.
Unfortunately, no matter what user is clicked, it displays the applications of the first user on the list.
Can anyone see what logic is going wrong here?
The only problem which I am seeing here now is panel ids and link to that panel id shouldn't start with a number. Instead, you can use panel-<id>. i.e: panel-1.
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
<!-- Nav tabs -->
<div class="row">
<div class="col-md-3">
<ul class="nav md-pills pills-primary flex-column" role="tablist">
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#panel-2" role="tab">
Member #00002 (1)
</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#panel-1" role="tab">
Member #00001 (13)
</a>
</li>
</ul>
</div>
<div class="col-md-9">
<!-- Tab panels -->
<div class="tab-content vertical">
<!-- Panel 1 -->
<div class="tab-pane active" id="panel-2" role="tabpanel">
...panel 2 content...
</div>
<!-- Panel 1 -->
<!-- Panel 1 -->
<div class="tab-pane fade" id="panel-1" role="tabpanel">
...panel 3 content...
</div>
<!-- Panel 1 -->
</div> <!-- tab content -->
</div> <!-- col -->
</div> <!-- row -->
<!-- Nav tabs -->

Looping images in bootstrap carousal rails

I'm having some trouble trying to display multiple images attached to a job post using bootstrap carousal. Here's the original code without the caraousal template:
<% if #job.images.attached? %>
<% (0...#job.images.count).each do |image| %>
<%= image_tag(#job.images[image]) %>
<% end %>
<% else %>
<%= image_tag "missing.jpg" %>
<% end %>
Below is a bootstrap carousal example i used from w3 schools and it works fine when i tested it on my app.
<div id="demo" class="carousel slide" data-ride="carousel">
<!-- Indicators -->
<ul class="carousel-indicators">
<li data-target="#demo" data-slide-to="0" class="active"></li>
<li data-target="#demo" data-slide-to="1"></li>
<li data-target="#demo" data-slide-to="2"></li>
</ul>
<!-- The slideshow -->
<div class="carousel-inner">
<div class="item active">
<img src="la.jpg" alt="Los Angeles">
</div>
<div class="item">
<img src="chicago.jpg" alt="Chicago">
</div>
<div class="item">
<img src="ny.jpg" alt="New York">
</div>
</div>
<!-- Left and right controls -->
<a class="carousel-control-prev" href="#demo" data-slide="prev">
<span class="carousel-control-prev-icon"></span>
</a>
<a class="carousel-control-next" href="#demo" data-slide="next">
<span class="carousel-control-next-icon"></span>
</a>
</div>
So far i've tried :
<div id="demo" class="carousel slide" data-ride="carousel">
<!-- Indicators -->
<ul class="carousel-indicators">
<% (0...#job.images.count).each do |image| %>
<li data-target="#demo" data-slide-to=#{image} class="active"></li>
<% end %>
</ul>
<!-- The slideshow -->
<div class="carousel-inner">
<% (0...#job.images.count).each do |image| %>
<div class="item active">
<%= image_tag(#job.images[image]) %>
</div>
<% end %>
</div>
<!-- Left and right controls -->
<a class="carousel-control-prev" href="#demo" data-slide="prev">
<span class="carousel-control-prev-icon"></span>
</a>
<a class="carousel-control-next" href="#demo" data-slide="next">
<span class="carousel-control-next-icon"></span>
</a>
</div>
The images are stacking up against each other, and when i click on the next slide, all the images disappear.I'm not sure exactly what to put in the data-slide classes, i'm suspecting that's the cause of the problem.
UPDATE
This code is working when the image slider automatically plays, but manually clicking on an image slider causes the entire carousal to disappear:
<div id="demo" class="carousel slide" data-ride="carousel">
<!-- Indicators -->
<ul class="carousel-indicators">
<% #job.images.each_with_index do |image, index| %>
<li data-target="#demo" data-slide-to=#{index} <%= index == 0 ? 'class="active"' : '' %>></li>
<% end %>
</ul>
<!-- The slideshow -->
<div class="carousel-inner">
<% #job.images.each_with_index do |image, index| %>
<div class="item <%= index == 0 ? 'active' : '' %>">
<%= image_tag image %>
</div>
<% end %>
</div>
<!-- Left and right controls -->
<a class="carousel-control-prev" href="#demo" data-slide="prev">
<span class="carousel-control-prev-icon"></span>
</a>
<a class="carousel-control-next" href="#demo" data-slide="next">
<span class="carousel-control-next-icon"></span>
</a>
</div>
Firstly, it looks like you're giving the active class to every image in the slideshow. Secondly, there are easier methods of iterating through your images.
Something like this should work:
<div id="demo" class="carousel slide" data-ride="carousel">
<!-- Indicators -->
<ul class="carousel-indicators">
<% #job.images.each_with_index do |image, index| %>
<li data-target="#demo" data-slide-to=#{index} <%= index == 0 ? 'class="active"' : '' %>></li>
<% end %>
</ul>
<!-- The slideshow -->
<div class="carousel-inner">
<% #job.images.each_with_index do |image, index| %>
<div class="item <%= index == 0 ? 'active' : '' %>">
<%= image_tag image %>
</div>
<% end %>
</div>
<!-- Left and right controls -->
<a class="carousel-control-prev" href="#demo" data-slide="prev">
<span class="carousel-control-prev-icon"></span>
</a>
<a class="carousel-control-next" href="#demo" data-slide="next">
<span class="carousel-control-next-icon"></span>
</a>
</div>

Why is my ternary operator not working on a class in ERB to load images from a database to a bootstrap carousel?

I am trying to load images in a bootstrap carousel with images stored in a PostgreSQL database on my Ruby on rails app. I am using ERB on the front end. With the code below nothing shows up... I believe it has something to do with my ternary operator on my class but i am not sure what the exact problem is....
<div id="myCarousel" class="carousel slide" data-ride="carousel">
<!-- Indicators -->
<ol class="carousel-indicators">
<% #post.first(3).each do |image, index| %>
<li data-target="#myCarousel" data-slide-to="<%= index %>" class="<%= index == 0 ? 'active' : '' %>"></li>
<% end %>
</ol>
<!-- Wrapper for slides -->
<div class="carousel-inner" role="listbox">
<% #post.first(3).each do |image, index| %>
<div class="item <%= index == 0 ? 'active' : '' %>">
<%= link_to image_tag(image.image.url, class:"images") %>
<div class="">
<h3><%= index %></h3>
</div>
</div>
<% end %>
</div>
<!-- Left and right controls -->
<a class="left carousel-control" href="#myCarousel" role="button" data-slide="prev">
<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="right carousel-control" href="#myCarousel" role="button" data-slide="next">
<span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
Reason being your ternary operator are not getting index to make it working you need to do with each_with_index instead of each
So here is right way to do:
<div id="myCarousel" class="carousel slide" data-ride="carousel">
<!-- Indicators -->
<ol class="carousel-indicators">
<% #post.first(3).each_with_index do |image, index| %> <!--use each_with_index -->
<li data-target="#myCarousel" data-slide-to="<%= index %>" class="<%= index == 0 ? 'active' : '' %>"></li>
<% end %>
</ol>
<!-- Wrapper for slides -->
<div class="carousel-inner" role="listbox">
<% #post.first(3).each_with_index do |image, index| %> <!--use each_with_index -->
<div class="item <%= index == 0 ? 'active' : '' %>">
<%#= link_to image_tag(image.image.url, class:"images") %> <!--use image_tag -->
<%=image_tag image.image.url ,class: "images"%>
<div class="">
<h3><%= index %></h3>
</div>
</div>
<% end %>
</div>
<!-- Left and right controls -->
<a class="left carousel-control" href="#myCarousel" role="button" data-slide="prev">
<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="right carousel-control" href="#myCarousel" role="button" data-slide="next">
<span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
I have just fixed the issue, if images are there than it should work now. let me know for further guidance.

Resources