jQuery 移动多页在切换页面之前等待 ajax 到 return 数据

jQuery mobile multipage wait for ajax to return data before switching pages

我在多页表单中使用 jQuery 移动设备。当用户单击标记为 Next Page 的按钮时,我想 运行 一个 AJAX 请求。根据 AJAX 请求的结果,我想阻止用户访问下一页。

我认为我 运行 遇到的问题是我的 AJAX 请求比下一页事件需要更长的时间来回复。因此,它让用户进入下一页而不让 AJAX 请求完成。

至少我认为这就是正在发生的事情。有没有人运行以前参与过这个问题或者知道这个问题的解决方案?

HTML:

<div id="page1" data-role="page">
    <div data-role="header" data-position="fixed" data-theme="d">
        <a href="#page2" data-role="button" id="btnPage2" class="ui-btn-right" data-icon="arrow-r">Next Page</a>
    </div>
    <div role="main" class="ui-content">
        <label for="testVal">Test Val:</label>
        <input type="text" id="testVal" name="testVal"/>
    </div>
</div>
<div id="page2" data-role="page">
    <div role="main" class="ui-content">

    </div>
</div> 

jQuery:

$('#btnPage2').click(function(){
    var isValid = '';
    var aValue = $('#testVal').val();
    $.ajax({
        type: "POST",
        url: "php-file.php",
        data: {"something":aValue },
        success: function(data){
            var json = $.parseJSON(data);
            if(json.hasOwnProperty('error')){
                console.log("the user is not allowed on page 2");
                isValid = false;
            }else{
                isValid = true;
            }
        }
    });
    if(!isValid){
        alert('sorry you can not enter page 2');
        return false;
    }
});

