web-dev-qa-db-ja.com

Spring MVC 3.2 Thymeleaf Ajaxフラグメント

Spring MVC 3.2とThymeleafテンプレートエンジンを使用してアプリケーションを構築しています。私は、Thymeleafの初心者です。

Thymeleafを含むすべてが機能していますが、コントローラーへの単純なAjaxリクエストを実行し、その結果テンプレート(フラグメント)の一部のみをレンダリングする方法について、シンプルで明確なtoturialを知っている人がいるかどうか疑問に思いました。

私のアプリはすべて設定されており(Spring 3.2、spring-security、thymeleafなど)、期待どおりに動作します。今、私はAjaxリクエストをしたいと思います(jQueryでは非常に簡単ですが、Thymeleafのチュートリアル、第11章:テンプレートフラグメントのレンダリング( link )が言及されているので、私は使用したくないフラグメントで行われます。

現在、私は自分のコントローラーにいます

@RequestMapping("/dimensionMenuList")
public String showDimensionMenuList(Model model) {

    Collection<ArticleDimensionVO> articleDimensions;
    try {
        articleDimensions = articleService.getArticleDimension(ArticleTypeVO.ARTICLE_TYPE);
    } catch (DataAccessException e) {
        // TODO: return ERROR
        throw new RuntimeException();
    }

    model.addAttribute("dimensions", articleDimensions);

    return "/admin/index :: dimensionMenuList";
}

<ul></ul>メニュー項目を置き換えたいビューの部分:

<ul th:fragment="dimensionMenuList" class="dropdown-menu">
    <li th:unless="${#lists.isEmpty(dimensions)}" th:each="dimension : ${dimensions}">
        <a href="#" th:text="${dimension.dimension}"></a>
    </li>
</ul>

どんな手がかりも大歓迎です。特に、これ以上フレームワークを含める必要がない場合。 JavaそのままのWebアプリでは既に多すぎます。

45
y0gie007

ブログ投稿 で出会ったアプローチを次に示します。

これらのフレームワークを使用したくなかったので、このセクションではjQueryを使用してAJAXリクエストをサーバーに送信し、レスポンスを待機し、ビューを部分的に更新します(フラグメントレンダリング)。

フォーム

<form>
    <span class="subtitle">Guest list form</span>
    <div class="listBlock">
        <div class="search-block">
            <input type="text" id="searchSurname" name="searchSurname"/>
            <br />
            <label for="searchSurname" th:text="#{search.label}">Search label:</label>
            <button id="searchButton" name="searchButton" onclick="retrieveGuests()" type="button" 
                    th:text="#{search.button}">Search button</button>
        </div>

        <!-- Results block -->
        <div id="resultsBlock">

        </div>
    </div>
</form>

このフォームには、サーバーに送信される検索文字列(searchSurname)を含む入力テキストが含まれています。また、サーバーから受信した応答で更新されるリージョン(resultsBlock div)もあります。

ユーザーがボタンをクリックすると、retrieveGuests()関数が呼び出されます。

function retrieveGuests() {
    var url = '/th-spring-integration/spring/guests';

    if ($('#searchSurname').val() != '') {
        url = url + '/' + $('#searchSurname').val();
    }

    $("#resultsBlock").load(url);
}

JQueryロード関数は、指定されたURLでサーバーに要求を行い、返されたHTMLを指定された要素(resultsBlock div)に配置します。

ユーザーが検索文字列を入力すると、指定された姓を持つすべてのゲストを検索します。それ以外の場合は、完全なゲストリストを返します。これらの2つの要求は、次のコントローラー要求マッピングに到達します。

@RequestMapping(value = "/guests/{surname}", method = RequestMethod.GET)
public String showGuestList(Model model, @PathVariable("surname") String surname) {
    model.addAttribute("guests", hotelService.getGuestsList(surname));

    return "results :: resultsList";
}

@RequestMapping(value = "/guests", method = RequestMethod.GET)
public String showGuestList(Model model) {
    model.addAttribute("guests", hotelService.getGuestsList());

    return "results :: resultsList";
}

SpringはThymeleafと統合されているため、HTMLのフラグメントを返すことができるようになります。上記の例では、戻り文字列「results :: resultsList」は、結果ページにあるresultsListという名前のフラグメントを参照しています。この結果ページを見てみましょう。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:th="http://www.thymeleaf.org" lang="en">

<head>
</head>

<body>
    <div th:fragment="resultsList" th:unless="${#lists.isEmpty(guests)}" id="results-block">
        <table>
            <thead>
                <tr>
                    <th th:text="#{results.guest.id}">Id</th>
                    <th th:text="#{results.guest.surname}">Surname</th>
                    <th th:text="#{results.guest.name}">Name</th>
                    <th th:text="#{results.guest.country}">Country</th>
                </tr>
            </thead>
            <tbody>
                <tr th:each="guest : ${guests}">
                    <td th:text="${guest.id}">id</td>
                    <td th:text="${guest.surname}">surname</td>
                    <td th:text="${guest.name}">name</td>
                    <td th:text="${guest.country}">country</td>
                </tr>
            </tbody>
        </table>
    </div>
</body>
</html>

フラグメントは、ゲストが登録されたテーブルであり、結果ブロックに挿入されます。

71
Sohail

レンダリングのみThymeleaf fragmentsModelAndViewでもうまく機能します。

あなたのコントローラー

@RequestMapping(value = "/feeds", method = RequestMethod.GET)
public ModelAndView getFeeds() {
    LOGGER.debug("Feeds method called..");
    return new ModelAndView("feeds :: resultsList");
}

あなたの見解

<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">

<head></head>
<body>
    <div th:fragment="resultsList" id="results-block">
        <div>A test fragment</div>
        <div>A test fragment</div>
    </div>
</body>
</html>

実際にレンダリングされるもの

<div id="results-block">
    <div>A test fragment</div>
    <div>A test fragment
    </div>
</div>
7
Abdullah Khan

Sohailの素晴らしい答えの代替バージョンとして、javascriptを使用してth:オブジェクト全体をコントローラに送信し、Thymeleafをフォームに統合して、乱雑になったり使用できなくなったりする@PathVariableを使用する必要がないバージョンを提供したい多くのフィールドを持つフォームを作成しました。

フォーム(idとStringsという名前のオブジェクトを返し、それらのオブジェクトの一部を値として持つMapをコンボボックスに供給する例を使用して)には次のようになります:

<form method="post" th:action="@{/yourMapping}" th:object="${object}" id="yourFormId">
    <select th:field="*{mapOfObjects}">
       <option 
          th:each="entry: ${mapOfObjects}"
          th:value="${entry.value.id}"
          th:text="${entry.value.name}" >
       </option>
    </select>

    <p>Name: 
       <input type="text" th:field="*{name}" />
    </p>
</form>

このフォームが送信されると(たとえば送信タイプのボタンを使用して)、ドキュメント全体が置き換えられます。ただし、この送信をjavascriptでインターセプトして、ajax方式で行うことができます。これを実現するために、関数を使用してインターセプターをフォームに追加します。まず、フォームの直後にインターセプターを追加する関数を呼び出します。

<script>formInterceptor("yourFormId");</script>

関数は次のようになります(ドキュメントの先頭または必要に応じてどこにでも配置できます)。

<script>
function formInterceptor(formName) {
    var $form = $("#" + formName);

    $form.on('submit', function(e) {
        e.preventDefault();
        $.ajax({
            url : $form.attr('action'),
            type : 'post',
            data : $form.serialize(),
            success : function(response) {
                if ($(response).find('.has-error').length) {
                    $form.replaceWith(response);
                }
                else{
                    $("#ajaxLoadedContent").replaceWith(response);
                }
            }
        });
    });
};
</script>

これで、フォームが送信されるたびに、この関数がトリガーされ、次のようになります。

  • 元のフォームの送信を禁止する
  • フォームのth:actionで定義されたURLを使用してajax呼び出しを行います
  • フォームデータをシリアル化します。コントローラーはオブジェクトでこれを受け取ることができます
  • HTMLコードの一部を返されたフラグメントで置き換えます

交換した部品は次のようになります

<div id="ajaxLoadedContent"></div>

そして、コントローラーはフォームのth:objectを受け取り、その値を埋めて、次のようにします(オブジェクトをオブジェクトのタイプに、「オブジェクト」を適切な名前に置き換えます):

@PostMapping(value = /yourMapping)
public String ajaxCtrlExample(@ModelAttribute("object") Object object, Model model) {
    return yourFragmentPath;
}

そして、それがすべてです。 ajaxバージョンで必要なすべてのフォームの後にインターセプターを追加する関数を呼び出します。

1
Jorge.V