javascript 用于 Rails 3 数据确认属性的 jQuery UI 对话框而不是 alert()
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 
原文地址: http://stackoverflow.com/questions/4421072/
Warning: these are provided under cc-by-sa 4.0 license.  You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
jQuery UI Dialog instead of alert() for Rails 3 data-confirm attribute
提问by Sai Perchard
In Rails 3, passing a :confirm parameter to link_to will populate the data-confirm attribute of the link. This will induce a JS alert() when the link is clicked.
在 Rails 3 中,将 :confirm 参数传递给 link_to 将填充链接的 data-confirm 属性。这将在单击链接时引发 JS alert()。
I am using the rails jQuery UJS adapter (https://github.com/rails/jquery-ujs). The relevant code from rails.js is:
我正在使用 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;
        }
    }
});
and
和
triggerAndReturn: function (name, data) {
        var event = new $.Event(name);
        this.trigger(event, data);
        return event.result !== false;
    }
I would like to know how this could be modified to instead yield a jQuery dialog (e.g. the jQuery UI Dialog) allowing the user to confirm or cancel.
我想知道如何修改它以生成一个 jQuery 对话框(例如jQuery UI Dialog),允许用户确认或取消。
My knowledge of JavaScript isn't sufficient to achieve this elegantly. My current approach would be to simply rewrite the $('body').delegate() function to instead instantiate a lightbox. However I imagine that there is a more effective approach than this.
我对 JavaScript 的了解不足以优雅地实现这一点。我目前的方法是简单地重写 $('body').delegate() 函数来实例化一个灯箱。但是我想还有比这更有效的方法。
回答by Marc Schütz
As others have mentioned, you cannot use a jQuery dialog box, as $.rails.confirmneeds to block until it returns the users answer.
正如其他人所提到的,您不能使用 jQuery 对话框,因为$.rails.confirm需要阻止直到它返回用户答案。
However, you can overwrite $.rails.allowActionin your application.jsfile like this:
但是,您可以覆盖$.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
}
It works by returning falseimmediately, thus effectively canceling the click event. However, your custom function can then call the callback to actually follow the link/submit the form.
它的工作原理是false立即返回,从而有效地取消点击事件。但是,您的自定义函数然后可以调用回调以实际跟随链接/提交表单。
回答by jangosteve
I just added an external API to the Rails jquery-ujs for exactly this kind of customization. You can now make rails.js use a custom confirm dialog by plugging into (and re-writing 1 line of) the $.rails.allowActionfunction.
我刚刚为 Rails jquery-ujs 添加了一个外部 API,以实现这种自定义。您现在可以通过插入(并重写 1 行)$.rails.allowAction函数使 rails.js 使用自定义确认对话框。
See my article, Rails jQuery UJS: Now Interactive, for a full explanation with examples.
请参阅我的文章Rails jQuery UJS:Now Interactive以获取完整的示例说明。
EDIT: As of this commit, I moved the confirmdialog function to the $.railsobject, so that it can be modified or swapped out even more easily now. E.g.
编辑:截至本次提交,我将confirm对话功能移至$.rails对象,以便现在可以更轻松地对其进行修改或换出。例如
$.rails.confirm = function(message) { return myConfirmDialog(message); };
回答by Mark G.
I liked the answer from @Marc Schütz about overriding $.rails.allowActionthe most of anything I found online - but I'm not a big fan of overriding the functionality in allowActionsince it's used all throughout the jquery-ujs codebase (what if there are side effects? Or if the source for that method changes in  a future update?).  
我喜欢@Marc Schütz 关于覆盖$.rails.allowAction我在网上找到的大部分内容的答案- 但我不是覆盖功能的忠实粉丝,allowAction因为它在整个 jquery-ujs 代码库中都被使用(如果有副作用怎么办?或者如果该方法的来源在未来的更新中发生变化?)。  
By far, the best approach would be to make $.rails.confirmreturn a promise... But it doesn't look like that's going to happen anytime soon:(
到目前为止,最好的方法是让$.rails.confirmreturn 成为一个承诺......但看起来这不会很快发生:(
So... I rolled my own method which I think is worth mentioning because it's lighter weight than the method outlined above.  It doesn't hiHyman allowAction.  Here it is:
所以......我推出了我自己的方法,我认为值得一提,因为它比上面概述的方法更轻。它不会劫持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)
How does it work?  Well, if you look at the source, you'll notice that the allowActionmethod halts if the confirm eventreturns a falsy value.  So the flow is:
它是如何工作的?好吧,如果您查看源代码,您会注意到allowAction如果confirm event返回假值,该方法将停止。所以流程是:
- User clicks link or button with 
data-confirmattribute. There is nodata-confirmedpresent on the link or button, so we fall into the first if block, trigger our custom modal and return false, thereby stopping the action from continuing in the ujs click handler. - User confirms in the custom modal, and the callback is triggered.  We store state on the element via 
data('confirmed', true)and re-trigger the same event that was triggered previously (click.rails). - This time the 
confirm eventwill fall into theelseblock (sincedata('confirmed')is truthy) and return true, causing theallowActionblock to evaluate to true. 
- 用户单击具有
data-confirm属性的链接或按钮。data-confirmed链接或按钮上没有存在,因此我们陷入第一个 if 块,触发我们的自定义模式并返回 false,从而阻止动作在 ujs 单击处理程序中继续。 - 用户在自定义模态中确认,并触发回调。我们通过在元素上存储状态
data('confirmed', true)并重新触发之前触发的相同事件(click.rails)。 - 这次
confirm event将落入else块中(因为data('confirmed')是真)并返回真,导致allowAction块评估为真。 
I'm sure I'm even missing other ways that might make this even simpler, but I think this is a really flexible approach to get a custom confirm modal without breaking core jquery-ujsfunctionality.
我敢肯定我什至缺少其他可能使这更简单的方法,但我认为这是一种非常灵活的方法,可以在不破坏核心jquery-ujs功能的情况下获得自定义确认模式。
(Also, because we're using .on()this will bind to any data-confirmelements on the page at load time or in the future, similarly to how .delegate()works, in case you are wondering.)
(另外,因为我们使用.on()this 将data-confirm在加载时或将来绑定到页面上的任何元素,与.delegate()工作原理类似,以防万一。)
回答by Roman Malkevych
This is how I solved this problem. I tried a lot of different ways, but only this one works.
这就是我解决这个问题的方法。我尝试了很多不同的方法,但只有这一种有效。
In rails.js
在 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');
}
In this place I used code of @Mark G. with some changes. Because this $(this).trigger('click.rails') snipped of the code didn't work for me.
在这个地方,我使用了@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;
    }
});
Then in the html.erb file I have this code for link:
然后在 html.erb 文件中,我有这个链接代码:
<%= link_to "documents/#{document.id}", method: "delete", data: {confirm: "sure?"}, id: "document_#{document.id}" %>
and this code for modal:
和这个模态代码:
<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>
I hope, it will help someone.
我希望,它会帮助某人。
回答by Samo
I don't understand why you need to use the jQuery dialog when the JavaScript confirm() function will still work just fine. I would do something like this:
我不明白为什么当 JavaScript 确认()函数仍然可以正常工作时,你需要使用 jQuery 对话框。我会做这样的事情:
$('a[data-confirm]').click(funciton() {
  confirm($(this).data("confirm"));
});
If you want to use a dialog instead, it's a little different. You can one-off each dialog you want, or you can probably take a uniform approach application wide so that your rails.js or your application.js can handle any dialog instance. For example, you'd need something like this on your page:
如果你想改用对话框,那就有点不同了。您可以一次性处理您想要的每个对话框,或者您可以在应用程序范围内采用统一的方法,以便您的 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>
Then, in your js:
然后,在你的 js 中:
$('.dialogLauncher').click(function() {
  var dialog = $(this).next('.dialog');
  dialog.dialog();
})
If you want to customize your dialog a little more, check out this example.
如果您想更多地自定义对话框,请查看此示例。
Edit
编辑
Now that I think of it, this would be a good opportunity for a custom form builder. You could override one of your Rails link tags to output html similar to what's listed above whenever a certain attribute is present, i.e. :dialog => true. Surely that would be the Railsy way to do it. You could add other options into your tag as well, like the dialog title, etc.
现在我想到了,这对于自定义表单构建器来说是一个很好的机会。您可以覆盖您的 Rails 链接标签之一,以在存在某个属性时输出类似于上面列出的 html,即:dialog => true. 当然,这将是 Railsy 的方式来做到这一点。您也可以在标签中添加其他选项,例如对话框标题等。
Edit
编辑
Better yet, instead of :dialog => true, use :confirm => "my confirm message"just as you would normally, but in your override of link_to, you will use the :confirmoption to create the dialog html that jQuery needs, delete that option, and then call super.
更好的是,不是像通常那样:dialog => true使用:confirm => "my confirm message",但是在覆盖 link_to 时,您将使用该:confirm选项来创建 jQuery 需要的对话框 html,删除该选项,然后调用super.
回答by Mohith Thimmaiah
This is how I got it to work. Please suggest any corrections / improvements
这就是我让它工作的方式。请提出任何更正/改进建议
#in rails.js
在 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;
        }
    });
#
in application.js
在 application.js 中
#//Ensure confirm Dialog is pre-created
jQuery(function () {
    $( "#dialog-confirm" ).dialog({
        autoOpen: false,
        resizable: false,
        height:140,
        modal: true     
    });
});
#
in layout.html Alt you can place this div anywhere in your generated html
在 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> 

