Meteor - 如何使用Deps使其“响应式”?

7
在我的客户端上,我展示了一个用户列表和每个用户在数据库中存储的积分的小图表(使用名为sparklines的jQuery插件)。
绘制图表是在Template.rendered方法中完成的。
// client/main.js
Template.listItem.rendered = function() {
    var arr = this.data.userPoints // user points is an array of integers
    $(this.find(".chart")).sparkline(arr);
}

现在我有一个Meteor方法在服务器端,定期调用以更新用户积分。
Meteor.methods({
    "getUserPoints" : function getUserPoints(id) {
        // access some API and fetch the latest user points
    }
});

现在我希望图表可以在每次调用Meteor方法时自动更新。我有一个在模板上运行并调用此Meteor方法的方法。
Template.listItem.events({
    "click a.fetchData": function(e) {
        e.preventDefault();
        Meteor.call("getUserPoints", this._id);
    }
});

我该如何将这段代码转化为“响应式”的?


Meteor核心开发者Sashko Stubailo最近发布了一个响应式方法的包。 - Dan Dascalescu
2个回答

15

你需要结合Tracker反应式数据源SessionReactiveVar)来使用。

使用 ReactiveVar:

if (Meteor.isClient) {
    Template.listItem.events({
        "click a.fetchData": function(e) {
            e.preventDefault();
            var instance = Template.instance();
            Meteor.call("getUserPoints", this._id, function(error, result) {
                instance.userPoints.set(result)
            });
        }
    });

    Template.listItem.created = function() {
      this.userPoints = new ReactiveVar([]);
    };

    Template.listItem.rendered = function() {
        var self = this;
        Tracker.autorun(function() {
            var arr = self.userPoints.get();
            $(self.find(".chart")).sparkline(arr);
        })
    }
}

使用会话:

if (Meteor.isClient) {
    Template.listItem.events({
        "click a.fetchData": function(e) {
            e.preventDefault();
            Meteor.call("getUserPoints", this._id, function(error, result) {
                Session.set("userPoints", result);
            });
        }
    });

    Template.listItem.rendered = function() {
        var self = this;
        Tracker.autorun(function() {
            var arr = Session.get("userPoints");
            $(self.find(".chart")).sparkline(arr);
        })
    }
}

这些实现之间的区别:

ReactiveVar类似于Session变量,但有一些不同之处:

ReactiveVar没有全局名称,如Session.get("foo")中的"foo"。相反,它们可以在本地创建和使用,例如附加到模板实例,如:this.foo.get()。

ReactiveVar不会在热代码推送期间自动迁移,而Session状态会。

ReactiveVar可以保存任何值,而Session变量仅限于JSON或EJSON。

Deps已被弃用,但仍可使用。


我如何在不使用会话的情况下完成这个任务?单个页面上可能有很多“用户”视图... - ericbae

7

最容易扩展的解决方案是将数据存储在一个本地集合中——通过传递一个空名称,该集合将既是本地的又是会话性的,因此您可以将所需内容放入其中,仍然实现所有响应性的好处。如果将getUserPoints的结果插入到此集合中,您只需要编写一个助手程序即可获取每个用户的适当值,并且它会自动更新。

userData = new Meteor.Collection(null);

// whenever you need to call "getUserPoints" use:
Meteor.call("getUserPoints", this._id, function(err, res) {
    userData.upsert({userId: this._id}, {$set: {userId: this._id, points: res}});
});

Template.listItem.helpers({
    userPoints: function() {
        var pointsDoc = userData.findOne({userId: this._id});
        return pointsDoc && pointsDoc.points;
    }
});

有一个另外的方式可以使用Tracker包(以前是Deps),这种方法实现起来很快,但扩展性较差。基本上,您可以设置一个新的Tracker.Dependency来跟踪用户点数的更改:

var pointsDep = new Tracker.Dependency();

// whenever you call "getUserPoints":
Meteor.call("getUserPoints", this._id, function(err, res) {
    ...
    pointsDep.changed();
});

那么只需在您的listItem模板中添加一个虚拟辅助函数(即故意不返回任何内容的辅助函数):

<template name="listItem">
    ...
    {{pointsCheck}}
</template>

Template.listItem.helpers({
    pointsCheck: function() {
        pointsDep.depend();
    }
});

虽然这不会返回任何内容,但当调用pointsDep.changed()时(当接收到新用户积分数据时),它将强制重新渲染模板。


太棒了!我不知道可以将一个集合设置为本地使用! - Jammer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接