流星:每个模板范围都有反应吗?

时间:2013-02-07 13:21:05

标签: meteor handlebars.js

我在同一页面上的多个(任意多个)位置渲染相同的Handlebars模板。在每个模板中,我想要一个按钮来切换div的可见性。当我使用Session.set保存此状态时,单击一个按钮显然会切换所有模板实例中不需要的所有div。

我可以将状态保存在模板实例的数据上下文中(在this.dataTemplate.myTemplate.rendered回调中绑定到Template.myTemplate.created),但是有两个问题。

  1. this.data不是反应式数据源,因此不会传播到div
  2. 我无法访问Template.myTemplate.events中的模板实例(如meteor-core所述)
  3. 最后,我可以以某种方式将其保存到一个集合中。但是,我如何唯一地标识每个呈现的模板的状态? jQuery可能也有一种hacky方式,但这不是我想要开发应该与反应模板一起使用的Meteor应用程序的方式。特别是,当该模板变得更复杂时。

    我是否遗漏了某些东西,或者是否真的没有等同于AngularJS的控制器,每个模板实例化都会传递$scope

    更新: 我在考虑这样的事情。

    template.html:

    <template name="jsonObject">
        <input type="button" />
        <div class="{{hidden}}">content</div>
    </template>
    

    client.js:

    Template.jsonObject.hidden = function(){
        var ret = "";
        if (this._hidden) {
            ret = "hidden";
        }
        return ret;
    };
    
    Template.jsonObject.events({
        'click input' : function(event, template){
            template.data._hidden = true;
        }
    });
    

4 个答案:

答案 0 :(得分:18)

所有其他答案都过于复杂和/或过时。从Meteor 1.0开始,维护每个模板状态的建议解决方案是使用存储在reactive variables中的template instance

  

模板实例对象表示文档中出现的模板。它可以用于访问DOM,并且可以为其分配属性,这些属性在模板被反应更新时持久存在。 [...]您可以将您选择的其他属性分配给对象。

活跃变量由reactive-var core package提供:

  

ReactiveVar包含可以获取和设置的单个值,因此根据通常的响应数据源合约,调用set将使任何调用get的计算无效。

您的代码变为:

Template.jsonObject.onCreated = function () {
  this.hidden = new ReactiveVar(false);
};

Template.jsonObject.helpers({
  hidden: function () {
    return Template.instance().hidden.get() ? 'hidden' : '';
  }
});

Template.jsonObject.events({
  'click input': function (event, template) {
    template.hidden.set(true);
  }
});

HTML与您期望的相同:

<template name="jsonObject">
  <button>Click Me</button>
  <p>Hidden is {{hidden}}.</p>
</template>

答案 1 :(得分:2)

这就是我实现每个模板实例反应源的方法

我只想询问用户灭绝状态灭绝时恐龙灭绝的原因。

