在提交表单之前等待异步调用完成

3
这个有点棘手。
这是我的jQuery代码:
/**
 * Updating websites
 */
$('.website').on('blur', function () {
    var websiteId = this.id;
    console.log(websiteId);
    var website = this.value;
    console.log(website);
    fetch(`/api/edit/${websiteId}&${website}`).then(() => {

    });
})

这是我的HTML代码

<form method="post">
    <% for(let i = 0; i < websites.length; i++){ let website = websites[i]; %>
    <fieldset id="site<%= i %>">
        <p style="color: <% if(errorMsgs[i] === 'Wrong website address'){ %> red;<% } else { %> green;<% } %>
                padding-bottom: 0;
                padding-top: 10px;
                margin-bottom: 0;"
           class="">
            <%= errorMsgs[i] %>
        </p>
        <div class="">
            <input class="website"
                   name="website<%= i %>"
                   id="<%= i %>"
                   value="<%= website %>"
                   type="text"/>
            <a href="/websites/edit/<%= i %>"
               class="btn btn-info hide">
                Edit
            </a>
            <a href="/websites/delete/<%= i %>"
               data-id="<%= i %>"
               class="btn btn-danger confirmation removeBtn">
                Remove
            </a>
        </div>
    </fieldset>
    <% } %>
    <button type="submit"
            class="btn btn-primary col-sm-offset-2"
            value="Generate a report"
            name="generateReport"
            data-toggle="modal"
            data-target="#exampleModal">
        Generate a report
    </button>
    <!-- Save button created using Javascript. In case JS is disabled -->
</form>

这是我的API

// GET: api/edit/_id - save updates
router.get('/edit/:_id&:url', (req, res, next) => {
    Account.findById(req.user._id, (err, acc) => {
        if (err) {
            console.log(err);
        } else {
            acc.websites.set(req.params._id, req.params.url);
            acc.save((err, webs) => {
                if (err) {
                    console.log(err);
                } else {
                    console.log('all good');
                    // res.redirect('/reports');
                    res.json(webs);
                }
            });
        }
    });
});

如何操作:用户在输入字段中输入值,失去焦点后,我向API发出AJAX调用,将值保存在数据库中。这一切都能正常工作。
然而,有一个问题。如果用户点击提交按钮,则获取没有时间运行。这是有道理的。
但我需要提交等待值更新。我考虑做一些事情,例如在提交按钮上添加preventDefault,然后在保存后删除preventDefault。
但我不知道如何做到这一点,也不知道这是否是一个好主意,甚至是否有任何意义。
因此,总结一下:我需要表单提交等待AJAX调用完成。
这个<% smth %>只是EJS模板系统。不应影响它的工作方式。

1
你不能在按钮上设置disabled属性,然后在获取响应后将其移除吗? - p0rter
3个回答

4

看起来fetch()返回一个承诺。将这些承诺保存在共享变量中。在提交按钮代码中,使用Promise.all()等待所有承诺解决。像这样:

const fetchPromises = [];
$('.website').on('blur', function () {
    var websiteId = this.id;
    console.log(websiteId);
    var website = this.value;
    console.log(website);
    var fetchPromise = fetch(`/api/edit/${websiteId}&${website}`).then(() => {

    });
    fetchPromises.push(fetchPromise);
});
$('form').on('submit', function (e) {
    e.preventDefault();
    Promise.all(fetchPromises).then(() => this.submit());
});

您可能希望添加一些错误处理。 如果您的任何fetch()调用失败,则会阻止提交表单。 理想情况下,失败的AJAX调用应该向用户显示一些消息,一旦被忽略,就会将Promise从fetchPromises数组中删除,并且在它被忽略之前,提交按钮应该被禁用(实际上是可见的)。


如果我理解正确的话,这只是将承诺添加到数组中,并在提交事件上运行它们。是这样吗? - Alex Ironside
1
fetch()调用不会发生任何变化。底层的AJAX请求与您当前的代码同时进行。我们将返回的promises存储起来,以防万一在promises解决之前点击了提交按钮。如果AJAX请求在表单提交之前完成,Promise.all()将立即解决,并且表单提交将顺利进行,没有延迟。 - gilly3
谢谢你的解释。现在明白了! - Alex Ironside

1
你可以将按钮设置为禁用状态,添加监听器,然后在 .then() 函数期间抛出事件。
$('.website').on('blur', function () {
    var elem = document.getElementById('submitButtonId');

    // create the event
    var isFinishedEvent = new Event('fetched');

    elem.addEventListener('fetched', function() {
        // logic to enable your button
    }

    var websiteId = this.id;
    console.log(websiteId);
    var website = this.value;
    console.log(website);
    fetch(`/api/edit/${websiteId}&${website}`).then(() => {
       // Fire the event
       elem.dispatchEvent(isFinishedEvent);
  });
})

我想我会结合你和p0rter的答案。给我一秒钟。 - Alex Ironside

1
使用preventDefault制作一个假的提交按钮,并使用display:none隐藏真正的提交按钮如何? 然后,一旦您获得了AJAX成功,就可以在隐藏的按钮上调用click()函数。

真狡猾。我试试看。 - Alex Ironside
1
你不需要隐藏一个按钮。一个表单有一个submit方法,你可以调用它。 - gilly3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接