Диалоговое окно JQuery UI вместо предупреждения() для атрибута подтверждения данных Rails 3

В Rails 3, передавая параметр: confirm в link_to, будет заполняться атрибут подтверждения данных ссылки. Это вызовет JS alert() при нажатии ссылки.

Я использую адаптер рельсов jQuery UJS (https://github.com/rails/jquery-ujs). Соответствующий код из rails.js:

$('body').delegate('a[data-confirm], button[data-confirm], input[data-confirm]', 'click.rails', function () {
    var el = $(this);
    if (el.triggerAndReturn('confirm')) {
        if (!confirm(el.attr('data-confirm'))) {
            return false;
        }
    }
});

и

triggerAndReturn: function (name, data) {
        var event = new $.Event(name);
        this.trigger(event, data);

        return event.result !== false;
    }

Я хотел бы знать, как это можно было бы изменить, чтобы вместо этого появилось диалоговое окно jQuery (например, диалоговое окно jQuery UI), позволяющее пользователю подтвердить или отменить.

Мое знание JavaScript недостаточно для достижения этого элегантно. Мой текущий подход состоял бы в том, чтобы просто переписать функцию $('body'). Delegate(), чтобы вместо этого создать лайтбокс. Однако я полагаю, что существует более эффективный подход, чем это.

Ответ 1

Я просто добавил внешний API для Rails jquery-ujs для именно такого рода настройки. Теперь вы можете сделать rails.js, используя специальный диалог подтверждения, подключив (и переписав 1 строку) функцию $.rails.allowAction.

См. мою статью, Rails jQuery UJS: Now Interactive, для полного объяснения примерами.

EDIT: с this commit, я переместил функцию диалога confirm в объект $.rails, чтобы его можно было изменить или обменивается теперь еще легче. Например.

$.rails.confirm = function(message) { return myConfirmDialog(message); };

Ответ 2

Как уже упоминалось, вы не можете использовать диалоговое окно jQuery, поскольку $.rails.confirm нужно заблокировать, пока он не вернет ответ пользователям.

Однако вы можете перезаписать $.rails.allowAction в файле application.js следующим образом:

$.rails.allowAction = function(element) {
        var message = element.data('confirm'),
        answer = false, callback;
        if (!message) { return true; }

        if ($.rails.fire(element, 'confirm')) {
                myCustomConfirmBox(message, function() {
                        callback = $.rails.fire(element,
                                'confirm:complete', [answer]);
                        if(callback) {
                                var oldAllowAction = $.rails.allowAction;
                                $.rails.allowAction = function() { return true; };
                                element.trigger('click');
                                $.rails.allowAction = oldAllowAction;
                        }
                });
        }
        return false;
}

function myCustomConfirmBox(message, callback) {
        // implement your own confirm box here
        // call callback() if the user says yes
}

Он работает, возвращая false немедленно, тем самым эффективно отменяя событие click. Тем не менее, ваша пользовательская функция может затем вызвать обратный вызов, чтобы фактически перейти по ссылке/отправить форму.

Ответ 3

Я не понимаю, почему вам нужно использовать диалог jQuery, когда функция JavaScript() будет по-прежнему работать нормально. Я бы сделал что-то вроде этого:

$('a[data-confirm]').click(funciton() {
  confirm($(this).data("confirm"));
});

Если вы хотите использовать диалоговое окно, это немного другое. Вы можете использовать одно диалоговое окно для каждого диалогового окна, или вы, вероятно, можете использовать приложение единого подхода, чтобы ваши rails.js или ваши приложения .js могли обрабатывать любой экземпляр диалога. Например, вам нужно что-то подобное на вашей странице:

<a class="dialogLauncher">The link that creates your dialog</a>
<div class="dialog" title="My confirmation title" style="display:none">
  <p>My confirmation message</p>
</div>

Затем в вашем js:

$('.dialogLauncher').click(function() {
  var dialog = $(this).next('.dialog');
  dialog.dialog();
})

Если вы хотите настроить свой диалог еще немного, просмотрите этот пример.

Edit

Теперь, когда я думаю об этом, это будет хорошей возможностью для создателя настраиваемой формы. Вы можете переопределить один из ваших тегов ссылок Rails для вывода html, аналогичного тому, что указано выше, когда присутствует какой-либо атрибут, т.е. :dialog => true. Конечно, это будет Railsy способ сделать это. Вы также можете добавить другие параметры в свой тег, например, заголовок диалога и т.д.

Edit

Еще лучше, вместо :dialog => true, используйте :confirm => "my confirm message" так же, как обычно, но в своем переопределении link_to вы будете использовать параметр :confirm для создания диалогового html, который нужен jQuery, удалить эту опцию, а затем вызовите super.

Ответ 4

Вот как я заработал. Пожалуйста, предложите любые исправления/улучшения

#

в rails.js

#
// Added new variable
var deleteConfirmed = false;

// Changed function to use jquery dialog instead of confirm   
$('body').delegate('a[data-confirm], button[data-confirm], input[data-confirm]', 'click.rails', function () {
        var el = $(this);
        /*
        if (el.triggerAndReturn('confirm')) {

            if (!confirm(el.attr('data-confirm'))) {
                return false;
            }

        }
        */

        if (el.triggerAndReturn('confirm')) {    

            if(deleteConfirmed) {
                deleteConfirmed = false;
                return true;
            }

            $( "#dialog-confirm" ).dialog("option", "buttons",
                    {
                        "Delete": function() {
                            $( this ).dialog( "close" );
                            deleteConfirmed = true;
                            el.trigger('click');   
                            return true;
                        },
                        Cancel: function() {
                            $( this ).dialog( "close" );
                            return false;
                        }
                    }
            );

            $( "#dialog-confirm" ).dialog("open");

            return false;

        }


    });
#

в application.js

#
//Ensure confirm Dialog is pre-created
jQuery(function () {


    $( "#dialog-confirm" ).dialog({
        autoOpen: false,
        resizable: false,
        height:140,
        modal: true     
    });

});
#

в layout.html Alt вы можете разместить этот div в любом месте вашего сгенерированного html

#
        <div id='dialog-confirm' title='Confirm Delete'> 
          <p> 
            <span class='ui-icon-alert' style='float:left; margin:0 7px 20px 0;'> 
              This item will be permanently deleted. Are you sure?
            </span> 
          </p> 
        </div> 

Ответ 5

Мне понравился ответ от @Marc Schütz о переопределении $.rails.allowAction большей части всего, что я нашел в Интернете, но я не большой поклонник переопределения функциональности в allowAction, поскольку он использовал все в кодовой базе jquery-ujs (что, если есть побочные эффекты, или если источник для этого метода изменяется в будущем обновлении?).

На сегодняшний день лучшим подходом было бы сделать $.rails.confirm вернуть обещание... Но не похоже, что это произойдет в ближайшее время: (

Итак... Я перекатил свой метод, который, как мне кажется, стоит упомянуть, потому что он легче, чем метод, описанный выше. Он не захватывает allowAction. Вот он:

# Nuke the default confirmation dialog. Always return true 
# since we don't want it blocking our custom modal.
$.rails.confirm = (message) -> true

# Hook into any data-confirm elements and pop a custom modal
$(document).on 'confirm', '[data-confirm]', ->
  if !$(this).data('confirmed')
    myCustomModal 'Are you sure?', $(this).data('confirm'), =>
      $(this).data('confirmed', true)
      $(this).trigger('click.rails')
    false
  else
    true

# myCustomModal is a function that takes (title, message, confirmCallback)

Как это работает? Ну, если вы посмотрите на источник , вы заметите, что метод allowAction останавливается, если confirm event возвращает значение falsy. Итак, поток:

  • Пользователь нажимает ссылку или кнопку с атрибутом data-confirm. На ссылке или кнопке нет data-confirmed, поэтому мы попадаем в первый блок if, запускаем наш собственный модальный и возвращаем false, тем самым останавливая действие от продолжения в обработчике ujs click.
  • Пользователь подтверждает в пользовательском модальном режиме, и обратный вызов запускается. Мы сохраняем состояние элемента через data('confirmed', true) и повторно запускаем то же событие, которое было инициировано ранее (click.rails).
  • В этот раз confirm event попадет в блок else (так как data('confirmed') является правдивым) и вернет true, в результате блок allowAction будет оцениваться как true.

Я уверен, что у меня даже отсутствуют другие способы, которые могли бы сделать это еще проще, но я думаю, что это очень гибкий подход для получения пользовательского подтверждения модальности без нарушения функциональности ядра jquery-ujs.

(Кроме того, поскольку мы используем .on(), это будет привязываться к любым элементам data-confirm на странице во время загрузки или в будущем, подобно тому, как работает .delegate(), если вы задаетесь вопросом.)