<template name="dinosaur">
   <label for="extinction-status">Extinction status</label>
   <select name="extinction-status">
     <option value="not-extinct">Not extinct</option>
     <option value="extinct">Extinct</option>
   </select>
   {{#if isExtinct extinctStatus newExtinctStatus extinctStatusDep}}
   <label for="extinction-reason">Reason for extinction</label>
   <input name="extinction-reason" type="text" placeholder="Why is it extinct?"/>
   {{/if}}
</template>

要反复显示原因字段,我们会在模板Deps.Dependency函数中添加this.datacreated

Template.dinosaur.created = function() {
  this.data.newExtinctStatus = null;
  this.data.extinctStatusDep = new Deps.Dependency;
};

我们会倾听用户何时更改消退状态选择,并更新newExtinctStatus上的changed并呼叫extinctStatusDep

Template.dinosaur.events({
  'change [name="extinction-status"]': function(event, template) {
    var extinctStatus = $(event.target).val();
    template.data.newExtinctStatus = extinctStatus;
    template.data.extinctStatusDep.changed();
  }
});

在助手中,我们说我们依赖于传递的Deps.Dependency

Template.dinosaur.helpers({
  isExtinct: function(status, newStatus, statusDep) {
    if (statusDep) {
      statusDep.depend();
    }
    if (newStatus) {
      if (newStatus == 'extinct') {
        return true;
      }
    else if (status == 'extinct') {
        // No new status is set, so use the original.
        return true;
    }
  }
});

这是一个涉及添加到this.data的黑客攻击,但它允许每个模板的反应性,当您依赖尚未保存到Collection中的某些内容的数据时非常有用

答案 2 :(得分:1)

更新的答案 - METEOR 0.8.3

Meteor现在提供UI._templateInstance()功能,可以访问模板助手中的模板实例。这样可以实现更清晰的实施:

1。 在下面的HTML代码中,删除{{#with templateInstanceId=getTemplateInstanceId}}

2。 用这个替换JS代码:

var templateInstanceId = 0;

Template.divWithToggleButton.created= function() 
    { 
    this.templateInstanceId = ++templateInstanceId;
    };

Template.divWithToggleButton.events(
    {
    'click button': function(event, template) 
        {
        Session.set("visible", template.templateInstanceId);
        },
    });

Template.divWithToggleButton.visible= function() 
    { 
    return Session.equals("visible", UI._templateInstance().templateInstanceId);
    };

前回答 我的项目中有相同的要求:唯一标识要与Session.set一起使用的模板实例。

这是我的简单黑客,使用上下文替换和'滑动'唯一实例ID(稍后会详细介绍):

客户端代码中的任何位置,放置:

var templateInstanceId = 0;

UI.registerHelper('getTemplateInstanceId', function()
{
    return templateInstanceId++;
});

然后在要唯一标识实例的模板中使用{{#with templateInstanceId=getTemplateInstanceId }}。在你的例子中,它可能是这样的(未经测试):

HTML:

<!-- Div with a toggle visibility button. Use as a Block Helpers (with # instead of >) -->
<template name="divWithToggleButton">
    {{#with templateInstanceId=getTemplateInstanceId }}
    <div>
        <!-- The 'toggle visibility' button -->
        <button type="button">Toggle Visibility</button>

        <!-- The div content -->
        {{#if visible}}
            {{> UI.contentBlock}}
        {{/if}}
    </div>
    {{/with}}
</template>

JS(客户代码):

Template.divWithToggleButton.events(
{
    'click button': function(event, template) 
    {
        Session.set("visible", this.templateInstanceId);
    },
});

Template.divWithToggleButton.visible= function() 
{ 
    return Session.equals("visible", this.templateInstanceId);
};

现在关于这个奇怪的'滑动'唯一实例id:

此ID在每个模板渲染时更新,我找不到其他方法。 这意味着任何新呈现都将使会话中存储的唯一ID无效。如果您的div包含反应数据源,则会发生新的渲染。根据您的情况,这可能是一个问题。

答案 3 :(得分:0)

我认为关于meteor-core的讨论已经过时(虽然它是最近的)。 Per the docs(引自下文),这应该是可能的:

Template.myTemplate.events({
   foo: function(event, template) {
      var _div = template.find('...');
   }
});
  

处理程序函数接收两个参数:event,包含事件信息的对象,以及模板,定义处理程序的模板的模板实例。处理程序还会在此处接收一些其他上下文数据,具体取决于处理事件的当前元素的上下文。在Handlebars模板中,元素的上下文是发生该元素的Handlebars数据上下文,它由块帮助程序(如#with和#each)设置。

<强>更新

假设每个可隐藏的div都有一个唯一的id,如果你尝试过类似的东西怎么办?我还没有测试过。

Template.jsonObject.hidden = function(){
    var _id = $(".classOfPossibleHiddenDiv").prop("id");
    return Session.get(_id) || false;
};

Template.jsonObject.events({
    'click input' : function(event, template){
        Session.set($(template.find(".classOfPossibleHiddenDiv")).prop("id"), true);
    }
});