How to call confirm prompt using button_to in Rails with Turbo - ruby-on-rails

Previously in Rails when using the button_to tag, it was possible to use a confirmation dialog like this
<%= button_to 'Destroy', #post, method: :delete, data: { confirm: 'Are you sure?' } %>
data: { confirm: 'Are you sure?' } is Rails magic data attribute that was used by #rails/ujs library under the hood
Following Rails 7, this library is no longer on by default. Instead of this Rails use Turbo library
And now this code does not work
There is no information in official Rails docs and Turbo handbook
What I tried
<%= button_to 'Destroy', #post, method: :delete, data: { turbo_confirm: 'Are you sure?' } %>
<%= button_to 'Destroy', #post, method: :delete, data: { 'turbo-confirm': 'Are you sure?' } %>
But there is no result
I didn't find any solution on SO but found on Hotwire forum. This solution with Stimulus action. I just improve it a little
<%= form_with model: #post, method: :delete, data: { controller: 'confirmation', message: 'Are you sure?', action: 'submit->confirmation#confirm' } do |f| %>
<%= f.submit 'Destroy' %>
<% end %>
// app/javascript/confirmation_controller.js
import { Controller } from '#hotwired/stimulus'
export default class extends Controller {
confirm(event) {
if (!(window.confirm(this.element.dataset.message))) {
event.preventDefault()
}
}
}
It works but it's quite difficult and looks ugly, and we are used to Rails being cool

In Rails with Turbo without rails-ujs to call confirmation popup window with button_to we need to use code like this
<%= button_to 'Destroy', #post, method: :delete, form: { data: { turbo_confirm: 'Are you sure?' } } %>
or
<%= button_to 'Destroy', #post, method: :delete, form: { data: { 'turbo-confirm': 'Are you sure?' } } %>
Both generate data-turbo-confirm attribute
So we need to add this attribute not to submit button (like in rails-ujs) but directly to form containing this button (let me remind you this tag generates a form with button)

This is a bit complicated in 7.0.3, if it is a page that is using turbo it looks like this:
<%= button_to "Delete",
user_path(user),
method: :delete,
class: "button tight danger",
form: {
data: {
turbo_confirm: "Are you sure you want delete?"
}
} %>
This makes a little form. Now if you are using turbo, but not on that specific page, you no longer get the simple comfirm: 'message' from the old rails ujs. Instead you have to use stimulus controllers.
# app/javascript/controllers/confirmation_controller.js
import { Controller } from "#hotwired/stimulus"
export default class extends Controller {
static values = { message: String };
confirm(event) {
if (!(window.confirm(this.messageValue))) {
event.preventDefault();
event.stopImmediatePropagation();
};
};
}
then
# app/javascript/controllers/index.js
import ConfirmationController from "./confirmation_controller"
application.register("confirmation", ConfirmationController)
then
<%= button_to "Delete",
user_path(user),
method: :delete,
class: "button danger",
form: {
data: {
turbo: false,
controller: "confirmation",
action: 'submit->confirmation#confirm',
confirmation_message_value: "Are you sure you want to delete?",
}
} %>
It is a bummer that rails removed functionality, but if you want to use hotwire you need to commit to full buy in to the whole ecosystem.

I'm not fully understanding this, but the following worked for me
<%= button_to post_path(post), method: :delete, form: { data: { turbo_confirm: 'Are you sure?' } } do %>
an svg which shows a trash can
<% end %>.
I also don't have anything that looks like the OP's confirmation_controller

not the original question, but as of rails 7.0.4, it would work the same (stimulus-free) :
<%= link_to("Del",
del_path,
data: { turbo_method: :delete, turbo_confirm: "Sure?" }) %>

Related

Rails 7.0.4 and data confirm for link_to doesn't work

My last chance is some last living developer in RoR
Nothing in the darkest corners of the internet works for:
<%= link_to 'some-link', '#', data: { confirm:'Are You Sure?') %>;
it generate this:
<a data-confirm="Are you sure?" href="#">some link</a>
but there is no confirm dialog box.
I try everything - turbo_confirm, try to add controller with class for stimulus ... and still nothing.
Right now I remove turbo-rails, stimulus etc. - but still nothing.
data-turbo-confirm presents a confirm dialog with the given value. Can
be used on form elements or links with data-turbo-method.
https://turbo.hotwired.dev/reference/attributes
Examples that work
<%= link_to "GET turbo link", "/",
data: {
turbo_method: :get,
turbo_confirm: "Sure?"
}
%>
<%= link_to "POST turbo link", "/",
data: {
turbo_method: :post,
turbo_confirm: "Sure?"
}
%>
<%= button_to "Button", "/",
data: { turbo_confirm: "Sure?" }
%>
<%= form_with url: "/",
data: { turbo_confirm: "Sure?" } do |f| %>
<%= f.submit "Submit" %>
<% end %>
Doesn't work
<%= link_to "GET link", "/",
data: { turbo_confirm: "Sure?" }
%>
Fix by adding turbo_method: :get.
Really doesn't work
<%= link_to "UJS link", "/",
data: { confirm: "Sure?" }
%>
But easy to fix:
// app/javascript/application.js
document.addEventListener("click", event => {
const element = event.target.closest("[data-confirm]")
if (element && !confirm(element.dataset.confirm)) {
event.preventDefault()
}
})
data-disable-with
This one is a bit more involved, but it's all the same pattern, listen for event, check for data attribute, if else, do something.
// app/javascript/application.js
// on form submit
// set `value` if `input`
// set `innerHTML` if `button`
document.addEventListener("turbo:submit-start", event => {
const element = event.detail.formSubmission.submitter
if (element.hasAttribute("data-disable-with")) {
// element.disabled = true // this is done by turbo by default
if (element instanceof HTMLInputElement) {
element.value = element.dataset.disableWith
} else {
element.innerHTML = element.dataset.disableWith
}
}
})
<%= form_with url: "/" do |f| %>
# <button>
<%= f.button "Submit", data: { disable_with: "..." } %>
# <input>
<%= f.submit "Submit", data: { disable_with: "..." } %>
<% end %>

