本地数组更改时如何重新渲染流星火焰模板?

Posted

技术标签:

【中文标题】本地数组更改时如何重新渲染流星火焰模板?【英文标题】:How to re-render meteor blaze template when local array changes? 【发布时间】:2016-06-10 10:06:55 【问题描述】:

在过去 2 年多的时间里,我与 AngularJS 进行了大量合作,现在开始使用 MeteorJS 1.2,我觉得 blaze 模板层的某些方面非常不直观,但可能有一个简单的解决方案。

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

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

本质上,我希望能够填充投票选项字段,单击“添加选项”并让此选项以只读方式(纯文本)呈现在投票选项文本字段下方的列表中继器中。

我目前的问题是,如果不使用 ReactiveArray 之类的东西,当我在本地数组变量 options 中推送项目时,blaze 模板不会重新呈现。

解决这个问题的正确方法是什么?

代码如下:

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.jsPolls = 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) 响应式数据源和 (2) 响应式上下文。

现在您已经获得了响应式上下文 - 这就是模板帮助器 optionList,它会在其引用的响应式数据源发生更改时重新渲染。问题是选项列表不是响应式数据源,它只是一个普通数组。所以当Templace.instance().options数组改变时,助手不知道要重新渲染它。

修复非常简单 - 只需使用 Meteor 的内置反应结构 ReactiveVarReactiveDict 或使用 MiniMongoTemplate.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 交互,对吗? IE。直接调用array.pusharray.splice。那么唯一改变内容的方法是先调用ReactiveVar.get()方法,然后修改结果,然后通过ReactiveVar.set(new)重新设置ReactiveVar? 没错。可能是包创建者构建 ReactiveArray 的原因 再次感谢,我刚刚玩过这个场景,但使用 MiniMongo 创建本地 Options 集合,然后使用 insertremove 和最终 .find().fetch() 将选项保存为民意调查的一部分,从代码模式的角度来看,这对我来说感觉更好。在这个用例中使用这些仅限客户端的 mongo 集合有什么缺点吗? 我想不到。 ReactiveVar/ReactiveDict 的一个好处是您可以轻松地将它们的状态保留在模板实例内部 - 这可以是加号或减号。

以上是关于本地数组更改时如何重新渲染流星火焰模板?的主要内容,如果未能解决你的问题,请参考以下文章

Vuejs2:数组更改时如何重新渲染数组计算属性

如何将下拉值传递给模板助手。 [流星+火焰]

Ember:模型更改时重新渲染路线的模板

如何在状态更改(父)reactjs时重新渲染子组件

Vue 列表项不会在状态更改时重新渲染

流星 - 如何编写引导模式模板以重新呈现 html 输入值,即使用户已输入它们