自动运行的反应计算能否依赖于集合的一部分?

时间:2015-10-09 18:27:04

标签: javascript mongodb meteor spacebars

我希望在Meteor中创建一个模板,该模板具有Tracker.autorun,该模板仅在文档的一部分发生变化时才会运行---但是当文档的其他部分发生变化时则不会。 所以这里是使用minimongo集合和template.autorun

的示例代码

parent.html

{{#each items}}
  {{> child}}
{{/each}}

child.html

<div>{{title}}</div>
<p>{{description}}</p>

Minimongo Collection

LocalProject.findOne() output:
    "items": [
      {
        "title": "hi",
        "description": "test"
      },
      {
        "title": "hi 2",
        "description": "test 2"
      },
    ],
    "otherstuff:{//etc}

child.js

Template.child.onRendered(function(){
    this.autorun(function() {
        var data = Template.currentData();
        doSomething(data.title,data.description)
    });
});

addnewitem.js

LocalProject.update(currentEditingProjectID,{ $push: { 'items': newItem }},function(error,result){
    if(error){console.log(error)}
});

问题是,每当我运行addnewitem.js时,我的所有Template.child autoru都会执行,即使它们的反应数据源(Template.currentData())没有改变,除非它是我更新的特定项目。同样,如果我想更新现有项目,而不仅仅是向数组添加新项目,则每个项目的所有自动运行都会被执行。

有没有办法,使用这个模型,为autorun创建一个依赖于文档特定部分的反应性的依赖?

2 个答案:

答案 0 :(得分:0)

这里有一个工具 - 3stack:embox-value提供反应隔离和值缓存。

使用您的示例,您可以将更改隔离到标题/描述,如下所示:

首先,添加包

meteor add 3stack:embox-value
meteor add ejson

然后,更新您的代码:

Template.child.onRendered(function(){
     // creates a reactive data source, that only triggers "changes"
     // when the output of the function changes.
     var isolatedData = this.emboxValue(function(){
        var data = Template.currentData();
        return {title: data.title, description: data.description}
    }, {equals: EJSON.equals})

    this.autorun(function() {
         // This autorun will only execute when title or description changes.
        var data = isolatedData()
        doSomething(data.title,data.description)
    });
});

答案 1 :(得分:0)

我认为没办法就是使用自动运行。我会在每个项目上设置单独的反应依赖项,或使用observe / observeChange

第一个想法

<强> parent.html:

{{#each items}}
  {{> child}}
{{/each}}

<强> parent.js:

Template.parent.helpers({
  // Returns only item ids
  items: function() { 
    return Items.find({}, { fields: { _id: 1 } });
  }
});

<强> child.html:

{{#each items}}
  {{#with item=getItem}}
    <div>{{item.title}}</div>
    <p>{{item.description}}</p>
  {{/with}}
{{/each}}

<强> child.js:

Template.child.helpers({
  getItem: function() {
    // Get the item and set up a reactive dependency on this particular item
    var item = Items.find(this._id);

    // After item has been displayed, do something with the dom
    Tracker.afterFlush(function () {
      doSomething(item.title, item.description);
    });

    return item;
  }
});

第二个想法

<强> parent.html:

{{#each items}}
  {{> child}}
{{/each}}

<强> parent.js:

function do(item) {
  Tracker.afterFlush(function () {
    doSomething(item.title, item.description);
  });
}

Template.parent.onCreated({
  this.items = Items.find({});
  this.handle = this.items.observe({
    added:   function(item)             { do(item); },
    changed: function(oldItem, newItem) { do(newItem); },
  });  
});

Template.parent.onDestroyed({
  this.handle.stop();
});

Template.parent.helpers({
  items: function() { 
    return Template.instance().items;
  }
});

<强> child.html:

{{#each items}}
  <div>{{title}}</div>
  <p>{{description}}</p>
{{/each}}