【问题标题】:Triggering Spring MVC controller method without page redirect/refresh在没有页面重定向/刷新的情况下触发 Spring MVC 控制器方法
【发布时间】:2021-05-09 13:46:42
【问题描述】:

我想从 Thymeleaf 视图触发 Spring MVC 控制器方法,而不从该方法返回任何内容,也不刷新页面。

我有以下 HTML 页面:

<!-- 'block item' button code-->
<div class="row">
  <form method="get" th:action="@{'/items/block/' + ${item.itemId}}">
      <button type="submit" class="btn btn-warning">block item</button>
  </form>
</div>

相关控制器如下:

@GetMapping("/items/block/{itemId}")
@ResponseStatus(value = HttpStatus.OK)
public void blockItem(@PathVariable String itemId) {
    itemService.blockItem(itemId);
}

itemService.blockItem(itemId) 调用只是更改数据库中相关item 对象的相应布尔属性。

目前,每当我通过按 block item 按钮触发此方法时,我都会被重定向到像 http://localhost:8080/item/block/7C4A3744375523 这样的 URL。

我不想做任何重定向,只是执行相关的服务方法逻辑。我还需要从视图中删除相关项目 div,但我可以使用 JavaScript 非常简单地做到这一点。
我该怎么做?谢谢。

【问题讨论】:

标签: java spring-mvc controller thymeleaf


【解决方案1】:

您的按钮定义为typesubmit

因此,当您单击它时,表单已提交。

为了防止这种默认行为,您需要做几件事:主要思想是为您的表单 submit 事件定义一个处理程序,并使用 Javascript 执行实际的服务器调用。

您可以在different ways 中执行此操作,尽管我认为最好使用事件处理程序来定义所需的行为并尽可能将您的 HTML 代码与 Javascript 分开。

此外,您似乎正在遍历项目列表。

考虑到这两点,考虑以下代码 sn-p:

<script>
  // We use the DOMContentLoaded event (https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event)
  // but please, register the form event as you consider appropriate
  document.addEventListener('DOMContentLoaded', () => {
    // Register form submit event handler for every form in your page
    const forms = document.querySelectorAll('form');
    if (forms) {
      forms.forEach(f => {
        let action = f.action;
        f.addEventListener('submit', (event) => {
          // prevent default behavior, i.e., form submission
          event.preventDefault();
          // perform server side request. you can use several options
          // see https://developers.google.com/web/updates/2015/03/introduction-to-fetch, for instance
          // For example, let's use fetch (https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)
          fetch(action)
            .then((response) => {
              if (!response.ok) {
                throw new Error('Network response was not ok');
              }
              console.log('Request successfully completed');
            })
            .catch((error) => {
              console.error('There has been a problem while contacting server:',      error);
            });
          });
      });
    }
  });
</script>

事实上,你可以摆脱表格。请考虑一种稍微不同的方法。首先,在迭代您的项目时,生成以下 HTML 块(如果您愿意,可以使用 data 或任何自定义属性来存储当前处理的项目 ID):

<!-- 'block item' button code-->
<div class="row">
  <button type="button" class="btn btn-warning item-button" th:id="${item.itemId}">block item</button>
</div>

现在,以与上述类似的方式,为每个项目按钮注册处理程序:

<script th:inline="javascript">
  document.addEventListener('DOMContentLoaded', () => {
    // Register handlers for every button
    const buttons = document.querySelectorAll('.item-button');
    if (buttons) {
      buttons.forEach(b => {
        b.addEventListener('click', (event) => {
          let itemId = b.getAttribute('id');
          let link = /*[[@{/items/block/}]]*/ 'link';
          // Perform server side request
          fetch(link + itemId)
            .then((response) => {
              if (!response.ok) {
                throw new Error('Network response was not ok');
              }
              console.log('Request successfully completed');
            })
            .catch((error) => {
              console.error('There has been a problem while contacting server:', error);
            });
        });
      }
    }
  });
</script>

【讨论】:

  • 谢谢 - 我认为这是一种非常有趣的方法,但在您的 &lt;!-- 'block item' button code--&gt; 示例中有一些不可编译的内容,您是否在 Thymeleaf 模板中表示类似以下内容:&lt;button type="button" class="btn btn-warning item-button" th:id="${item.itemId}/&gt;"&gt;block item&lt;/button&gt;?你能澄清一下吗?
  • 欢迎您@DimaSan。对,就是这样。对不起,我通常使用JSLT standard taglib 进行属性处理,但是是的,它应该是等效的。我用 thymeleaf 的具体实现更新了答案。请,再次抱歉。
  • @DimaSan 我将代码简化为仅依赖于按钮 id 属性。我希望它有所帮助。
  • 感谢您的编辑,它调用了 JS 函数,但由于行 fetch('&lt;c:url value="/items/block/"/&gt;' + itemId) - 在 Chrome 控制台中,它无法加载资源,并且出现 404 错误。我将其更改为fetch('/items/block/' + itemId),现在它没有抱怨,但也没有任何反应,也没有触发控制器。
  • 对不起,又是 JSLT。请问,您可以尝试更新的答案吗?老实说,我从来没有尝试过 inlined javascript 和百里香,但我认为它应该可以工作。请在helpful SO question 中查看已接受的答案。
【解决方案2】:

导致重定向的不是 Thymeleaf 或 Spring MVC,而是 form 标签中的 submit 按钮。您需要覆盖此按钮的行为以通过 Ajax 发送表单数据并自己处理响应(基本上 - 忽略它) - 例如使用 jQuery 它可能看起来像:

<!-- 'block item' button code-->
<div class="row">
    <form id="someFormId" method="get" th:action="@{'/items/block/' + ${item.itemId}}">
        <button type="submit" class="btn btn-warning">block item</button>
    </form>
</div>

//...

$('#someFormId').submit(function(e) {
    e.preventDefault();
    $.ajax({
        type: 'GET',
        url: '@{'/items/block/' + ${item.itemId}}',
        data: $(this).serialize(),
        // here you can define some actions for beforeSend, success, etc...
        // for example to just ignore:
        success: function(){}
    });
})

有很多想法可以解决这个问题。请阅读例如

【讨论】:

  • 感谢您的回复,但是当使用您的方法时,GUI 仍会尝试将我重定向到不存在的 http://localhost:8080/items/block/44A13B4122343A 之类的 URL。也许我还需要在控制器端进行一些更改?
  • 在我的回答中,我错过了 form 标签的 id 属性 - 可能这就是它不起作用的原因
  • 无论如何谢谢你,我相信你的回答也很有用
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-25
  • 2019-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多