【问题标题】:Meteor - How can I pass data between helpers and events for a template?Meteor - 如何在模板的助手和事件之间传递数据?
【发布时间】:2013-09-18 18:17:34
【问题描述】:

我对 Meteor 有点陌生,我遇到的问题是响应式数据 - 特别是在我需要根据鼠标或键盘事件更改显示的数据的情况下。用普通的 js 方式做这种事情似乎给我带来了流星的麻烦,因为我改变的所有东西都会不断地重新渲染和重置。

所以,我想我会看看这是否是我可以使用 Meteor 的 Deps 对象的情况,但我不能完全掌握它。这是我正在使用的代码:

(function(){

    var tenants = [];
    var selectedTenant = 0;

    var tenantsDep = new Deps.Dependency;

    Template.tenantsBlock.tenantsList = function()
    {
        tenants = [];
        var property = $properties.findOne({userId: Meteor.userId(), propertyId: Session.get('property')});
        var tenancies = _Utils.resolveTenancies(property, true, null, true);

        for(var i = 0; i < tenancies.length; i++)
        {
            if(tenancies[i].tenancyId == Session.get('tenancy'))
            {
                tenants = tenants.concat(tenancies[i].otherTenants, tenancies[i].primaryTenant);
            }
        }

        tenants[selectedTenant].selected = 'Selected';

        tenantsDep.changed();

        return tenants;
    };

    Template.tenantsBlock.onlyOneTenant = function()
    {
        tenantsDep.depend();

        return tenants.length > 1 ? '' : 'OneChild';
    };

    Template.tenantsBlock.phoneNumber = function()
    {
        tenantsDep.depend();

        for(var i = 0; i < tenants[selectedTenant].details.length; i++)
            if(_Utils.getDynamicContactIconClass(tenants[selectedTenant].details[i].key) == 'Phone')
                return tenants[selectedTenant].details[i].value;

        return null;
    };

    Template.tenantsBlock.emailAddress = function()
    {
        tenantsDep.depend();

        for(var i = 0; i < tenants[selectedTenant].details.length; i++)
            if(_Utils.getDynamicContactIconClass(tenants[selectedTenant].details[i].key) == 'Email')
                return tenants[selectedTenant].details[i].value;

        return null;
    };

    Template.tenantsBlock.addedDate = function()
    {
        tenantsDep.depend();
        return _Utils.timeToDateString(tenants[selectedTenant].created);
    };

    Template.tenantsBlock.events({
        'click .Name': function(e, template)
        {
            tenantsDep.depend();
            var _this = e.currentTarget;
            var tenantName = _this.innerHTML;

            $(_this).addClass('Selected');
            $(_this).siblings().removeClass('Selected');

            for(var i = 0; i < tenants.length; i++)
            {
                if(tenants[i].name == tenantName)
                    tenants[i].selected = "Selected";
                else
                    tenants[i].selected = '';
            }
        }
    })


})();