How to put two links in one line in HAML?

I want to put two links next to each other in the same line with a space in between.
The following syntax is giving error. I appreciate any help! Thanks!
%p= link_to(h.title, h) link_to("Delete", homework_path(h), method: :delete, data: {confirm: "Are you sure?"})
Also how can i make a text and then link appear in the same line. I tried
%p Title: =link_to(h.title, h)
Put your links on seperate lines, nested inside the %p:
%p
= link_to(h.title, h)
= link_to("Delete", homework_path(h), method: :delete, data: {confirm: "Are you sure?"})
You can use the succeed helper to add white space after the first one:
%p
= succeed ' ' do
= link_to(h.title, h)
= link_to("Delete", homework_path(h), method: :delete, data: {confirm: "Are you sure?"})

form_for > f.submit, How to only alert at update action.

How would one set this up so it only pop's up at update action and not create?
= f.submit, data: { confirm: "you are about to update object, are you sure?" }
Try something like this
= f.submit, data: action == 'edit' ? { confirm: "you are about to update object, are you sure?" } : {}

How to make the rails link_to include other tags

i'm creating a like model, so here's a code:
- if policy(bonus).liked_by?
= link_to(image_tag("heart--filled--green.png", class: "Dislike"),
bonus_like_path(bonus, bonus.user_like(current_user)), method: :delete,
data: { remote: true, behavior: "fragments" })
- else
= link_to(image_tag("heart.svg", class: "Like"),
bonus_likes_path(bonus), method: :post,
data: { remote: true, behavior: "fragments" })
- if bonus.likes_count.zero?
span Like
-else
span.has-tip data-tooltip="" title="#{ bonus.liked_by }" Like
span class="like_count" #{ bonus.likes_count }
And it generates something like this:
The problem is that if I want to like something, I would like to press on the heart(like the given image), but I need to give the opportunity to press everywhere including the span Like and like's count. How can I solve my problem?
To make the image along with the spans part of the link, wrap them inside link_to using a block.
= link_to bonus_likes_path(bonus), method: :post, data: { remote: true, behavior: "fragments" } do
= image_tag("heart.svg", class: "Like"
- if bonus.likes_count.zero?
span Like
- else
span.has-tip data-tooltip="" title="#{ bonus.liked_by }" Like
span class="like_count" #{ bonus.likes_count }
You can have a bigger block in link_to using:
<%= link_to desired_path do %>
<div class="class-name">
</div>
<% end %>

Rails call a method from within a different view

I am trying to call a method of one controller from within a different view.
I saw the following post Rails call destroy method from within another controller, but I am doing something wrong with my implementation.
The method I am trying to call is in an accessor controller.
def remove_permission_from_index
Accessor.find(params[:id]).destroy
respond_to do |format|
format.html { redirect_to tasks_url }
format.json { head :no_content }
end
end
This controller also has the following strong param method
def accessor_params
params.require(:accessor).permit(:accessor_id, :access_rights, :task_id)
end
My view is calling the following code
<%= link_to 'Delete', {:controller => "accessors", :accessor => elem} ,method: :remove_permission_from_index, data: { confirm: "Are you sure?" }%> </p>
where elem an element within a set that is defined in the task controller
#canEdit = Accessor.select(:task_id).where("accessor_id = ? AND access_rights = ?", current_user, true)
#canEdit.each do |p|
p.task = Task.find(p.task_id)
When I am running my code I am getting the following error
param not found: accessor
def accessor_params
params.require(:accessor).permit(:accessor_id, :access_rights, :task_id)
end
end
and I can see that the accessor param is not being passed :
Parameters:
{"_method"=>"remove_permission_from_index"}
a) why is the accessor_params is being called if I am trying to access a different method
b) what am I doing wrong? After all I do set :accessor => elem
Could be a copy paste error but:
<%= link_to 'Delete', {:controller => "accessors", :accessor => elem} ,method: :remove_permission_from_index, data: { confirm: "Are you sure?" }%> </p>
should be more like:
<%= link_to 'Delete', {:controller => "accessors", :action => :remove_permission_from_index, :accessor => elem}, method: :delete, data: { confirm: "Are you sure?" }%> </p>

Resources