当本地数组发生变化时,如何重新渲染Meteor Blaze模板?

3

过去的两年中,我曾经大量使用AngularJS,并且目前开始接触MeteorJS 1.2。然而,Blaze模板层中一些方面让我感到不太直观,但可能有简单的解决方案。

我想要创建一个“创建投票”表单,其中包含以下字段:

  • 问题,文本字段
  • 投票选项,文本字段
  • 添加选项,按钮
  • 选项列表,基于本地options数组的重复器<li>
  • 列表项有一个删除按钮

实质上,我想要能够填充投票选项字段,单击“添加选项”并使该选项以只读方式(普通文本)呈现在下面的选项列表重复器中。

目前我的问题是,如果不使用ReactiveArray等类似的东西,则blaze模板不会在我将项目推送到本地数组变量options时重新渲染。

应该如何正确处理这个问题?

以下是代码:

poll.create.html 模板:

<template name="pollCreate">
    <form class="form form--create">
        <section class="form__section">
            <!-- Question Field -->
            <input class="form__field" type="text" name="question" required>

            <!-- Poll Options -->
            <ul class="form__list">

                <!-- Poll option form field -->
                <li class="form__list-item form__list-item--creator">
                    <input class="form__field" type="text" name="pollOption">
                    <button class="form__button--add" type="button">&plus;</button>
                </li>

                <!-- Render the options added above -->
                {{#each option in optionList}}
                <li class="form__list-item--option" data-index="{{option.index}}">
                    <span class="form__list-item-value">{{option.label}}</span>
                    <button type="button" class="form__button--delete">&times;</button>
                </li>
                {{/each}}
            </ul>
        </section>

        <button class="form__button--submit" type="submit">Create Poll</button>
        <button class="form__button--reset" type="reset">Clear Fields</button>
    </form>
</template>

poll.create.client.js 是一个客户端文件,其中 (Polls = new Mongo.Collection("polls")) 属于服务器文件。

// Setup
Template.pollCreate.onCreated(function () {

    // Local poll variable to capture the options in.
    this.options = [
        { index: 0, label: 'BurgerBurger' },
        { index: 1, label: 'BetterBurger' }
    ];

});

// Events
Template.pollCreate.events({
    /**
     * Adds an option to the local array for saving as part of the submit in the Posts.insert call.
     * @param event
     * @param instance
     */
    'click .form__button--add': function ( event, instance ) {
        event.preventDefault();

        // Get current option element
        var option = instance.find('[name="pollOption"]');

        // Store in a local array
        instance.options.push({
            index: instance.options.length,
            label: option.value
        });

        // Clear field so it's ready for another option
        option.value = '';
    },
    /**
     * Delete an existing option.
     * @param event
     * @param instance Template.instance
     */
    'click .form__button--delete': function ( event, instance ) {
        event.preventDefault();

        var listItemElement = event.target.parentNode;
        var itemIndex = instance.$(listItemElement).data('index');

        // Delete option from reactive source array
        instance.options.splice(itemIndex, 1);
    },
    /**
     * Submit the Poll Create form which will run Polls.insert with the question and options
     * @param event
     * @param instance
     */
    'submit .form--create': function ( event, instance ) {
        event.preventDefault();

        var question = instance.find('[name="question"]');
        var options = instance.options;

        var poll = {
            question: question.value,
            options: options
        };

        Polls.insert(poll);
    }
});

// Helpers
Template.pollCreate.helpers({
    optionList: function () {
        const instance = Template.instance();
        return instance.options;
    }
});

我可以看到当我推入或拼接选项时,instance.options数组值会发生变化,但似乎它不会重新渲染模板,选项列表从未更改。

我在这里做错了什么?

1个回答

1
每当您希望UI有反应性更新时,您需要两个东西:(1)一个反应式数据源和(2)一个反应式上下文。
现在您已经拥有了反应式上下文——即模板助手optionList,它将在被引用的反应式数据源发生更改时重新呈现。问题在于选项列表不是反应式数据源,而只是一个普通数组。因此,当Templace.instance().options数组发生更改时,助手不知道要重新呈现它。
修复非常容易——只需使用Meteor的内置反应式结构之一ReactiveVarReactiveDict或使用MiniMongo,使Template.instance.options成为反应式数据源。您提到的ReactiveArray也可以工作——我相信那是社区维护的而不是核心包,但这是相同的想法。
最快、最简单的解决方案是将整个数组保存到一个ReactiveVar中:
Template.pollCreate.onCreated(function(){
  this.options = new ReactiveVar([
    { index: 0, label: 'BurgerBurger' },
    { index: 1, label: 'BetterBurger' }
  ]);
});

Template.pollCreate.helpers({
  optionList({
    return Template.instance().options.get();
  })
});

那应该可以了。

明白了,谢谢。我刚试了一下,效果很好。现在唯一的问题是(我可能漏掉了什么),但我不能像本地数组一样与ReactiveVar交互,对它直接调用array.pusharray.splice。所以改变内容的唯一方法是先调用ReactiveVar.get()方法,然后修改结果,再通过ReactiveVar.set(new)重新设置ReactiveVar? - Jannis
没错。这也可能是包创建者构建ReactiveArray的原因。 - Jeremy S.
再次感谢,我刚刚尝试了一下这种情况,使用MiniMongo创建本地“Options”集合,然后使用“insert”,“remove”和最终的“.find().fetch()”将选项保存为投票的一部分,从代码模式的角度来看,这对我来说更好。在这种情况下,使用这些仅客户端的mongo集合是否有任何缺点? - Jannis
1
我想不出来。 ReactiveVar / ReactiveDict 的一个好处是,您可以轻松地将它们的状态保持为模板实例的内部状态 - 这可能是一个优点或缺点。 - Jeremy S.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接