最近在寫的元件有個「彈出對話框經使用者確認再刪除」的需求,原本是小事一椿,但之前介紹過使用自訂確認對話框取代window.confirm的技巧已廣泛應用在專案裡,某些時候也可能只用window.confirm就打發,問題就變複雜了。

二者最大的差異是前者($.kendoConfirm)為非同步執行,刪除動作需放在jQuery Deferred.promise()的done()方法;window.confirm則為內建同步指令,瀏覽器會等待使用者回應才往下執行,故用 if 判斷 true/false 決定是否刪除即可。

以下程式可看出兩種做法的差異:線上展示 (關於 $.kendoConfirm 的說明請參考舊文

    $(function() {
      function deleteConfirm_Kendo() {
        return $.kendoConfirm("刪除確認", "是否確定要刪除?");
      }
      function deleteConfirm_Native() {
        return confirm("是否確定要刪除?");
      }
      
      $("#btnTest1").click(function() {
        deleteConfirm_Kendo().done(function() {
          alert("刪除");
        });
      });
      $("#btnTest2").click(function() {
        if (deleteConfirm_Native()) {
          alert("刪除");
        }
      });
    });

要讓元件好用,最理想的做法是大小通吃:若確認函式傳回true/false就用 if 同步執行;若傳回jQuery Promise就用.done()非同步執行。

例如:線上展示

    $(function() {
      function deleteConfirm_Kendo() {
        return $.kendoConfirm("刪除確認", "是否確定要刪除?");
      }
      function deleteConfirm_Native() {
        return confirm("是否確定要刪除?");
      }
      function execDeleteAction() {
        alert("刪除");
      }
      function doDelete(deleteConfirm) {
        var res = deleteConfirm();
        //若函式傳回Promise, 放入done()中執行
        if (res && res.hasOwnProperty("done")) 
          res.done(execDeleteAction);
        //若函式傳回boolean,由傳回結果決定resolve或reject
        else if (res) 
          execDeleteAction();
      }
      $("#btnTest1").click(function() {
        doDelete(deleteConfirm_Kendo);
      });
      $("#btnTest2").click(function() {
        doDelete(deleteConfirm_Native);
      });
    });

以上寫法有個小缺點,執行刪除的部分必須抽出來變成函式,分於兩處呼叫。

針對這個缺點,額外加個jQuery.Deferred串場就能克服:線上展示

      function doDelete(deleteConfirm) {
        var dfd = jQuery.Deferred();
        var promise = dfd.promise();
        var res = deleteConfirm();
        //若函式傳回Promise, 替換原有Promise
        if (res && res.hasOwnProperty("done")) 
          promise = res;
        //若函式傳回boolean,由傳回結果決定resolve或reject
        else if (res) 
          dfd.resolve();
        else 
          dfd.reject();
        promise.done(function() {
          alert("刪除")
        });
      }

兩種做法都能讓刪除確認程序通吃window.confirm及jQuery.Deferred,擇一而用即可,以上提供大家參考。


Comments

# by Ammon

何不直接在 deleteConfirm_Native 回傳 deferred 物件? 可以省下if判斷

# by Jeffrey

to Ammon, 在我的案例中,deleteConfirm_Native相當於呼叫端提供的Callback參數,對於從來沒學過jQuery Deferred的開發者,Callback函式只需return true或false,降低元件使用的門檻。

# by Peter Tsai

最後一個例子的 promise 物件會被 res 蓋掉而變成另一個函示回傳的 promise 物件,這兩個 promise 畢竟是在執行時期才進行判斷,反而增加程式複雜度,提升了使用者偵錯的門檻。為了省下一個 function 而改用 defer 技巧我是覺得有點 over design 了。 :p

Post a comment