用于Rails 3数据确认属性的jQuery UI Dialog而不是alert()

时间:2010-12-12 09:49:59

标签: javascript jquery ruby-on-rails unobtrusive-javascript

在Rails 3中,将:confirm参数传递给link_to将填充链接的data-confirm属性。这将在单击链接时引发JS alert()。

我正在使用rails 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对话框(例如the jQuery UI Dialog),允许用户确认或取消。

我对JavaScript的了解不足以实现这一目标。我目前的方法是简单地重写$('body')。delegate()函数来实例化灯箱。但是,我认为有一种比这更有效的方法。

6 个答案:

答案 0 :(得分:15)

正如其他人所提到的,你不能使用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,从而有效地取消点击事件。但是,您的自定义函数可以调用回调来实际跟随链接/提交表单。

答案 1 :(得分:13)

我刚刚在Rails jquery-ujs中添加了一个外部API,以实现这种自定义。您现在可以通过插入(并重写1行)$.rails.allowAction函数来使rails.js使用自定义确认对话框。

请参阅我的文章Rails jQuery UJS: Now Interactive,获取有关示例的完整说明。

编辑:从this commit开始,我将confirm对话框函数移动到$.rails对象,以便现在可以更轻松地修改或交换它。 E.g。

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

答案 2 :(得分:1)

我喜欢@MarcSchütz关于覆盖$.rails.allowAction我在网上找到的大部分内容的答案 - 但我不是在allowAction覆盖功能的忠实粉丝,因为它在整个过程中都被使用了jquery-ujs代码库(如果有副作用怎么办?或者如果该方法的源在未来的更新中发生变化?)。

到目前为止,最好的做法是让$.rails.confirm返回承诺......但是it doesn't look like that's going to happen anytime soon :(

所以......我推出了自己的方法,我觉得值得一提,因为它比上面提到的方法重量更轻。它没有劫持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)

它是如何工作的?好吧,如果您查看source,您会注意到allowAction方法会在confirm event返回假值时暂停。所以流程是:

  1. 用户点击带有data-confirm属性的链接或按钮。链接或按钮上没有data-confirmed,因此我们会进入第一个if块,触发我们的自定义模式并返回false,从而停止在ujs单击处理程序中继续操作。
  2. 用户在自定义模式中确认,并触发回调。我们通过data('confirmed', true)在元素上存储状态,并重新触发先前触发的相同事件(click.rails)。
  3. 这次confirm event会落入else区块(因为data('confirmed')是真的)并返回true,导致allowAction块评估为true。
  4. 我确信我甚至错过了其他可能使这更简单的方法,但我认为这是一种非常灵活的方法,可以在不破坏核心jquery-ujs功能的情况下获得自定义确认模式。

    (另外,因为我们正在使用.on(),这将在加载时或将来绑定到页面上的任何data-confirm元素,类似于.delegate()的工作方式,以防万一你在想。)

答案 3 :(得分:0)

我不明白为什么当JavaScript confirm()函数仍能正常工作时你需要使用jQuery对话框。我会做这样的事情:

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

如果你想使用对话框,那就有点不同了。您可以一次性关闭所需的每个对话框,或者您可以在应用程序范围内采用统一的方法,以便rails.js或您的application.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();
})

如果您想稍微自定义对话框,请查看this example

修改

现在我想到了,这对于自定义表单构建器来说是一个很好的机会。您可以覆盖一个Rails链接标记,以输出类似于上面列出的内容的html,只要存在某个属性,即:dialog => true。当然,那将是Railsy这样做的方式。您也可以在标记中添加其他选项,例如对话框标题等。

修改

更好的是,而不是:dialog => true,而不是像往常一样使用:confirm => "my confirm message",但是在覆盖link_to时,您将使用:confirm选项来创建jQuery的对话框html需要,删除该选项,然后调用super

答案 4 :(得分:0)

这就是我开始工作的方式。请提出任何更正/改进建议

在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 :(得分:0)

这就是我解决此问题的方法。 我尝试了很多不同的方法,但是只有一种方法有效。

在rails.js中

function myCustomConfirmBox(element, callback) {

    const modalConfirmDestroy = document.getElementById('modal-confirm');

    // wire up cancel
    $("#modal-confirm #cancel-delete").click(function (e) {
        e.preventDefault();
        modalConfirmDestroy.classList.remove('modal--open');
    });

    // wire up OK button.
    $("#modal-confirm #confirm-delete").click(function (e) {
        e.preventDefault();
        modalConfirmDestroy.classList.remove('modal--open');
        callback(element, true);
    });

    // show the dialog.
    modalConfirmDestroy.classList.add('modal--open');
}

在这个地方,我使用@Mark G.的代码进行了一些更改。因为这段$(this).trigger('click.rails')代码片段对我不起作用。

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

$(document).on('confirm', '[data-confirm]', (event)=> {
    if (!$(this).data('confirmed'))
    {
        myCustomConfirmBox($(this), (element, choice)=> {
            element.data('confirmed', choice);
            let clickedElement = document.getElementById(event.target.id);
            clickedElement.click();
        });
        return false;
    }
    else
    {
        return true;
    }
});

然后在html.erb文件中,我具有以下链接代码:

<%= link_to "documents/#{document.id}", method: "delete", data: {confirm: "sure?"}, id: "document_#{document.id}" %>

以及此模式代码:

<div id="modal-confirm" class="modal modal-confirm">
  <h2 class="modal__ttl">Title</h2>
  <div class="modal__inner">
    <p>Description</p>
    <div class="modal__btns">
      <button type="button" name="cancel" id="cancel-delete" class="btn btn-primary">Cancel</button>
      <button type="button" name="confirm" id="confirm-delete" class="btn delete_button btn-secondary">Delete</button>
    </div>
  </div>
</div>

我希望它将对某人有所帮助。