在上面的代码示例中,当我单击“下一页”按钮(即 #btnPage2)时,它允许我访问第 2 页,即使当我检查控制台日志时看到“第 2 页不允许用户访问 " .

更新:

我尝试将代码调整为以下内容。它有点工作,但只有当不允许用户访问 page2 时。 e.preventDefault 正在让 ajax 完成,但是,我想不出强制更改 page2 的方法。如果允许用户访问 page2 行

$(":mobile-pagecontainer").pagecontainer("change", "#page2");

似乎是导致问题的原因...它只是创建了调用 pageChange 事件的无限循环。我认为这是强制将页面更改为 page2 的方法,但我认为我错了,我不知道如何强制它更改为 page2

$(document).bind('pagebeforechange', function(e, data) {
    var to = data.toPage,
        from = data.options.fromPage;

    if (typeof to === 'string') {
        var u = $.mobile.path.parseUrl(to);
        to = u.hash || '#' + u.pathname.substring(1);
        if (from) from = '#' + from.attr('id');

        if (from === '#page1' && to === '#page2') {
        e.preventDefault();
        var aValue = $('#testVal').val();
            $.ajax({
                type: "POST",
                url: "php-file.php",
                data: {"something":aValue },
                cache: false,
                success: function(data){
                    var json = $.parseJSON(data);
                    if(json.hasOwnProperty('canNotPass')){
                        alert('Sorry you can not view page 2');
                    }else{
                        ///  if json does not contain canNotPass then continue to page2 
                        $(":mobile-pagecontainer").pagecontainer("change", "#page2");
                    }
                },
                beforeSend: function() {
                    $.mobile.loading("show");
                },
                complete: function() {
                    $.mobile.loading("hide");
                }
            });   
        }            
    }
});

更新 #2

我尝试实施@deblocker 修复,但我仍然遇到同样的问题。当我在 ajax 将 canNotPass 设置为 true 的情况下 运行 以下代码时,代码似乎永远不会达到测试 [=19= 的值的地步].因此,它永远不会设置 e.preventDefault 并且允许用户访问 page2.

我认为解决方案与设置延迟对象有关,该对象允许 ajax 有时间在页面更改发生之前完成。唯一的问题是我知道如何使用延迟对象设置 up/work 。

JSFiddle

使用按钮代替锚点:

<button id="btnPage2" class="ui-btn-right ui-icon-arrow-r">Next Page</a>

...并通过 success 处理程序中的代码导航:

$(":mobile-pagecontainer").pagecontainer("change", "#page2");

顺便说一句,您还可以使用 ajax:

中的一些其他处理程序
$.ajax({
  type:...
  url:...
  data:...
  success: ...
  beforeSend: function() {
    $.mobile.loading("show");
  },
  complete: function() {
    $.mobile.loading("hide");
  },
  error: function (request,error) {
    alert('sorry you can not enter page 2');
});

更新:

按照 Omar 的建议,在 pagecontainerbeforechange 处理程序的第一部分中,您应该中断导航,因为您已经到了 page2 的一半。因此,您不再需要告诉 pagecontainerpagecontainerbeforechange 事件中切换到 page2。您应该只将 ajax 请求和 canNotPass 标志保留在 $('#btnPage2').click().

演示:

$(document).on("pagecontainerbeforechange", function(e, ui) {
  var from = "#" + $(":mobile-pagecontainer").pagecontainer("getActivePage").prop("id");
  var to = ui.toPage; 
  if (typeof ui.toPage == "string") {
    var u = $.mobile.path.parseUrl(to);
    to = u.hash || "#" + u.pathname.substring(1);
    if (from === "#page-one" && to === "#page-two") {
      var canNotPass = !$("#canPass").prop("checked");
      if(canNotPass) {
        e.preventDefault();
        $.mobile.activePage.find('.ui-btn-active').removeClass('ui-btn-active').blur();
      }
    }
  }
});
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
  <link rel="stylesheet" href="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.css">
  <script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
  <script src="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.js"></script>
</head>
<body>
  <div data-role="page" id="page-one">
    <div data-theme="a" data-role="header" data-position="fixed">
      <h1>First Page</h1>
      <a href="#page-two" class="ui-btn-left">Next</a>
    </div>
    <div data-role="content">
      <label for="canPass">Can Navigate to Page 2 ?</label>
      <input data-role="flipswitch" name="canPass" id="canPass" data-on-text="Yes" data-off-text="No" type="checkbox">
    </div>
    <div data-theme="a" data-role="footer" data-position="fixed">
      <h1>Footer</h1>
    </div>
  </div>
  <div data-role="page" id="page-two">
    <div data-theme="a" data-role="header" data-position="fixed">
      <h1>Second Page</h1>
      <a href="#page-one" class="ui-btn-left">Back</a>
    </div>
    <div data-role="content">
    </div>
    <div data-theme="a" data-role="footer" data-position="fixed">
      <h1>Footer</h1>
    </div>
  </div>
</body>
</html>

EDIT2:(致谢:Omar)- 以下是如何将直接导航捕获到 "protected" 页面

$(document).on("pagecontainerbeforechange", function(e, ui) {
  /* in case user enter foo.com/#page2 directly */
  if (typeof ui.toPage === "object" && ui.absUrl === undefined && ui.toPage[0].id == "page2") {
    /* make ajax call */
    var ajaxCall = true;
    if (ajaxCall) {
      $.noop
    } else {
      ui.toPage = "#noAccessPage"
    }
  }

  /* internal navigation from page1 to page2 */
  if (typeof ui.toPage === "string" && ui.absUrl !== undefined && $.mobile.path.parseUrl(ui.absUrl).hash == "#page2") {
    /* make ajax call */
    var ajaxCall = true;
    if (ajaxCall) {
      $.noop
    } else {
      ui.toPage = "#noAccessPage"
    }
  }
});

感谢 Omar and deblocker 花这么多时间解决这个问题。不幸的是,我无法在不干扰我的其余页面交互的情况下实施您的建议。下面列出了我想出的唯一可行的解​​决方案。不幸的是,该解决方案不使用处理页面更改事件的内置哈希函数。我只是使用一个普通按钮并为其分配点击监听器。有点笨拙,但这似乎是它在我的情况下工作的唯一方法。

<button id="btnNewPage">Go To Page 2</button>

$(document).ready(function(){
    $("#btnPageNew").click(function(){
        var testVal = $('#testVal').val();
        var canNotPass = promiseTest(testVal);
            canNotPass.done(function(data){
                var json = $.parseJSON(data);
                    console.log(json);
                    if(json.hasOwnProperty('error')){
                        // user can not access the next page
                        console.log('can not pass');
                        $.mobile.activePage.find('.ui-btn-active').removeClass('ui-btn-active').blur();
                    }else{
                        // allow user to access the next page
                        $(":mobile-pagecontainer").pagecontainer("change", "#page2");
                    }
            });


    });
});

function promiseTest(aValue){
    return $.ajax({
                type: "POST",
                url: "php-file.php",
                data: {"something":aValue  },
                cache: false
            });
}