在 Meteor 中如何有条件地向客户端发送数据?

Posted

技术标签:

【中文标题】在 Meteor 中如何有条件地向客户端发送数据?【英文标题】:How do you conditionally send data to the client in Meteor? 【发布时间】:2012-12-14 00:13:41 【问题描述】:

我试图弄清楚如何有条件地将数据发送到meteor 中的客户端。我有两种用户类型,根据用户类型,他们在客户端上的界面(因此他们需要的数据不同)。

假设用户的类型为counselorstudent。每个用户文档都有类似role: 'counselor'role: 'student' 的内容。

学生有sessionsRemainingcounselor等学生特定信息,辅导员有pricePerSession等信息。

我如何确保客户端的Meteor.user() 拥有我需要的信息,而且没有多余的信息?如果我以学生身份登录,Meteor.user() 应该包括 sessionsRemainingcounselor,但如果我以辅导员身份登录,则不会。我想我可能正在搜索的是流星术语中的有条件的出版物和订阅。

【问题讨论】:

我现在得到了一些答案,但我不确定我知道如何选择最好的,因为它们似乎都在表面上起作用。我想我想要最简单、最“流星”的版本,它适用于更复杂的情况(即角色不互斥时等) 在这种情况下,您可能应该选择@debergalis 的答案,因为他是 Meteor 的创造者之一。没有比这更流星了:) 【参考方案1】:

使用 fields 选项仅从 Mongo 查询中返回您想要的字段。

Meteor.publish("extraUserData", function () 
  var user = Meteor.users.findOne(this.userId);
  var fields;

  if (user && user.role === 'counselor')
    fields = pricePerSession: 1;
  else if (user && user.role === 'student')
    fields = counselor: 1, sessionsRemaining: 1;

  // even though we want one object, use `find` to return a *cursor*
  return Meteor.users.find(_id: this.userId, fields: fields);
);

然后在客户端调用

Meteor.subscribe('extraUserData');

订阅可以在 Meteor 中重叠。因此,这种方法的巧妙之处在于,向客户端发送额外字段的发布功能与 Meteor 发送基本字段(如用户的电子邮件地址和个人资料)的幕后发布功能一起工作。在客户端,Meteor.users 集合中的文档将是两组字段的并集。

【讨论】:

一个微妙的提示:如果用户的角色可以动态改变,这个发布者不会注意到这一点并改变它正在发布的字段。如果你需要做类似的事情,你目前必须在observe 之上手动实现它;希望将来 Meteor 能够以某种方式进行完全反应式发布。 另请注意,由于一旦extraUserData 被标记为就绪,Meteor.user() 将发生变化,所有自动运行将重新运行两次:一次在登录用户首次加载时,一次在extraUserData已加载。为避免这种情况,请改用Meteor.userId():它只会更改一次。 另请注意,在合并重叠记录集(游标)时,仅比较***值。这意味着,如果一个订阅包含a: x: 'x' ,而另一个订阅包含a: y: 'y' (对于具有相同_id 的文档),那么客户端将不会像您预期的那样获得a: x: 'x', y: 'y' ,而是任意原件之一。请参阅this open issue,在this closed issue 中有更好的描述。【参考方案2】:

默认情况下,Meteor 用户仅发布其基本信息,因此您必须使用 Meteor.publish 手动将这些字段添加到客户端。值得庆幸的是,Meteor docs on publish 有一个示例向您展示如何执行此操作:

// server: publish the rooms collection, minus secret info.
Meteor.publish("rooms", function () 
  return Rooms.find(, fields: secretInfo: 0);
);

// ... and publish secret info for rooms where the logged-in user
// is an admin. If the client subscribes to both streams, the records
// are merged together into the same documents in the Rooms collection.
Meteor.publish("adminSecretInfo", function () 
  return Rooms.find(admin: this.userId, fields: secretInfo: 1);
);

基本上,您希望发布一个通道,该通道在满足条件时向客户端返回某些信息,而在不满足条件时返回其他信息。然后你在客户端订阅那个频道。