^这似乎是他们在流星文档 (http://docs.meteor.com/#deps_dependency) 中对dependency.changed() 和dependency.depend() 的理解,但所有这些都给了我一个无限循环。

那么我可以修改我声明 deps 的方式以使数据具有响应性吗?有没有更好的方法一起完成这一切?

更新:

虽然我对此持怀疑态度,但我一直倾向于尝试以本地化方式使用 Session.set/Session.get。所以,下次我必须这样做时,我就这样做了

Session.set('tenantsBlock' {tenants: [], selectedTenant: 0});

然后只需从与 Template.tenantsBlock 相关的帮助程序和事件映射中访问此变量。这样,他们都可以实时访问数据,并且在数据更改时都可以重新运行。这是我将此脚本转换成的内容(抱歉,它们都太大了):

(function()
{
Template.tenantsBlock.created = Template.tenantsBlock.destroyed =function()
{
    _Utils.setSession('tenantsBlock', {
        tenants: [],
        selectedTenant: 0
    })
};

Template.tenantsBlock.tenantsList = function()
{
    var localContext = Session.get('tenantsBlock');
    localContext.tenants = [];
    var property = $properties.findOne({userId: Meteor.userId(), propertyId: Session.get('property')});
    var tenancies = _Utils.resolveTenancies(property, true, null, true);

    for(var i = 0; i < tenancies.length; i++)
    {
        if(tenancies[i].tenancyId == Session.get('tenancy'))
        {
            localContext.tenants = localContext.tenants.concat(tenancies[i].otherTenants, tenancies[i].primaryTenant);
            break;
        }
    }

    localContext.tenants[localContext.selectedTenant].selected = 'Selected';
    Session.set('tenantsBlock', localContext);

    return localContext.tenants;
};

Template.tenantsBlock.onlyOneTenant = function()
{
    var localContext = Session.get('tenantsBlock');
    return localContext.tenants.length > 1 ? '' : 'OneChild';
};

Template.tenantsBlock.phoneNumber = function()
{
    var localContext = Session.get('tenantsBlock');
    for(var i = 0; i < localContext.tenants[localContext.selectedTenant].details.length; i++)
        if(_Utils.getDynamicContactIconClass(localContext.tenants[localContext.selectedTenant].details[i].key) == 'Phone')
            return localContext.tenants[localContext.selectedTenant].details[i].value;

    return null;
};

Template.tenantsBlock.emailAddress = function()
{
    var localContext = Session.get('tenantsBlock');
    var selectedTenantDetails = localContext.tenants[localContext.selectedTenant].details;

    for(var i = 0; i < selectedTenantDetails.length; i++)
        if(_Utils.getDynamicContactIconClass(selectedTenantDetails[i].key) == 'Mail')
            return selectedTenantDetails[i].value;

    return null;
};

Template.tenantsBlock.addedDate = function()
{
    var localContext = Session.get('tenantsBlock');
    return _Utils.timeToDateString(localContext.tenants[localContext.selectedTenant].created);
};

Template.tenantsBlock.events({
    'click .Name': function(e, template)
    {
        var localContext = Session.get('tenantsBlock');
        var _this = e.currentTarget;
        var tenantName = _this.innerHTML;

        for(var i = 0; i < localContext.tenants.length; i++)
        {
            if(localContext.tenants[i].name == tenantName)
            {
                localContext.tenants[i].selected = 'Selected';
                localContext.selectedTenant = i;
            }
            else
            {
                localContext.tenants[i].selected = '';
            }
        }

        Session.set('tenantsBlock', localContext);
    }
})

})();

【问题讨论】:

  • Session 变量中只能设置字符串。我仍然不明白你想用这段代码做什么。为什么不能把localContext.tenants 放在一个集合中,把selectedTenant 放在一个会话变量中?
  • 我不知道,伙计。 session 变量似乎可以很好地存储一个对象。需要注意的是,我无法使用 Session.get('tenantsBlock.selectedTenant') 访问它,但它仍会将“tenantsBlock”的值存储为对象并将其原样返回。也就是说,除非您知道这样做存在跨浏览器问题。
  • 我必须承认我不太习惯将控制数据放入数据库。存储和检索数据似乎是一种笨拙的方式,另外,如果我为我的应用程序中的每个模板控件 + 所有正常的数据库集合都这样做,我将拥有至少 30 个数据库集合。作为对全局可访问控制数据的妥协,Session 变量似乎更有效。
  • 您对 Session 的看法可能是正确的;但是,您可以在客户端和服务器端(不与 Mongo 同步)创建匿名集合。这些将允许您访问无法通过 Session 访问的嵌套字段。我仍然不明白您所说的“控制数据”是什么意思,因为我认为您没有非常清楚地解释您的应用需要做什么。
  • 匿名集合:new Meteor.Collection(null) 仅用于客户端/服务器集合,new Meteor.Collection("name", {connection: null}) 用于仅在内存中的客户端/服务器同步集合。根据您的解释,我将创建一个 Properties 集合和一个在属性中引用 _ids 的 Tenants 集合。然后将任何上下文放入Session 或匿名集合中。这将摆脱您在这里筛选原始 Javascript 对象的所有麻烦。看看我在那里链接的应用程序,它有一个这样做的例子。

标签: meteor reactive-programming


【解决方案1】:

您必须克服老式的做法:) Meteor 比您想象的要简单得多。 一个好的经验法则是,如果您使用 jQuery 来操作任何 DOM 元素,那么您可能做错了。 此外,如果您在不使用集合 API 的情况下访问任何数据,您最好有充分的理由这样做。

在您的情况下,您根本不需要编写任何手动依赖项。大多数 Meteor 应用程序很少需要手动依赖。

您需要做的第一件事是将所有租户放入Meteor.Collection,这将使他们更容易使用。

Tenants = new Meteor.Collection("tenants");

您的tenantsBlock 模板应如下所示(以一些不同的 html 元素为模):

