Spring, Thymeleaf and asynchronous data loading
<p>Im right now facing a huge problem because i have a fragment containing a list with very big amount of data. What i do until now is the following:</p>

<p>When the user clicks a button I perform a javascript post like this:</p>

<pre><code>function loadEvaluations() {

    $.ajax({
        url : "/evaluation/data",
        type : "POST",
        headers : createAuthorizationTokenHeader(),
        async : !1,
        data : {
            from: rangeFrom,
            to: rangeTo
        },
        success : function(data) {
            $("#portal_container").html(data);
        },
        error : function(data) {
            $("#portal_container").html(data);
        }
    });
}
</code></pre>

<p>In the Spring backend i select the data from the database, put it inside the model and return the fragment:</p>

<pre><code>@RequestMapping(HelperUrls.URL_WEB_EVALUATION_DATA)
public String data(Model model, HttpServletRequest request,
        @RequestParam(value = Params.PARAM_FROM, required = true) long from,
        @RequestParam(value = Params.PARAM_TO, required = true) long to) {

    final IDM_USER user = this.idm_user_repository.findByEmail(request.getUserPrincipal().getName());

    if (user != null) {
        final int unit = user.getUnit();
        final Locale locale = LocaleContextHolder.getLocale();
        final String unitValue = HelperShortcuts.getUnitForShortcut(this.messageSource, locale, unit);

        Set&lt;IDM_BREAD&gt; breads = this.idm_bread_repository.findByDatesBetweenAndUser(new Date(from), new Date(to), user);
        Set&lt;IDM_FOOD&gt; foods = this.idm_food_repository.findByDatesBetweenAndUser(new Date(from), new Date(to), user);
        Set&lt;IDM_INSULIN&gt; insulins = this.idm_insulin_repository.findByDatesBetweenAndUser(new Date(from), new Date(to), user);
        Set&lt;IDM_MEASURE&gt; measures = this.idm_measure_repository.findByDatesBetweenAndUser(new Date(from), new Date(to), user);
        Set&lt;IDM_MOOD&gt; moods = this.idm_mood_repository.findByDatesBetweenAndUser(new Date(from), new Date(to), user);
        Set&lt;IDM_NOTE&gt; notes = this.idm_note_repository.findByDatesBetweenAndUser(new Date(from), new Date(to), user);
        Set&lt;IDM_SPORT&gt; sports = this.idm_sport_repository.findByDatesBetweenAndUser(new Date(from), new Date(to), user);

        List&lt;DatatransferListEntry&gt; entries = new ArrayList&lt;&gt;();

        entries.addAll(breads);
        entries.addAll(foods);
        entries.addAll(insulins);
        entries.addAll(measures);
        entries.addAll(moods);
        entries.addAll(notes);
        entries.addAll(sports);
        entries = this.initHeaders(entries, locale);

        model.addAttribute(ReturnKeys.TIME, Helper.getDateTimePatternEvaluation(this.messageSource, locale, from, to));
        model.addAttribute(ReturnKeys.ENTRIES, entries);
        model.addAttribute(ReturnKeys.USER_UNIT_VALUE, unitValue);
    }

    return Htmls.WEB_FRAGMENT_EVALUATION_DATA_F;
}
</code></pre>

<p>This is getting presented in the fragment like the following:</p>

