Saya ingin memicu metode pengontrol Spring MVC dari tampilan Thymeleaf tanpa mengembalikan apa pun dari metode dan tanpa menyegarkan halaman.

Saya memiliki halaman HTML berikut:

enter image description here

<!-- '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>

Pengontrol terkait terlihat sebagai:

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

Panggilan itemService.blockItem(itemId) hanya mengubah atribut boolean yang sesuai dari objek item terkait dalam database.

Saat ini setiap kali saya memicu metode ini dengan menekan tombol blokir item saya diarahkan ke URL seperti http://localhost:8080/item/block/7C4A3744375523.

Saya ingin tidak melakukan pengalihan apa pun tetapi hanya menjalankan logika metode layanan terkait. Saya juga perlu menghapus div item terkait dari tampilan tetapi ini dapat saya lakukan dengan cukup mudah dengan JavaScript.
Bagaimana saya bisa melakukannya? Terima kasih.

2
DimaSan 9 Mei 2021, 16:46

2 jawaban

Jawaban Terbaik

Tombol Anda didefinisikan sebagai type submit.

Akibatnya, ketika Anda mengkliknya, formulir dikirimkan.

Untuk mencegah perilaku default itu, Anda memerlukan beberapa hal: ide utamanya adalah mendefinisikan handler untuk acara submit formulir Anda dan menggunakan Javascript untuk melakukan pemanggilan server yang sebenarnya.

Anda dapat melakukannya dalam cara yang berbeda, meskipun saya pikir itu lebih baik untuk menggunakan event handler untuk menentukan perilaku yang diinginkan dan memisahkan kode HTML Anda dari Javascript sebanyak mungkin.

Selain itu, Anda tampaknya mengulangi daftar item.

Dengan mengingat dua hal tersebut, pertimbangkan cuplikan kode berikut:

<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>

Bahkan, Anda bisa menyingkirkan formulir. Tolong, pertimbangkan pendekatan yang sedikit berbeda. Pertama, saat mengulangi item Anda, buat blok HTML berikut (Anda dapat menggunakan data atau atribut khusus apa pun untuk menyimpan id item yang diproses saat ini jika Anda mau):

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

Sekarang, dengan cara yang sama seperti yang dijelaskan di atas, daftarkan penangan untuk setiap tombol item:

<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>
1
jccampanero 12 Mei 2021, 22:43

Bukan Thymeleaf atau Spring MVC yang menyebabkan pengalihan tetapi tombol submit di tag form Anda. Anda perlu menimpa perilaku tombol ini untuk mengirim data formulir melalui Ajax dan menangani respons sendiri (pada dasarnya - abaikan saja) - mis. menggunakan jQuery itu bisa terlihat seperti:

<!-- '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>

//...

$('#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(){}
    });
})

Ada banyak ide untuk menyelesaikan masalah ini. Silakan baca mis.

1
m.antkowicz 12 Mei 2021, 12:53