【问题标题】:Show loading message in bootbox while ajax process is running?ajax 进程运行时在引导箱中显示加载消息?
【发布时间】:2020-06-01 15:11:43
【问题描述】:

我的应用程序中有显示状态的页面。状态显示在下拉菜单中,有两个选项Active/Inactive。用户只需单击菜单并选择选项即可更改状态。对于那个过程,我使用 JQuery/Ajax onchange 触发器来跟踪状态变化。该代码工作正常并将值保存在数据库中。我遇到的问题与bootbox 和显示加载消息有关。当用户单击菜单并更改状态时,ajax 将发送请求。此时,对话框窗口将显示在用户屏幕上,并显示加载消息。一旦 ajax 请求完成,加载对话框应该隐藏并向用户显示成功保存状态的消息。下面是代码示例:

function alertBox(title, message, size) {
  title = title || "Alert";
  message = message || "Alert Box";
  size = size || "lg";

  bootbox.alert({
    size: size,
    title: title,
    message: message
  });
};

$('.status').on('change', function() {
  let $curr_fld = $(this),
    status_id = $curr_fld.attr('data-id'),
    dialog = bootbox.dialog({
      message: '<p class="text-center mb-0"><i class="fa fa-spin fa-cog"></i> Loading...</p>',
      closeButton: false
    });

  $curr_fld.prop("disabled", true);
  /*** Start: This is a code for testing. ***/
  /*
  setTimeout(function() {
    dialog.modal('hide');
    $curr_fld.prop("disabled", false);
    alertBox('Alert Message', status_id);
  }, 3000);
  */
  /*** End: This is a code for testing. ***/
  
  $.ajax({
    type: 'POST',
    url: 'test.php?id=1',
    data: {
      'status_id': status_id
    },
    dataType: 'json'
  }).done(function(obj) {
    dialog.modal('hide');
    $curr_fld.prop("disabled", false);
    if (obj.status === 200) {
      alertBox('Alert Message', obj);
    } else {
      alertBox('Alert Message', obj);
    }
  }).fail(function(jqXHR, textStatus, errorThrown) {
    $curr_fld.prop("disabled", false);
    alertBox('Error', textStatus);
  });

});
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/5.4.0/bootbox.min.js"></script>

<table class="table table-striped table-bordered" id="tbl_nofa">
  <thead>
    <tr class="bg-dark">
      <th class="text-center text-white">Section</th>
      <th class="text-center text-white">Status</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td class="text-center"> South</td>
      <td class="text-center">
        <select class="custom-select status" name="status_1" id="status_1" data-id="1">
          <option value="A" selected>Active</option>
          <option value="I">Inactive</option>
        </select>
      </td>
    </tr>
    <tr>
      <td class="text-center"> North</td>
      <td class="text-center">
        <select class="custom-select status" name="status_2" id="status_2" data-id="2">
          <option value="A">Active</option>
          <option value="I" selected>Inactive</option>
        </select>
      </td>
    </tr>
    <tr>
      <td class="text-center"> East</td>
      <td class="text-center">
        <select class="custom-select status" name="status_3" id="status_3" data-id="3">
          <option value="A" selected>Active</option>
          <option value="I">Inactive</option>
        </select>
      </td>
    </tr>
    <tr>
      <td class="text-center"> West</td>
      <td class="text-center">
        <select class="custom-select status" name="status_4" id="status_4" data-id="4">
          <option value="A">Active</option>
          <option value="I" selected>Inactive</option>
        </select>
      </td>
    </tr>
  </tbody>
  <tfoot>
    <tr class="bg-dark">
      <th class="text-center text-white">Section</th>
      <th class="text-center text-white">Status</th>
    </tr>
  </tfoot>
</table>

如果您运行上面的代码示例,一旦 ajax 请求完成,您将看到加载对话框仍在屏幕上。我不知道为什么,因为我有.modal('hide')。如果您使用 setTimeout 注释掉代码块并注释掉 ajax 代码,您将看到加载对话框工作正常。如果有人知道如何解决这个问题,请告诉我。

【问题讨论】:

  • 我们如何复制问题?我把dialog.modal('hide'); 放在了ajax 的fail 函数中。我运行你的代码 ajax 给出了错误,然后对话框被隐藏了。所以我确信它应该适用于 ajax 的done 函数。
  • @Kenny 运行代码示例并更改状态。然后你会看到问题。
  • 我能想到的,你的 ajax 成功/失败是在对话框完成渲染之前执行的

标签: javascript jquery ajax bootstrap-4 bootbox


【解决方案1】:

根据 Daniel Gimenez 的说法,问题是 dialog.modal('close') 很快就会被调用,一个简单的解决方法是使用 setTimeout() ,这样您也可以保留那些花哨的动画:

将所有dialog.modal('hide')替换为

setTimeout(function(){ 
   dialog.modal('hide')
},200);

因此您的完整代码将是:

function alertBox(title, message, size) {
  title = title || "Alert";
  message = message || "Alert Box";
  size = size || "lg";

  bootbox.alert({
    size: size,
    title: title,
    message: message
  });
};

