为什么这个 alpine.js x-for 循环 2 次

Why this alpine.js x-for looping 2 times

我使用 alpine.js 渲染一个带有 x-for 循环的模式,该循环填充 select。奇怪的行为,x-for 似乎循环了 2 次,我不明白为什么:

代码:

<script>
    document.addEventListener('alpine:init', () => {
        Alpine.data('load', () => ({
            indicateurs: [],
            indicateurDictionnaireForm: {},
            typeIndicateurs: {},
            detectedTypeIndicateur: '',
            loadIndicateur() {
                $('#overlay').fadeIn();
                fetch('<%= request.getContextPath() %>/mesures.load.action')
                    .then(response => response.json())
                    .then(data => {
                        this.indicateurs = data;
                        $('#overlay').fadeOut();
                    })
                    .catch(error => {
                    });
            },
            deleteIndicateur(id) {
                $.ajax({
                    type: "DELETE",
                    url: "<%= request.getContextPath() %>/mesures.delete.action?indicateurDictionnaireId=" + id,
                }).then(() => this.loadIndicateur());
            },
            postForm() {
                return {
                    submitData() {
                        $.ajax({
                            type: "POST",
                            url: "<%= request.getContextPath() %>/mesures.save.action",
                            data: JSON.stringify(this.indicateurDictionnaireForm),
                            dataType: "JSON",
                            contentType: "application/json; charset=utf-8",
                        }).then(() => {
                            this.loadIndicateur();
                            this.resetForm();
                            $('#modalIndicateur').modal('hide');
                        })
                    },
                }
            },
            editIndicateur(id) {
                $.ajax({
                    type: "GET",
                    url: "<%= request.getContextPath() %>/mesures.load.type-indicateur.action"
                }).then(data => {
                    this.typeIndicateurs = data;
                }).then(
                    $.ajax({
                        type: "GET",
                        url: "<%= request.getContextPath() %>/mesures.edit.action?indicateurDictionnaireId=" + id,
                    }).then(data => {
                        this.indicateurDictionnaireForm = data;
                        this.detectedTypeIndicateur = data.typeIndicateur.code;
                        this.loadIndicateur();
                        $('#modalIndicateur').modal('show');
                    })
                );
            },
            resetForm() {
                this.indicateurDictionnaireForm = {};
            }
        }))
    })
</script>

模态代码:

<div class="modal" tabindex="-1" role="dialog" id="modalIndicateur">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title"><s:text name="indicateur.ce.add"/></h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div class="modal-body">
                <form action="<%= request.getContextPath() %>/save.action"
                      method="POST"
                      class="w-64 mx-auto" x-data="postForm()" @submit.prevent="submitData">
                    <div class="form-group" style="padding-left: 15px; padding-bottom: 5px">
                        <div class="row">
                            <label class="control-label col-md-2" style="text-align: left"
                                   for="libelle_fr">
                                <s:text name="indicateur.ce.libelle_fr"/></label>
                            <div class="col-md-8">
                                <input id="libelle_fr" type="text"
                                       name="indicateurDictionnaireForm.libelleFr"
                                       class="form-control input-sm"
                                       x-model="indicateurDictionnaireForm.libelleFr">
                            </div>
                        </div>
                        <div class="row">
                            <label class="control-label col-md-2" style="text-align: left"
                                   for="libelle_nl">
                                <s:text
                                        name="indicateur.ce.libelle_nl"/></label>
                            <div class="col-md-8">
                                <input id="libelle_nl" type="text"
                                       name="indicateurDictionnaireForm.libelleNl"
                                       class="form-control input-sm"
                                       x-model="indicateurDictionnaireForm.libelleNl">
                            </div>
                        </div>
                        <div class="row">
                            <label class="control-label col-md-2" style="text-align: left"
                                   for="code">
                                <s:text name="indicateur.ce.code"/></label>
                            <div class="col-md-8">
                                <input id="code" type="text"
                                       name="indicateurDictionnaireForm.typeIndicateurCode"
                                       class="form-control input-sm"
                                       x-model="indicateurDictionnaireForm.code">
                            </div>
                        </div>
                        <div class="row">
                            <label class="control-label col-md-2" style="text-align: left"
                                   for="code">
                                <s:text name="indicateur.ce.code"/></label>
                            <div class="col-md-4">
                                <div class="flex flex-col w-full md:w-2/3">
                                    <select x-model="indicateurDictionnaireForm.typeIndicateurCode"
                                            class="form-control input-sm">
                                        <template x-for="option in typeIndicateurs" x-effect="console.log(detectedTypeIndicateur)">
                                            <option :key="option.code"
                                                    :value="option.code"
                                                    selected="option.code === detectedTypeIndicateur"
                                                    x-effect="console.log('code:', option.code, ' type: ', detectedTypeIndicateur)"
                                                    x-text="option.libellefr"></option>
                                        </template>
                                    </select>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-secondary" data-dismiss="modal"
                                @click="resetForm()">
                            <s:text name="common.button.quitter"/>
                        </button>
                        <button type="submit" class="btn btn-primary">
                            <s:text name="common.button.save"/>
                        </button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