在您的情况下,您可能希望在服务器中使用这样的东西:

Meteor.publish("studentInfo", function() 
  var user = Meteor.users.findOne(this.userId);

  if (user && user.type === "student")
    return Users.find(_id: this.userId, fields: sessionsRemaining: 1, counselor: 1);
  else if (user && user.type === "counselor")
    return Users.find(_id: this.userId, fields: pricePerSession: 1);
);

然后在客户端订阅:

Meteor.subscribe("studentInfo");

【讨论】:

哎呀,我们重叠了。但是Meteor.user 在发布功能中不起作用。请参阅我的答案中的变体。 我的借口是我在它上面写了 // 伪代码!但你是对的 ;-) 如果用户类型/角色不是互斥的,这将如何工作? (我实际上简化了我的情况)。 我的意思是,假设您正在创建一个博客并且用户具有角色(评论版主、管理员、编辑),您可以成为其中的一种或多种。使用这些发布和订阅策略,我如何确保始终为当前用户发送信息,以及他们需要根据他们的所有角色看到的信息? 什么意思?您可以使用更多 if/else 语句扩展我为各种角色提供的示例代码。【参考方案3】:

因为 Meteor.users 是一个与任何其他 Meteor 集合一样的集合,所以您实际上可以像任何其他 Meteor 集合一样细化它的公开内容:

Meteor.publish("users", function () 
    //this.userId is available to reference the logged in user 
    //inside publish functions
    var _role = Meteor.users.findOne(_id: this.userId).role;
    switch(_role) 
        case "counselor":
            return Meteor.users.find(, fields:  sessionRemaining: 0, counselor: 0 );
        default: //student
            return Meteor.users.find(, fields:  counselorSpecific: 0 );
    
);

然后,在您的客户端中:

Meteor.subscribe("users");

因此,Meteor.user() 将根据登录用户的角色自动被截断。

这是一个完整的解决方案:

if (Meteor.isServer) 
    Meteor.publish("users", function () 
        //this.userId is available to reference the logged in user 
        //inside publish functions
        var _role = Meteor.users.findOne( _id: this.userId ).role;
        console.log("userid: " + this.userId);
        console.log("getting role: " + _role);
        switch (_role) 
            case "counselor":
                return Meteor.users.find(,  fields:  sessionRemaining: 0, counselor: 0  );
            default: //student
                return Meteor.users.find(,  fields:  counselorSpecific: 0  );
        
    );

    Accounts.onCreateUser(function (options, user) 
        //assign the base role
        user.role = 'counselor' //change to 'student' for student data

        //student specific
        user.sessionRemaining = 100;
        user.counselor = 'Sam Brown';

        //counselor specific
        user.counselorSpecific =  studentsServed: 100 ;

        return user;
    );


if (Meteor.isClient) 
    Meteor.subscribe("users");

    Template.userDetails.userDump = function () 
        if (Meteor.user()) 
            var _val = "USER ROLE IS " + Meteor.user().role + " | counselorSpecific: " + JSON.stringify(Meteor.user().counselorSpecific) + " | sessionRemaining: " + Meteor.user().sessionRemaining + " | counselor: " + Meteor.user().counselor;
            return _val;
         else 
            return "NOT LOGGED IN";
        
    ;

还有 html

<body>
    <div style="padding:10px;">
        loginButtons
    </div>

    > home
</body>

<template name="home">
    <h1>User Details</h1>
    > userDetails
</template>

<template name="userDetails">
   DUMP:
   userDump
</template>

【讨论】:

以上是关于在 Meteor 中如何有条件地向客户端发送数据?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Meteor 中使用客户端重新连接事件

如何将一个模板的一些数据发送到另一个模板 Meteor

如何有条件地向对象添加键?

如何在 Meteor 中使用 #each 块实现最后一项的条件?

如何使用 Meteor 处理文件上传?

您如何在 Meteor 中存储特定于客户端的数据服务器端?