I have an issue with a delete method not triggering the redirect specified in my controller. I am using Rails 7.0.4 and Ruby 3.1.2
employees_controller.rb
def destroy
#employee.destroy
respond_to do |format|
format.html { redirect_to root_path, notice: "Employee was deleted" }
format.turbo_stream { flash.now[:notice] = "Employee was deleted" }
end
end
_employees.html.erb
<%= turbo_frame_tag "employees" do %>
<%= render employees %>
<% end %>
_employee.html.erb
<div id="<%= dom_id employee %>">
<div class="index bg-white dark:bg-gray-900 px-4 md:px-10">
<div class="overflow-x-auto">
<table class="w-full whitespace-nowrap">
<tbody>
<tr tabindex="0" class="focus:outline-none text-sm leading-none text-gray-600 dark:text-gray-200 h-16">
<td class="w-1/2">
<div class="flex items-center">
<div class="w-10 h-10 rounded-sm flex items-center justify-center">
<p class="text-xs font-bold leading-3 text-white"><%= gravatar_for employee %></p>
</div>
<div class="pl-2">
<p><%= link_to "#{employee.first_name} #{employee.last_name}", employee_path(employee, employee), class: "text-sm font-medium leading-none text-gray-800 dark:text-white"%></p>
<p class="text-xs leading-3 text-gray-600 dark:text-gray-200 mt-2"><%= employee.email %></p>
</div>
</div>
</td>
<td class="pl-8">
<p>
<%= link_to "View", employee_path(employee, employee), type: "button",
class: "inline-block px-6 py-2.5 bg-blue-600 text-white font-medium text-xs leading-tight uppercase rounded shadow-md hover:bg-blue-700 hover:shadow-lg
focus:bg-blue-700 focus:shadow-lg focus:outline-none focus:ring-0 active:bg-blue-800 active:shadow-lg transition duration-150 ease-in-out" %>
</p>
</td>
<td class="pl-8">
<p>
<%= link_to "Edit", edit_employee_path(employee, employee), type: "button",
class: "inline-block px-6 py-2.5 bg-yellow-600 text-white font-medium text-xs leading-tight uppercase rounded shadow-md hover:bg-yellow-700 hover:shadow-lg
focus:bg-yellow-700 focus:shadow-lg focus:outline-none focus:ring-0 active:bg-yellow-800 active:shadow-lg transition duration-150 ease-in-out" %>
</p>
</td>
<td class="pl-8">
<p>
<%= link_to "Delete", employee_path(employee), data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' }, type: "button",
class: "inline-block px-6 py-2.5 bg-red-600 text-white font-medium text-xs leading-tight uppercase rounded shadow-md hover:bg-red-700 hover:shadow-lg
focus:bg-blue-700 focus:shadow-lg focus:outline-none focus:ring-0 active:bg-blue-800 active:shadow-lg transition duration-150 ease-in-out" %>
</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
I even recorded a quick Loom showcasing the lack of redirect in action:
https://www.loom.com/share/e2c333e54fb84b9193c07b54a3546d54
EDIT
The answer below is opinionated: I assumed you want to use Turbo frames/streams as you enclose your employees in an "employees" turbo frame, and also you have a turbo_stream method in your controller.
Though it doesn't clearly answer your question. And the fact the URL changes when you click on an employee is something to investigate.
Please share your show.html.erb file in order to get a better picture.
You are not using Turbo Frame with the single page principle in mind.
You have only a single frame at the moment called "employees". All your employees cards are added as plain HTML. Basically adding a dom_id on a div will have no effect as Turbo (whether through an HTML response or a Turbo_stream response) cannot remove any DOM element which is not a turbo Frame.
When you delete a card from the index page, what I think happens is that the index pages is replayed and the flash happens. Just like the old Rails pre-Turbo era.
In your controller you don't say if you have a destroy.turbo_stream.erb file but I doubt that. If you had it would have no effect anyway.
Now, regarding your show view, it is interesting because the code seems to not be matching your _employee.html.erb partial. Then you have probably some custom HTML in show.html.erb in order to make a more detailed profile. But this detailed profile may not have a turbo frame either.
What to do
In your _employee.html.erb file, replace <div id="<%= dom_id employee %>"> by <%= turbo_frame_tag dom_id(employee) do %> and
replace the closing </div> by a closing <% end %>. All your
employees will have their own turbo frame.
create a destroy.turbo_stream.erb file and add <%= turbo_stream.remove "employee_#{#employee.id}" %> inside. This means that instead of reloading your index page, or basically navigating your app with new pages being loaded, the right Turbo frame is deleted from the view. You basically have not left your view. The call to employees#destroy has been done async by Turbo.
Now regarding your detailed view :
It seems it all lives in show. It would be better to create another partial called _detailed_employee.html.erb and add the same turbo frame <%= turbo_frame_tag dom_id(employee) %>
When you destroy it, it should be removed from the DOM the same way as detailed above.
Note that when you delete this card in show, your show view will be left empty. Which is probably not what you want. So your probably have to tune your logic later.
Also your code is hybrid at the moment: you leverage both Turbo frames (SPA concept) and classic navigation. It is all fine and you will probably find the right balance later. But it is not easy even for experienced programmers to wrap their mind around the concept of actually visiting a page the HTTP way and doing some async requests with Turbo.
I hope my post doesn't have too many errors and that it will help you.
PS: nearly forgot: for your links, you don't need to repeat employee twice employee_path(employee, employee) it makes the path in navbar looks like employees/14.14 Rails understand that the ID param is 14, but the second 14 is not needed.
Related
I have a standard scaffold from
rails g model Room name:string
index.html.erb:
<%= turbo_frame_tag "rooms" do %>
<turbo-frame id="room-list">
<div id="rooms">
<% #rooms.each do |room| %>
<turbo-frame id=<%= "room-controls-#{room.id}"%>>
<div>
<%= render room %>
<%= link_to "Show this room", room } %>
</div>
</turbo-frame>
<% end %>
</div>
</turbo-frame>
<% end %>
when the user clicks on "Show this room" it directs them to show.html.erb
<turbo-frame id=<%="room-controls-#{#room.id}"%>>
<div id="<%= dom_id #room %>">
<%= render #room %>
<div>
<%= link_to "Back to rooms", rooms_path, data: { turbo_frame: "room-controls-#{#room.id}" } %>
</div>
</div>
</turbo-frame>
when the user clicks on the "Back to rooms" link, it fetches the index page as expected, but I'm getting the outer turbo frame rooms in the network response, and not just room-controls-#{#room.id}
In other words, from looking at the network response, it appears reloading and replacing the outer turbo frame along with all the inner turbo frames instead of just the one inner turbo frame that got rendered when I initially clicked on the "Show this room" button.
I tried adding/removing data: { turbo_frame: "room-controls-#{room.id}" } to each of the links but its still behaving in the same manner where it's reloading the outer turbo frame with every instance of the inner turbo frames rather than just the one instance of the inner turbo frame. Is this normal/acceptable? I assume the performance is lower this way since it's re-rendering more than it needs to at O(n) time in the loop. Is there a way to control this so that it will focus on just the inner turbo frame?
Below is an example of the network response. If I clicked on room-controls-17, I'd prefer to only be receiving the turbo frame room-controls-17 instead of all of this
This is my home.html.erb:
<div class="container">
<% #articles.each do |article| %>
<div class="bloc-article">
<div class="article">
<%= image_tag(article.picture, alt: "Article", width:"230", height:"240")%>
<div class="align-content-column">
<div class="celsuis"><span class="moins">- </span> <%= article.counter %>° <span class="plus">+</span></div>
<div class="title">
<%= article.title %>
</div>
<h2><%= article.price %> €</h2>
<div class="desc-author">
<p><%= article.description[0, 200] %>....</p>
<div class="author-deal">
<h3><%= article.author %></h3>
<div class="alert alert-info"><%= link_to "Voir le Deal", article_path(article) %></div>
</div>
</div>
</div>
</div>
</div>
<% end %>
</div>
I have a span - and a span +. I want put an onClick method to increase for + and decrease for -.
To do that I have a database with article.counter (auto-generate number with Faker::numb)
With Vue.js I can do that easily, but on Ruby on Rails I am totaly lost.
You have lots of serious issues about understanding Rails applications (and web applications in general). Here are some hints:
All Ruby code (including those snippets inside <% %>) is evaluated on the server.
All JavaScript code in a Rails application is evaluated on the browsers.
The server and the browsers are different computers, so they don't share memory and thus objects.
A browser can't call Ruby methods as the Ruby code can only be evaluated on the server. A browser can only ask the server to do something via sending HTTP requests.
When a request is finished and the web page is shown on the browser, the server knows the browser no more, and every bit of memory on the server that's associated with the request becomes garbage.
Let's say you have a Model "Article" with a column "quantity". You could do something in the lines of this (I haven't tested it, just giving you a guideline)
As long as you use the normal resources in your routes:
In your view:
<%= form_for (#article), method: :put do |f| %>
<%= f.button "-", name: "update", value: "decrement" %>
<%= f.button "+", name: "update", value: "increment" %>
<% end %>
In your controller:
def update
#article = Article.find(whatever you use to find it)
if params[:update]["increment"]
#article.quantity += 1
elsif params[:update]["decrement"]
#article.quantity -= 1
end
# repond_to here and so on
# and don't forget to #article.save
end
You could then use jQuery (probably no need for an onClick event) to render the part where you show the count in the view.
I am trying to create my image as a block, but it's not working ... The app doesn't read the code inside the block :
<%= image_tag("house-toiture.jpg") do %>
<%= puts "COME ON DUDE" %>
<% end %>
I want to add the following information inside :
<% #operation_presenter.works.each do |work| %>
<!-- (<%= work.travaux %>) -->
<button type="button" data-container="body" data-toggle="popover" data-trigger="hover" data-placement="top" data-html="true" title="<%= work.nom_travaux %>" data-content="travaux : <%= work.travaux %><br />
Cout travaux : <%= work.cout_travaux %>€<br />
TVA : <%= work.tva_travaux %>%"><%= work.nom_travaux %></button>
<% end %>
The idea is to add popover on the image.
Someone has an idea where comes from the issue ?
Thank you very much !
HTML <img> tags cannot have children, so this doesn't make sense.
To add popups, you need some JavaScript (or very nifty CSS with hidden checkboxes). If you don't want to write that yourself, use something like Bootstrap popups.
i want to use a common partial to render a listing page and its going good expect i am facing a problem to generate dynamic link_to for :edit and :delete action which is common.
so i have #menu and #picture model in two different views rendering the same common partial (which i created).
this is my common partial.
shared/_index_common_grid.html.erb
<% pictures.each do |picture| %>
<div class="col-sm-4 pull-left">
<div class="thumbnail">
<div class="caption">
<h4><%= picture.title.capitalize%></h4>
<p class="text-muted">
<!--THIS IS WHERE I WANT TO HANDLE DYNAMIC GENERATION OF LINK_TO FOR #picture and #menu models -->
<%= link_to "Edit", [:edit, current_user,#request,#shop,picture]%>
| <%= link_to "Delete", [current_user,#request,#shop,picture],:data=>{:confirm=>"Are you sure ?"}%>
| <span class="pull-right"><%= show_formatted_date(picture.created_at)%></span>
</p>
</div>
</div>
</div>
<%end%>
this is my one view using above common partial,there is one more similar to this except i pass different model.
##my view page --------pictures/index
<%unless #pictures.blank?%>
<%= render partial: "shared/index_common_grid", locals: {pictures: #pictures}%>
<%end%>
I dont want to go with switch case in application_helper which can be done easily.
You can use layouts with your partials. See the API docs for more details.
Rendering pictures with layouts
First, rewrite pictures/index.html.erb to:
<%= render partial: 'picture', layout: 'shared/index_common_grid', collection: #pictures, as: :object %>
Second, rewrite shared/index_common_grid.html.erb to:
<div class="col-sm-4 pull-left">
<div class="thumbnail">
<div class="caption">
<h4><%= object.title.capitalize%></h4>
<p class="text-muted">
<%= yield %> | <span class="pull-right"><%= show_formatted_date(object.created_at)%></span>
</p>
</div>
</div>
</div>
Thrid, create pictures/_picture.html.erb:
<%= link_to "Edit", [:edit, current_user, #request, #shop, picture]%>
| <%= link_to "Delete", [current_user, #request, #shop, picture], :data=>{:confirm=>"Are you sure ?"}%>
Adding other types of objects
If you want to render other types of objects (menus in your example) then:
Add menus/index.html.erb with a content similar to pictures/index.html.erb (replace picture and #pictures with menu and menus).
Add menus/_menu.html.erb with links rendered appropriately for menus.
I have a pair of very similar views that render almost identical information, only in one view there are couple of extra columns and in the other the rows link slightly different nested resources. My initial approach was to keep it DRY by using a partial and then placing conditionals throughout the view. The resulting partial looked something like this:
<div id='overview_table'>
<div id="overview_header">
<span id="sort_title" class="title cell">Title<span id="publication_sort_arrow"> ↓</span></span>
<span id="sort_author" class="author cell">Author</span>
<span id="sort_status" class="status cell">Status</span>
<% if #user.present? %>
<span id="sort_impression_date" class="date cell">Date</span>
<span id="sort_impression_vote" class="votes cell">Votes</span>
<span id="sort_children_total" class="children_total cell">Replies</span>
<% end %>
</div>
<span id="sort_method">title ASC</span>
<% #publications.each do |publication| %>
<div class='<%= cycle("odd", "even") %>'>
<% if #user.present? %>
<% link = [#user, publication] %>
<% else %>
<% link = [#group, publication] %>
<% end %>
<%= link_to(link, :remote => true) do %>
<span class="title cell"><%= publication.full_title %></span>
<span class="author cell"><%= publication.authors %></span>
<span class="status cell"><%= publication_status(publication.status) %></span>
<% if #user.present? %>
<span class="date cell"><% if publication.impression_date %><%= publication.impression_date.strftime("%B %d, %Y") %><% end %></span>
<span class="votes cell"><% if publication.impression_vote %><%= publication.impression_vote.to_i %><% end %></span>
<span class="children_total cell"><% if publication.impression_vote %><%= publication.children_total %><% end %></span>
<% end %>
<% end %>
</div>
<% end %>
It worked fine, but the code felt hacky. I ultimately separated these back out into the two different views, though now there's a lot of repeated code. Both approaches feel inadequate. Is there another approach that I'm not considering?
There are different strategies here but in this case if you are just adding some fields, I would do something like this (which is similar to what you are doing).
in my controller I'll set some tag value to true:
#show_val_extra=true
and in my view(probably be a partial so rather than inline code in your example):
<%="something here" unless #show_val_extra.nil? %>
No matter what you are going to have to check and other issues of managing the view in the controller are ugly to me. YMMV but this is what I'd do since it basically makes it to a single value and a single check for when you want different information. Usually, it's in multiple places but you have content in multiple places and a further refactor is easy if the situation arises.