<pre><code>&lt;!DOCTYPE html&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"&gt;
&lt;body&gt;
    &lt;div th:fragment="fragment_evaluations_data" class="margin-top-100 margin-bottom-100"&gt;

        &lt;div th:each="entry: ${ENTRIES}"&gt;
            &lt;div class="container page-small page-small-header" th:if="${entry.type == 0}"&gt;
                &lt;div class="row page-row-evaluation"&gt;
                    &lt;span th:text="${entry.timestampLabel}"&gt;&lt;/span&gt;
                &lt;/div&gt;

                &lt;div class="row page-row-evaluation"&gt;&lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="container page-small page-small-header-sub" th:if="${entry.type == 1}"&gt;
                &lt;div class="row page-row-evaluation"&gt;
                    &lt;span th:text="${entry.timestampLabel}"&gt;&lt;/span&gt;
                &lt;/div&gt;

                &lt;div class="row page-row-evaluation"&gt;&lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="container page-small" th:if="${entry.type == 2}"&gt;
                &lt;div class="row page-row-evaluation"&gt;
                    &lt;div class="col-md-12"&gt;
                        &lt;span class="evaluation-font" th:text="${entry.value} + ' ' + ${USER_UNIT_VALUE}"&gt;&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;div class="col-md-12 margin-top-5"&gt;
                        &lt;span th:text="${entry.timestampLabel} + ' '"&gt;&lt;/span&gt;
                        &lt;span th:if="${entry.mealtime == 0}" th:text="'- ' + #{label.meal_before}"&gt;&lt;/span&gt;
                        &lt;span th:if="${entry.mealtime == 1}" th:text="'- ' + #{label.meal_after}"&gt;&lt;/span&gt;
                    &lt;/div&gt;
                &lt;/div&gt;

                &lt;div class="row page-row-evaluation"&gt;&lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="container page-small" th:if="${entry.type == 3}"&gt;
                &lt;div class="row page-row-evaluation"&gt;
                    &lt;div class="col-md-12"&gt;
                        &lt;span class="evaluation-font" th:text="${entry.units} + ' ' + #{label.insulin_units}"&gt;&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;div class="col-md-12 margin-top-5"&gt;
                        &lt;span th:text="${entry.timestampLabel}"&gt;&lt;/span&gt;
                    &lt;/div&gt;
                &lt;/div&gt;

                &lt;div class="row page-row-evaluation"&gt;&lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="container page-small" th:if="${entry.type == 4}"&gt;
                &lt;div class="row page-row-evaluation"&gt;
                    &lt;div class="col-md-12"&gt;
                        &lt;span class="evaluation-font" th:text="${entry.units} + ' ' + #{label.bread}"&gt;&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;div class="col-md-12 margin-top-5"&gt;
                        &lt;span th:text="${entry.timestampLabel}"&gt;&lt;/span&gt;
                    &lt;/div&gt;
                &lt;/div&gt;

                &lt;div class="row page-row-evaluation"&gt;&lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="container page-small" th:if="${entry.type == 5}"&gt;
                &lt;div class="row page-row-evaluation"&gt;
                    &lt;div class="col-md-12"&gt;
                        &lt;span class="evaluation-font" th:text="${entry.sporttype.sporttype}"&gt;&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;div class="col-md-12 margin-top-5"&gt;
                        &lt;span th:if="${entry.intensity == 0}" th:text="#{label.intensity_easy}"&gt;&lt;/span&gt;
                        &lt;span th:if="${entry.intensity == 1}" th:text="#{label.intensity_moderate}"&gt;&lt;/span&gt;
                        &lt;span th:if="${entry.intensity == 2}" th:text="#{label.intensity_hard}"&gt;&lt;/span&gt;
                        &lt;span th:if="${entry.intensity == 3}" th:text="#{label.intensity_very_hard}"&gt;&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;div class="col-md-12 margin-top-5"&gt;
                        &lt;span th:text="${entry.duration} + ' ' + #{label.minutes}"&gt;&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;div class="col-md-12 margin-top-5"&gt;
                        &lt;span th:text="${entry.timestampLabel}"&gt;&lt;/span&gt;
                    &lt;/div&gt;
                &lt;/div&gt;

                &lt;div class="row page-row-evaluation"&gt;&lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="container page-small" th:if="${entry.type == 6}"&gt;
                &lt;script&gt;
                    function loadFoodImage(id) {
                        $.ajax({
                            url : "/rest/evaluation/foodiamgeid",
                            type : "POST",
                            headers : createAuthorizationTokenHeader(),
                            async : 1,
                            data : {
                                image_id: id
                            },
                            success : function(data) {
                                var image = JSON.parse(data).USER_IMAGE;

                                if (image != null) {
                                    var selector = "#evaluation_food_image_" + id;

                                    $(selector).attr("src", image);
                                }
                            },
                            error : function(data) {

                            }
                        });
                    }
                &lt;/script&gt;

                &lt;div class="row page-row-evaluation"&gt;
                    &lt;div class="col-md-12"&gt;
                        &lt;span class="evaluation-font" th:text="#{label.food}"&gt;&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;div class="col-md-12 margin-top-5"&gt;
                        &lt;span th:text="${entry.timestampLabel}"&gt;&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;div class="col-md-12 margin-top-5"&gt;
                        &lt;img th:id="'evaluation_food_image_' + ${entry.imageId}" src="/img/ic_rolling.gif" class="center-block img-responsiv image-upload-image" th:onload="|loadFoodImage('${entry.imageId}')|" /&gt;
                    &lt;/div&gt;
                &lt;/div&gt;

                &lt;div class="row page-row-evaluation"&gt;&lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="container page-small" th:if="${entry.type == 7}"&gt;
                &lt;div class="row page-row-evaluation"&gt;
                    &lt;div class="col-md-12"&gt;
                        &lt;span class="evaluation-font" th:text="#{label.note}"&gt;&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;div class="col-md-12 margin-top-5"&gt;
                        &lt;span th:text="${entry.timestampLabel}"&gt;&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;div class="col-md-12 margin-top-5"&gt;
                        &lt;span th:text="${entry.note}"&gt;&lt;/span&gt;
                    &lt;/div&gt;
                &lt;/div&gt;

                &lt;div class="row page-row-evaluation"&gt;&lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="container page-small" th:if="${entry.type == 8}"&gt;
                &lt;div class="row page-row-evaluation"&gt;
                    &lt;div class="col-md-12"&gt;
                        &lt;span class="evaluation-font" th:text="#{label.mood}"&gt;&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;div class="col-md-12 margin-top-5"&gt;
                        &lt;span th:text="${entry.timestampLabel}"&gt;&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;div class="col-md-12 text-center"&gt;
                        &lt;img th:if="${entry.mood == 1}" class="image-mood" src="/img/ic_mood_very_bad_red.png"&gt;&lt;/img&gt;
                        &lt;img th:if="${entry.mood == 2}" class="image-mood" src="/img/ic_mood_bad_orange.png"&gt;&lt;/img&gt;
                        &lt;img th:if="${entry.mood == 3}" class="image-mood" src="/img/ic_mood_neutral_yellow.png"&gt;&lt;/img&gt;
                        &lt;img th:if="${entry.mood == 4}" class="image-mood" src="/img/ic_mood_good_green.png"&gt;&lt;/img&gt;
                        &lt;img th:if="${entry.mood == 5}" class="image-mood" src="/img/ic_mood_very_good_green.png"&gt;&lt;/img&gt;
                    &lt;/div&gt;
                &lt;/div&gt;

                &lt;div class="row page-row-evaluation"&gt;&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>

<p><strong>The Problem</strong> </p>

<p>Loading the data this way is a very bad user experience because the loading takes round about 10 seconds and the ui freezes and the user is not able to do something. He stays on the old page, has to wait 10 seconds and after that gets "navigated" to the new fragment.</p>

<p><strong>What i need</strong></p>

<p>I need a thymeleaf/spring solution to load the data asynchronously. So i want the user to see the new page immediatley after he clicked the link and is then getting presented some kind of loading animation while the data is gathered from the server. When the server is done the view should get updated automatically.</p>

<p>Is that possible?</p>