使用 x-effect 标签,我尝试记录要在 select 标签中 selected 的检测项目的值和值本身。

这是模式打开时的控制台输出:

// first loop

<empty string>

code: RESULTAT type: <empty string>
code: INCIDENCE type: <empty string>

code: REALISATION type: <empty string>

// second loop    

REALISATION

code: RESULTAT type: REALISATION

code: INCIDENCE type: REALISATION

code: REALISATION type: REALISATION 

在第一个循环中,我看到函数 editIndicateur() 的值是空的,所以我假设,alpine.s 尝试在回调之前呈现模态,然后进行第二个循环。

您是否知道为什么模态框在值被函数 return 之前填充,以及为什么它会第二次循环?

感谢您的帮助

您将 select 元素绑定到 indicateurDictionnaireForm.typeIndicateurCode 变量,并将其选项绑定到 typeIndicateurs 变量(通过对其进行循环)。

所以当你调用editIndicateur方法时:

  1. 首先,您通过 AJAX 调用仅加载 select 选项,然后通过 this.typeIndicateurs = data 将其传递给 Alpine.js。
  2. Alpine 检测到您更改了 typeIndicateurs 并呈现模板。这是第一个循环。但是此时 indicateurDictionnaireFormdetectedTypeIndicateur 均未设置,因此它们将为空。
  3. 之后您进行新的 AJAX 调用,在其中加载额外的表单数据并设置 indicateurDictionnaireFormdetectedTypeIndicateur 变量。
  4. Alpine.js 检测到您更改了两个附加变量并再次呈现模板。这是第二个循环。

如果您有两个单独的 AJAX 调用,这是正常行为,因为更改不会在同一个更新队列中。要解决此问题,您应该在 editIndicateur 函数内创建一个本地(非反应性)变量,然后在第一次 AJAX 调用时将选项的数组存储在该变量中。然后在第二个 AJAX 调用中将所有 3 个 Alpine.js 反应变量设置在一起,因此更改将在同一个更新队列中,因此 Alpine.js 只渲染模板一次。

editIndicateur(id) {
    let indicators;
    $.ajax({
        type: "GET",
        url: "<%= request.getContextPath() %>/mesures.load.type-indicateur.action"
    }).then(data => {
        indicators = data;
    }).then(
        $.ajax({
            type: "GET",
            url: "<%= request.getContextPath() %>/mesures.edit.action?indicateurDictionnaireId=" + id,
        }).then(data => {
            this.indicateurDictionnaireForm = data;
            this.detectedTypeIndicateur = data.typeIndicateur.code;
            this.typeIndicateurs = indicators;
            this.loadIndicateur();
            $('#modalIndicateur').modal('show');
        })
    );
},