【问题标题】:In Meteor, how to update one db field when another field changes?在 Meteor 中,如何在另一个字段更改时更新一个 db 字段?
【发布时间】:2012-09-17 17:38:07
【问题描述】:

一般问题:在 Meteor 中,实现在模型更新时触发的业务逻辑的最佳方式是什么——例如,用于更新依赖字段或验证或...

具体示例:我想在Meteor todos example 的Lists 集合中添加一个“slug”字段。每当列表名称更改时,slug 都需要自动更新。

这就是我所拥有的...我正在观察 每一个 对列表的更改,以查看是否需要创建/更新其 slug。这是在 shared models.js 中(运行服务器和客户端,以获得延迟补偿的好处):

// Lists -- {name: String}
Lists = new Meteor.Collection("lists");

var listsObserver = Lists.find().observe({
    added: updateSlug,
    changed: updateSlug
});

function updateSlug(doc, idx) {
    var slug = (doc.name || '').replace(/\W+/g, '-').toLowerCase();
    if (slug !== doc.slug) {
        console.log("Updating slug for '" + doc.name + "' to " + slug);
        Lists.update(doc._id, {$set: {slug: slug}});
    }
}

(和最初的 todos 示例一样,server/publish.js 将所有 Lists.find() 发布为“列表”,client/todos.js 订阅该集合。)

上面的代码似乎可以工作,但对我来说似乎不太合适。问题:

  1. 这样观察 Lists 集合是否合理?这好像是 它可能效率低下 - 任何对 Lists 文档的更改都会触发此代码。
  2. 我应该做一个不同的(模拟的)更新客户端,还是可以让 两者都运行相同的 Mongo/Minimongo 更新?
  3. 我是否需要在某个时候调用listsObserver.stop() 来处理观察者? 如果是的话,什么时候?

(我刚刚开始使用 Meteor,所以也许我对其他环境的偏见正在泄漏。这里隐含的元问题是,我是否以正确的方式思考这个问题?)

【问题讨论】:

  • 嗯...刚刚在有关 Meteor 数据验证的旧问题中找到 this comment。好像说你应该实现 RPC 风格的 Meteor.methods 进行模型编辑,然后阻止客户端的 minimongo 模型自动同步回服务器。 (希望这不是建议。自动、实时、双向同步是 Meteor 的一大吸引力。)

标签: meteor


【解决方案1】:

我建议使用 Collection-Hooks 包。它使用 before 和 after 钩子扩展了收集操作。这比拥有大量的 collection Observes 或 ObserveChanges 要好,尤其是在收集观察的开销会变得非常大的服务器上。

这适用于客户端和服务器。如果您在客户端实现它,您将获得更新本地集合(延迟补偿)的好处,并且更改将被推送到服务器,因此无需再次执行。

您还可以获得只执行一个 MongoDB 操作的好处,而不是像使用观察或观察更改那样执行两个或更多操作。

你可以这样使用它:

var beforeInsertSlug = function(userId, doc) {
    var slug = (doc.name || '').replace(/\W+/g, '-').toLowerCase();
    if (slug !== doc.slug) {
        console.log("Updating slug for '" + doc.name + "' to " + slug);
        doc.slug = slug;
    }
};

var beforeUpdateSlug = function(userId, doc, fieldNames, modifier, options){
    if(modifier && modifier.$set && modifier.$set.doc && _.isString(modifier.$set.doc.name)){
        var slug = (modifier.$set.doc.name || '').replace(/\W+/g, '-').toLowerCase();
        if (slug !== doc.slug) {
            console.log("Updating slug for '" + modifier.$set.doc.name + "' to " + slug);
            modifier.$set.doc.slug = slug;
        }
    }

};

Lists.before.insert(beforeInsertSlug);

Lists.before.update(beforeUpdateSlug);

你可以在这里找到包:https://atmospherejs.com/matb33/collection-hooks

【讨论】:

  • 哇,我什至不知道它的存在!
  • 这正是我希望在 Meteor 核心中找到的。
【解决方案2】:

我在服务器代码中做了类似的事情。基本上将此代码与您想要对 Lists 集合进行的任何其他检查和更新一起放入 Meteor.methods() 中。

虽然下面的代码看起来有点乱,而且用 var slug 开头的那行肯定很难理解:

Meteor.methods({
   myupdate: function (doc) {

     var slug = (doc.name || '').replace(/\W+/g, '-').toLowerCase();

     if (slug !== doc.slug) {
        console.log("Updating slug for '" + doc.name + "' to " + slug);
        Lists.update(doc._id, {$set: {slug: slug}});
     }
   }
});

【讨论】:

  • Meteor 是如何知道每次有人试图修改列表名称时运行myupdate 的?还是每个可能修改列表名称的程序员只需要知道之后调用myupdate? (我正在寻找可以自动运行并且防弹的东西。)
  • 什么情况下会修改列表名?即将更改写入数据库?如果是这种情况,只需将上面的代码结合到您的主要编写中。如果不让我知道,解决方案可能是使用 observeChanges() 将代码放入 Publish() 中。
【解决方案3】:

实现这一点的一种方法是定义一个自定义模板函数并在正在更改的模板中触发它。例如:

在 client.js 中

Template.myTemplate.custom_function_to_update = function() {
    // do my update code.  i.e. MyCollections.Update(...);
}

在带有模板的html文件中

<template name="myTemplate">
    <!-- Normal template code -->
    {{ custom_function_to_update }}
</template>

并且每次模板“myTemplate”更新时,它都会调用你的方法。

【讨论】:

  • 但是模板代码只在客户端运行,并且只针对那个特定的模板,对吧?因此,在不触发业务逻辑的情况下,很容易将更改滑入模型中。使用 todo 示例,如果只有该模板负责使 list.slug 保持最新,那么任何其他修改 list.title 的代码都会留下不同步的 slug。
  • 这也将为每个打开该模板的客户运行一次更新,并不理想。
猜你喜欢
  • 2019-11-18
  • 1970-01-01
  • 1970-01-01
  • 2020-06-05
  • 2015-04-29
  • 1970-01-01
  • 1970-01-01
  • 2022-12-15
  • 1970-01-01
相关资源
最近更新 更多