$('.status').on('change', function() {
  let $curr_fld = $(this),
    status_id = $curr_fld.attr('data-id'),
    dialog = bootbox.dialog({
      message: '<p class="text-center mb-0"><i class="fa fa-spin fa-cog"></i> Loading...</p>',
      closeButton: false
    });

  $curr_fld.prop("disabled", true);
  /*** Start: This is a code for testing. ***/
  /*
  setTimeout(function() {
    dialog.modal('hide');
    $curr_fld.prop("disabled", false);
    alertBox('Alert Message', status_id);
  }, 3000);
  */
  /*** End: This is a code for testing. ***/
  
  $.ajax({
    type: 'POST',
    url: 'test.php?id=1',
    data: {
      'status_id': status_id
    },
    dataType: 'json'
  }).done(function(obj) {
    setTimeout(function(){ 
        dialog.modal('hide')
        $curr_fld.prop("disabled", false);
        if (obj.status === 200) {
          alertBox('Alert Message', obj);
        } else {
          alertBox('Alert Message', obj);
        }
    },200);
    
  }).fail(function(jqXHR, textStatus, errorThrown) {
    setTimeout(function(){ 
        dialog.modal('hide')
        $curr_fld.prop("disabled", false);
        alertBox('Error', textStatus);
    },200);
   
  });

});
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/5.4.0/bootbox.min.js"></script>

<table class="table table-striped table-bordered" id="tbl_nofa">
  <thead>
    <tr class="bg-dark">
      <th class="text-center text-white">Section</th>
      <th class="text-center text-white">Status</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td class="text-center"> South</td>
      <td class="text-center">
        <select class="custom-select status" name="status_1" id="status_1" data-id="1">
          <option value="A" selected>Active</option>
          <option value="I">Inactive</option>
        </select>
      </td>
    </tr>
    <tr>
      <td class="text-center"> North</td>
      <td class="text-center">
        <select class="custom-select status" name="status_2" id="status_2" data-id="2">
          <option value="A">Active</option>
          <option value="I" selected>Inactive</option>
        </select>
      </td>
    </tr>
    <tr>
      <td class="text-center"> East</td>
      <td class="text-center">
        <select class="custom-select status" name="status_3" id="status_3" data-id="3">
          <option value="A" selected>Active</option>
          <option value="I">Inactive</option>
        </select>
      </td>
    </tr>
    <tr>
      <td class="text-center"> West</td>
      <td class="text-center">
        <select class="custom-select status" name="status_4" id="status_4" data-id="4">
          <option value="A">Active</option>
          <option value="I" selected>Inactive</option>
        </select>
      </td>
    </tr>
  </tbody>
  <tfoot>
    <tr class="bg-dark">
      <th class="text-center text-white">Section</th>
      <th class="text-center text-white">Status</th>
    </tr>
  </tfoot>
</table>

【讨论】:

    【解决方案2】:

    我注意到了几个问题:

    1. 您需要通过 ajax 调用的 always 函数关闭对话框。测试时会发生失败,但您没有关闭对话框的代码。但是,这仍然没有解决问题。
    2. 这似乎是 boostrap 模式的问题。正在发生的事情是在动画完成之前调用了hide 函数。有两种解决方案可以解决此问题:

      1. 使用animate: false禁用动画。
      2. 添加检测动画是否已完成的代码,如果未完成,请在 always 函数中采取适当的行动。
    $('.status').on('change', function() {
      let $curr_fld = $(this),
        loading_shown = false,
        status_id = $curr_fld.attr('data-id'),
        dialog = bootbox.dialog({
          // animate: false, // <-- simple way that makes the issue go away.
          message: '<p class="text-center mb-0"><i class="fa fa-spin fa-cog"></i> Loading...</p>',
          closeButton: false,
        }).on('shown.bs.modal', () => loading_shown = true);
    
      $curr_fld.prop("disabled", true);
    
      $.ajax({
        type: 'POST',
        url: 'test.php?id=1',
        data: {
          'status_id': status_id
        },
        dataType: 'json'
      }).done(function(obj) {
        $curr_fld.prop("disabled", false);
        if (obj.status === 200) {
          alertBox('Alert Message', obj);
        } else {
          alertBox('Alert Message', obj);
        }
      }).fail(function(jqXHR, textStatus, errorThrown) {
        $curr_fld.prop("disabled", false);
        alertBox('Error', textStatus);
      }).always(() => {
        // hide immediately, or wait until the animation is complete by listening
        // to shown.bs.modal.
        loading_shown 
          ? dialog.modal('hide')
          : dialog.on('shown.bs.modal', () => dialog.modal('hide'));
      }); 
    });
    
    

    可能有更简洁的方法来处理动画问题而不禁用动画本身。这只是一种方法。

    【讨论】:

    • 谢谢详细回复。我忘了关闭 .fail 块中的对话框。您提供的解决方案 .always 应该可以工作。我对动画了解不多。
    • 这不是一个错误——Bootbox 只是在创建 Bootstrap 模态,模态的文档 (getbootstrap.com/docs/4.5/components/modal/#methods) 说明了动画是如何工作的。使用 v5.4 或更高版本有一些帮助函数可以帮助缓解这种情况 (github.com/makeusabrew/bootbox/releases/tag/v5.4.0)
    • 你是对的@TiesonT。 - 这是一个自举模态问题。我不想挖掘引导箱代码来查找辅助函数,但我确实更新了我的答案,以便仍然可以使用动画。谢谢!
    • 功能在文档中注明,从这里开始:bootboxjs.com/documentation.html#bb-option-onShow - 仅供参考。不同意您的回答,只是指出这是我们所知道的(并且可能发生在类似的库中)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-22
    • 1970-01-01
    • 1970-01-01
    • 2013-06-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多