<template name="tenantsBlock">
    <ol>    
    {{#each tenants}}
        <li class="name {{selected}}">
            <span>Primary Tenant: {{primaryTenant}}</span>
            <span>Other Tenants: {{otherTenants}}</span>
            <span>Phone Number: {{phoneNumber}}</span>
            <span>Email Address: {{emailAddress}}</span>
            <span>Added Date: {{addedDate}}</span>
        </li>    
    {{/each}}
    </ol>
</template>

Tenants 中的每个文档应如下所示:

{
    primaryTenant: "Joe Blow",
    otherTenants: "Mickey Mouse, Minnie Mouse",
    phoneNumber: "555-234-5623",
    emailAddress: "joe.blow@foo.com",
    addedDate: "2005-10-30T10:45Z"
}

然后,您需要的所有代码仅用于选择/取消选择,您可以删除其他所有内容:

Template.tenantsBlock.tenants = function() {
    return Tenants.find();
};

Template.tenantsBlock.selected = function() {
    return Session.equals("selectedTenant", this._id);
};

Template.tenantsBlock.events({
    'click .name': function(e) {   
        Session.set("selectedTenant", this._id);
    }
});

我再次重申,在使用 Meteor 时,您永远不应该使用 Javascript 进行 DOM 操作。您只需更新您的数据,如果一切都正确完成,您的模板将响应更新。声明您希望数据的外观如何,然后更改数据并观察其神奇之处。

【讨论】:

  • 我非常高兴听到您反对手动操作 dom。感觉就像它正在与框架作斗争,但我并不认为我有任何其他可接受的选择。也就是说,我的用例比我能真正传达的要动态得多,因为会有很多属性,每个属性都有很多租约,所有这些都可以显示在下一页上。尽管如此,我的推断是,我可能应该克服对全局变量的恐惧,不带偏见地使用 Session,并在模板被破坏时取消设置(在其他地方重新渲染)。
  • 我看到了 David Greenspan (youtube.com/watch?v=pGQ-ax5cFnk) 关于添加对“本地反应状态”的支持的演讲,也许我只需要等待它离开 Session 对象。
  • 我不清楚您要做什么,因为您没有显示任何模板。我所做的是我从您的数据中推断出的最简单的示例。我可以向您指出我的一个项目,在该项目中,许多用户同时对许多领域的大量数据进行实时编辑,您可能会发现这很有帮助:github.com/mizzao/CrowdMapper。您可能需要做的一件事是将租约及其属性放在不同的集合中,随着您对 Meteor 越来越熟悉,您将能够轻松地进行操作。
  • 我去看看。谢谢。
  • 我玩流星已经有几个星期了,这个答案帮助我了解了很多流星可以做什么。非常感谢!
【解决方案2】:

自从我在 2013 年发布这篇文章以来,Meteor 已经真正进化了。我以为 我应该发布一个现代的、优越的方法。

现在您已经能够创建ReactiveVar 有一段时间了,现在您可以将它们直接附加到模板中。 ReactiveVar 与 Session 类似,是一种反应式数据存储。然而,ReactiveVar 只保存一个值(任何类型)。

您可以将ReactiveVar 添加到项目的客户端,方法是从应用的根目录在终端中运行它:

$meteor add reactive-var

此 javascript 展示了如何在模板的 onCreatedonRenderedonDestroyedeventshelpers 之间传递变量。

Template.myTemplate.onCreated = function() {
    // Appends a reactive variable to the template instance
    this.reactiveData = new ReactiveVar('Default Value');
};

Template.myTemplate.events({
    'click .someButton': (e, template) => {
        // Changes the value of the reactive variable for only this template instance
        template.reactiveData.set('New Value');
    },
});

Template.myTemplate.helpers({
    theData: () => {
        // Automatically updates view when reactive variable changes
        return Template.instance().reactiveData.get();
    },
});

这是优越的有几个原因:

  1. 它只将变量范围限定为单个模板实例。在页面上可能有十几个模板实例且都需要独立状态的情况下特别有用。
  2. 模板消失时它会消失。使用 ReactiveVar 或 Session 变量,您必须在模板被销毁时清除该变量(如果它甚至可以预见地被销毁)。
  3. 代码更简洁。

奖励积分:请参阅ReactiveDict,了解您在页面上同时拥有多个模板实例但需要管理少数反应变量并使这些变量在会话期间持续存在的情况。

【讨论】:

    猜你喜欢
    • 2016-08-24
    • 2015-09-02
    • 2016-01-03
    • 2015-01-06
    • 1970-01-01
    • 2015-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多