【问题标题】:Meteor: How to get Reactive Parent/Child dependency templates for Object attributesMeteor:如何获取对象属性的反应性父/子依赖模板
【发布时间】:2017-03-06 17:26:40
【问题描述】:

我正在研究 Meteor(使用 Blaze)的反应性依赖树,但我没有找到最好的方法。

首先,问题

在这个例子中,我有一个容器,它设置了一个 item 对象,它有一个属性 elements(数组)。

elements 属性是循环的,并且为它的每个实例加载一个子模板。

可以在子模板上更新元素,但更新不会传播到容器。

另外,我添加了一个Add 按钮来更新item ReactiveVar(来自container)。每个字段渲染在容器上更新item 后更新,因此对象已更新但未重新渲染。

HTML

<template name="container">
  {{#let item=getItem}}
    <h1>Update {{item._id}}</h1>
    {{#each element in item.elements}}
      <div>
        {{> elementForm element}}
        <small>{{element.name}}</small>
      </div>
    {{/each}}
  {{/let}}
  <button name="add">Add</button>
</template>


<template name="elementForm">
  {{#let el=getEl}}
    <input value="{{el.name}}"/>
  {{/let}}
</template>

父模板

Template.container.onCreated(function onContainerCreated() {
  // From data
  let item = {
    _id: '123abc',
    elements: [
      {name: "El #1"},
      {name: "El #2"},
      {name: "El #3"},
    ]
  }
  // Create master ReactiveVar
  this.item = new ReactiveVar(item);
});

Template.container.helpers({
  getItem() {
    return Template.instance().item.get();
  },
});
Template.container.events({
  // Add a new element (Will refresh all elements render)
  'click button[name="add"]'(e) {
    let instance = Template.instance()
    let item = instance.item.get()
    item.elements.push({name: "new El"})
    instance.item.set(item)
  },
})

子模板

/* Element */
Template.elementForm.onCreated(function onElementCreated() {
  this.el = new ReactiveVar(this.data)
});
Template.elementForm.helpers({
  getEl() {
    return Template.instance().el.get()
  }
})
Template.elementForm.events({
  'change input'(e) {
    let instance = Template.instance()
    let el = instance.el.get()
    el.name = e.currentTarget.value
    instance.el.set(el)
  }
})

预览

由于流星无法集成到嵌入代码中,所以我制作了一些图像。

默认页面

更新输入不影响容器渲染

添加一个新项目,将重新渲染所有容器并获取新值

解决方案 1 ?itemReactiveVar 发送给孩子

我知道将整个 ReactiveVar 发送给孩子可能是对孩子使用 .set() 方法的解决方案。

但如果我使用这个 hack,我将不得不将整个 item 发送给每个 elementForm(不是最好的)

示例

/* Html */
<Template name="parentTemplate">
  <small>{{getFoo}}</small>
  {{> childTemplate getItem}}
</Template>
<Template name="childTemplate">
  <input value="{{getFoo}}" />
</Template>

/* Parent */
Template.parentTemplate.onCreated(function onParentCreated() {
  this.item = new ReactiveVar({ foo: bar})
});
Template.parentTemplate.helpers({
  getFoo() {
    return Template.instance().item.get().foo    
  },
  getItem() {
    return Template.instance().item // Send reactive var, net value (.get)
  }
})
/* Child */
Template.childTemplate.onCreated(function onChildCreated() {
  this.item = this.data.item // Use Parent Reactive var
});
Template.childTemplate.helpers({
  getFoo() {
    return Template.instance().item.get().foo    
  },
})
 Template.childTemplate.events({
  'change input'(e) {
    let item = Template.instance().item.get()   
    item.foo = e.currentTarget.value
    Template.instance().item.set(item) // Will update parent   
  },
})

解决方案 2 ? 回调

另一种解决方案是向子模板发送除了元素数据之外的每个操作(如更改)的回调,以通知父模板更新特定元素的主 ReactiveVar。

示例

/* Html */
<Template name="parentTemplate">
  <small>{{getFoo}}</small>
  {{> childTemplate getData}}
</Template>
<Template name="childTemplate">
  <input value="{{getFoo}}" />
</Template>

/* Parent */
Template.parentTemplate.onCreated(function onParentCreated() {
  this.item = new ReactiveVar({ foo: bar})
});
Template.parentTemplate.helpers({
  getFoo() {
    return Template.instance().item.get().foo    
  },
  getData() {
    let instance = Template.instance()
    return {
      item: instance.item.get(),
      onChange: function(val) {
        // Update item here
        let item = instance.item.get()
        item.foo = val
        instance.item.set(item)
      }
    }
  }
})
/* Child */
Template.childTemplate.onCreated(function onChildCreated() {
});
Template.childTemplate.helpers({
  getFoo() {
    return Template.instance().data.item.foo    
  },
})
 Template.childTemplate.events({
  'change input'(e) {
    // Call parent callback
    Template.instance().data.onChange(e.currentTarget.value)  
  },
})

最后一个问题

处理父/子模板以处理来自项目对象的元素列表的最佳方法是什么?

【问题讨论】:

    标签: javascript templates meteor meteor-blaze


    【解决方案1】:

    其实是个好问题。

    恕我直言,视情况而定,我主要采用您的解决方案 1。

    类似:

    Template.container.onCreated(function(){
      //each elements are ReactiveVar
      let item = {
        _id: '123abc',
        elements: [
          {name: new ReactiveVar("El #1")},
          {name: new ReactiveVar("El #2")},
          {name: new ReactiveVar("El #3")},
        ]
      }
      // I don't wrap the wole item on a ReactiveVar if I don't need it.
    });
    

    然后,当您使用 #each 对其进行迭代时,您只会将当前元素数据注入到您孩子的数据上下文中。从这里,您可以轻松做到:

    Template.childTemplate.helpers({
      getName() {
        return this.name.get();    
      },
    })
     Template.childTemplate.events({
      'change input'(e, tp) {
        newVal = e.currentTarget.value
        tp.currentData().name.set(newVal); 
      }
    });
    

    我使用了一些“快捷方式”作为Template.currentData(),并使用this.name.get() 直接访问助手上的数据上下文。

    您也可以直接访问模板实例作为事件的第二个参数:

    'change input'(event, templateInstance) {}
    

    我没有测试它,我只是给你一个想法。

    希望这会对你有所帮助。

    【讨论】:

    • 即使您没有ReactiveVar,您的代码是否会在container 上更新item?您是否在子模板上创建一个?
    • 为什么要将 item 包装在 reactiveVar 上?您想要更新的唯一内容是元素的数据。所以是的,从某种意义上说,您可以随时根据每个孩子的 ReactiveVar 对您的父母进行评估。
    • 嗯好吧!我错过了现在每个元素都是ReactiveVar 的部分。它工作正常,但现在item 对象(在我的情况下是一个 mongodb 对象,使用meteor-astronomy 包)没有关于对儿童模板进行的更新的信息。我想当它用于反应性对象(而不仅仅是单个变量)时,我会为强烈的父/子模板使用回调
    • 然后在每个 reactiveVar 的位置使用流星 mongobd 游标。在我的例子中,我只使用 reactiveVar 来存储 UI 状态(以一种比使用 Session 更好的方式)。光标也是反应式的!
    • 在我的例子中,它是一个 Mongo 对象上的嵌套元素列表。 (示例(Users.emails = [ Emails ])所以我没有光标,在我的情况下我使用回调,但很高兴知道更简单的父/子问题。
    猜你喜欢
    • 1970-01-01
    • 2013-10-08
    • 1970-01-01
    • 2020-08-06
    • 2017-11-09
    • 2011-05-13
    • 1970-01-01
    • 2016-06-21
    • 2016-05-15
    相关资源
    最近更新 更多