Rails 5.1: How to override allowAction in rails-ujs to use a custom confirmation dialog

2.5k views Asked by At

In versions of Rails previous to 5.1, which used jquery_ujs, we could replace the browser's confirmation popup with our own by overriding $.rails.allowAction, as explained here.

As of Rails 5.1+, which uses rails-ujs, $.rails.allowAction is no longer available. How can we override Rails' default confirmation with our own in Rails 5 without having to switch back to jquery_ujs?

Thanks in advance.

3

There are 3 answers

0
peterfication On BEST ANSWER

I had the same challenge and I looked a little bit more into it. What I found out along the way can be read here: https://medium.com/store2be-tech/how-to-use-sweetalert2-for-your-rails-5-1-rails-ujs-confirms-without-jquery-8a5b516b2a1

Here the final solution:

(function() {
  var handleConfirm = function(element) {
    if (!allowAction(this)) {
      Rails.stopEverything(element)
    }
  }

  var allowAction = function(element) {
    if (element.getAttribute('data-confirm-swal') === null) {
      return true
    }

    showConfirmationDialog(element)
    return false
  }

  // Display the confirmation dialog
  var showConfirmationDialog = function(element) {
    var message = element.getAttribute('data-confirm-swal')
    var text = element.getAttribute('data-text')

    swal({
      title: message || 'Are you sure?',
      text: text || '',
      type: 'warning',
      showCancelButton: true,
      confirmButtonText: 'Yes',
      cancelButtonText: 'Cancel',
    }).then(function(result) {
      confirmed(element, result)
    })
  }

  var confirmed = function(element, result) {
    if (result.value) {
      // User clicked confirm button
      element.removeAttribute('data-confirm-swal')
      element.click()
    }
  }

  // Hook the event before the other rails events so it works togeter
  // with `method: :delete`.
  // See https://github.com/rails/rails/blob/master/actionview/app/assets/javascripts/rails-ujs/start.coffee#L69
  document.addEventListener('rails:attachBindings', function(e) {
    Rails.delegate(document, 'a[data-confirm-swal]', 'click', handleConfirm)
  })

}).call(this)
1
localhostdotdev On

You can override it with Rails.confirm, e.g. with CoffeeScript:

Rails.confirm = (message, element) ->
  # your code

e.g. to have the confirm text show up for 2 seconds:

WAITING_CLASS = "waiting-for-confirmation"
TIMEOUT = 2000

Rails.confirm = (message, element) ->
  if element.classList.contains(WAITING_CLASS)
    true
  else
    element.dataset.beforeConfirm = element.textContent
    element.textContent = element.dataset.confirm
    element.classList.add(WAITING_CLASS)

    timeout TIMEOUT, ->
      element.classList.remove(WAITING_CLASS)
      element.textContent = element.dataset.beforeConfirm

    false

See: https://github.com/rails/rails/blob/master/actionview/app/assets/javascripts/rails-ujs/features/confirm.coffee

And timeout and just a simple function that inverses the setTimeout parameters:

var timeout = function(time, callback) {
  setTimeout(callback, time)
}
0
M. Stavnycha On

I haven't found a beautiful way to tweak into rails_ujs, so I came with with this workaround (using CoffeeScript):

```

$(document).on 'mousedown', 'a[data-confirm]', (e) ->
  e.preventDefault()
  link = $(e.target)

  message = link.data 'confirm'
  modal = $('.modal.confirmation')
  modal.find('.content').text(message)
  approve = modal.find('.approve')
  approve.attr('data-method', link.data('method'))
  approve.attr('href', link.attr('href'))

  modal.modal('show')

```

Mousedown event allows my event handler to be executed first (it goes before click event, which rails_ujs uses)