流星:订阅的集合不会自动更新(反应性)

时间:2013-12-20 18:34:53

标签: mongodb templates meteor

我遇到的问题是,向订阅的Collection添加数据不会自动刷新集合中显示的元素。如果我添加一个新元素,元素会显示一秒钟,然后消失!刷新浏览器(F5)并显示新元素。 我将订阅放入Meteor.autorun,但事情仍然保持不变。

lists.html(客户端):

    <<template name="lists">
  <div class="lists col-md-12" {{!style="border:1px red solid"}}>
        <!-- Checklist Adder -->
            <form id="list-add-form" class="form-inline" role="form" action="action">
              <div class="col-md-6">
                <input class="form-control" id="list-name" placeholder="Neue Liste" required="required"/>
              </div>
                 <button type="submit" class="btn btn-primary" id="submit-add">
                      <span class="glyphicon glyphicon-plus-sign"></span>
                      Neue Liste
                  </button>
            </form>
        <!-- Checklist Ausgabe -->
            <ul>
              <br/>
                {{#each lists}}
                    <li style="position: relative;"  id="{{this._id}}" data-id="{{_id}}" class="clickOnList">
                        <!--<input type="button" class="deleteLists" id="{{this._id}}" value="-" style="z-index: 999;"/> -->
                        <span  id="{{this._id}}" data-id="{{_id}}" style="padding-left: 10px; vertical-align:middle;">{{this.name}}</span>
                        <form id="changerForm_{{_id}}" class="changeList-name-form" data-id="{{_id}}" style="visibility: hidden; position: absolute; top:0;">
                            <input id="changerText_{{_id}}"  type="text" class="list_name" data-id="{{_id}}" value="{{this.name}}" />
                        </form>
                        {{#if ownerOfList this._id}}
                        <a data-toggle="modal" class="userForListModal" id="{{this.name}}" data-id="{{this._id}}" data-target="#userForListModal">
                          <span class="glyphicon glyphicon-user" id="{{this.name}}" style="color:black;"data-id="{{this._id}}"></span><span style="color:black;" id="{{this.name}}" data-id="{{this._id}}" style="font-size: small; vertical-align: super">{{memberCount this._id}}</span></a>

                        <div class="deleteLists" id="dLBtn_{{_id}}"  data-id="{{this._id}}" style="float: right; padding-right: 5px; padding-top: 1px; visibility: hidden;">
                            <span class="deleteLists glyphicon glyphicon-minus-sign" data-id="{{this._id}}"></span>
                        </div>
                        {{else}}
                        <a class="userForListModal">
                          <span class="glyphicon glyphicon-user" style="color:black;"></span><span style="color:black;" style="font-size: small; vertical-align: super">{{memberCount this._id}}</span></a>
                        {{/if}}
                        <!--        <button type="submit" class="deleteLists btn btn-default btn-xs" id="dLBtn_{{_id}}"  data-id="{{this._id}}" style="float: right;"  > -->
                        </button>
                    </li>

                {{/each}}
            </ul>
  </div>


<div class="modal fade" id="userForListModal" >
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
        <h4 class="modal-title" id="userForListModalLabel"></h4>
      </div></template>
      <div class="modal-body col-md-12">
        <div id="userForListModalUsers">

        </div>
        <p>Neuen Benutzer zur Checkliste hinzufügen:</p>
        <form id="list-addUser-form" class="form-inline" role="form" action="action">
          <div class="col-md-12">
            <div class="col-md-6">
              {{inputAutocomplete settings id="name-list-addUser" class="input-xlarge" placeholder="Benutzer Name" required="required"}}
            </div>
            <div class="col-md-6">
              <button type="submit" class="btn btn-secondary" id="submit-list-addUser">
                <span class="glyphicon glyphicon-plus-sign"></span>
                  Benutzer hinzufügen
              </button>
            </div>
          </div>
        </form>
      </div>
      <div class="modal-footer">
        <div id="userForListModalerrorMessage" style="color:red; display: none; text-align:left"></div><button type="button" class="btn btn-default" data-dismiss="modal">Schließen</button>
      </div>
    </div><!-- /.modal-content -->
  </div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</template>
<template name="userPill">
    <span class="label" style="color:black">{{username}}</span>

lists.js(客户):

Template.lists.lists = function(){ return Lists.find(); }


Lists = new Meteor.Collection("lists");


Deps.autorun(function() {
    Meteor.subscribe('lists');
})

lists.js

    var activeListName = "";
var activeListID = "";

Template.lists.lists = function()
{
        return Lists.find();
}

Template.lists.memberCount = function(id)
{
     var count = "";
     Meteor.call("listMemberCount", id, function(error,result)
                  {
                    if (error) {
                         console.log("List not initialized:" + error.reason);
                    }
                    else
                    {
                         Session.set("countMember_"+id,result);
                    }
                  });
     return Session.get("countMember_"+id);
}
Template.lists.ownerOfList = function(id)
{
     return ( Meteor.userId() == Lists.findOne({_id : id}).owner);
}
Template.lists.settings = function()
{
     return {
          position: "top",
          limit: 5,
          rules: [
          {
               token: '',
               collection: Meteor.users,
               field: "username",
               template: Template.userPill
          }]
     }
}
Template.lists.events({
     'submit #list-add-form'  : function(e, t) {
          /* Checklisten ausgeben */
          e.preventDefault();
          var name = t.find('#list-name').value;
          var id = new Meteor.Collection.ObjectID().valueOf();
          var id_block = new Meteor.Collection.ObjectID().valueOf();

         Lists.insert({_id : id, name : name, owner : Meteor.userId()});
         Meteor.users.update({_id : Meteor.userId()}, {$addToSet :{lists : id}});
         Listitems.insert({_id : id_block, name : "", items: []});
         Lists.update(id, {$addToSet : {items : id_block}});

     },
     'click .clickOnList' : function(e)
     {
        /* Eventhandler fuer klick auf Checkliste */
        Session.set("activeListId", e.target.id);
        $("#"+e.target.id).siblings('li').removeClass("active");
        $("#"+e.target.id).addClass("active");


     },
     'mouseover .clickOnList' : function (e,t) {
         $( ".deleteLists" ).each(function( index, item ) {
             if ( item.getAttribute("data-id") == e.target.getAttribute("data-id")) {
                 item.style.visibility = 'visible';
             } else {
                 item.style.visibility = 'hidden';
             }
         });
     },
     'mouseleave .clickOnList' : function (e,t) {
         $( ".deleteLists" ).each(function( index, item ) {
                  item.style.visibility = 'hidden';

         });
     },


     'click .deleteLists' : function(e, t)
     {
         /* Eventhandler zum loeschen einer Checkliste */
        var id = e.target.getAttribute("data-id");
        Meteor.call("removeList", id);
        console.log("test");
     },
     'click .changeListnameButton' : function(e,t) {
         var id = e.target.getAttribute("data-id");

         document.getElementById("changerForm_" + id).style.visibility = 'visible';
         document.getElementById(id).style.visibility = 'hidden';
         document.getElementById("changerText_" + id).focus();
     },
     'dblclick .clickOnList' : function(e,t){
         var id = e.target.getAttribute("data-id");

         document.getElementById("changerForm_" + id).style.visibility = 'visible';
         document.getElementById(id).style.visibility = 'hidden';
         document.getElementById("changerText_" + id).focus();


       },
     'submit .changeList-name-form' : function(e,t) {
         e.preventDefault();
         var id = e.target.getAttribute("data-id");
         var text = document.getElementById("changerText_" + id).value;
         if(text != '') {
             Meteor.call("changeListName", id, text);
         }

         if (Session.get("activeListId", e.target.id) == id ) {
             Session.set("activeListName", text);
         }


         document.getElementById("changerForm_" + id).style.visibility = 'hidden';
         document.getElementById(id).style.visibility = 'visible';



     },
     'blur .list_name' : function(e,t) {
         e.preventDefault();
         var id = e.target.getAttribute("data-id");
         var text = document.getElementById("changerText_" + id).value;
         if((text != '') && (document.getElementById(id).style.visibility == 'hidden')) {
             Meteor.call("changeListName", id, text);
         }

         if (Session.get("activeListId", e.target.id) == id ) {
             Session.set("activeListName", text);
         }

         document.getElementById("changerForm_" + id).style.visibility = 'hidden';
         document.getElementById(id).style.visibility = 'visible';



     },
     'click .userForListModal' : function(e,t) {
          e.preventDefault();
          activeListName = e.target.id;
          activeListID = e.target.getAttribute("data-id");
          //console.log(activeListID + " " + activeListName);
          //console.log("New user for Liste" + Lists.findOne({_id : activeListID}).name);
          userForList(activeListID);
          $("#userForListModalLabel").html("Benutzer der Liste '"+ activeListName+ "'");

     },
     'submit #list-addUser-form' : function(e,t) {
          e.preventDefault();
          var newUser = $('#name-list-addUser').val();
          Meteor.call("addUserToList", newUser, activeListID, function(error,result)
                  {
                    if (error) {
                         console.log(error.reason);
                    }
                    else
                    {
                         if (result == 1) {
                              $('#userForListModalerrorMessage').fadeIn(1000, function() {$(this).delay(1000).fadeOut(1000);});
                              $('#userForListModalerrorMessage').html("<div class=\"alert alert-danger\">Benutzer wurde nicht gefunden...</div>");
                         }
                         else if (result == 2) {
                              $('#userForListModalerrorMessage').fadeIn(1000, function() {$(this).delay(1000).fadeOut(1000);});
                              $('#userForListModalerrorMessage').html("<div class=\"alert alert-warning\">Benutzer ist Besitzer der Liste...</div>");
                         }
                    }
                  });
     }
 });

function userForList(id)
{
     try
     {
          var owner = Lists.findOne({_id : id}).owner;
          var members = Lists.findOne({_id : id}).member;          
     }
     catch(e){

     }
     output = "<ul>";
     output += "<li> Besitzer der Liste: <ul><li>" + owner + "</li></ul></li>";
     output += "<li>Mitarbeiter der Liste: <ul>"
     if (members != undefined) {
          for(i=0; i<members.length; i++)
          {
               output+= "<li>" + members[i] + "</li>";
          }
     }

     output += "</ul></li></ul>";
     $('#userForListModalUsers').html(output);
}

main.js(服务器):

Lists = new Meteor.Collection("lists");
Meteor.publish("lists", function(){

    var ListsOfUser = Meteor.users.findOne({_id : this.userId}).lists;
    return Lists.find({_id :{ $in : ListsOfUser}});
});

Lists.allow({

    insert : function(userID, list)
    {
        return (userID && (list.owner === userID));
    },
    //todo
    update : function(userID)
    {
        return true;
    },

    //todo
    remove : function(userID)
    {
        return true;
    }
});

提前致谢!

3 个答案:

答案 0 :(得分:5)

我相信这种情况正在发生,因为Meteor.publish“lists”函数中的ListsOfUser变量不是反应式数据源。 ListsOfUser是从结果集中绘制的数组,而不是反应光标。因此,当用户在客户端上添加新列表时,它不会在服务器端无效。来自Meteor文档(特别注意最后一句):

If you call Meteor.subscribe within a reactive computation, for example using
Deps.autorun,the subscription will automatically be cancelled when the computation
is invalidated or stopped; it's not necessary to call stop on subscriptions made
from inside autorun. However, if the next iteration of your run function subscribes
to the same record set (same name and parameters), Meteor is smart enough to skip a
wasteful unsubscribe/resubscribe.

当用户添加新列表时,ListsOfUser不会更改,因此您不会取消订阅并重新订阅列表发布。 (另请注意,Meteor.users.findOne()也不是反应式数据源 - 您可能希望将其切换为Meteor.users.find(),具体取决于您如何使ListsOfUser反应)。

有几种方法可以让用户列表被动反应。

首先,您可以单独发布用户光标和列表光标,也可以在同一个发布函数中作为数组发布,将两个订阅放在Deps.autorun中,然后在帮助器中删除用户列表客户端

Meteor.publish("userWithLists", function(){
  return Meteor.users.find(
      {_id: this.userId},
      {fields: {'lists': 1}}
    );
  });

其次,您可以将用户列表的静态数组发布为自己的Collection,然后使用cursor.observe或cursor.observeChanges来跟踪它何时更改。虽然我的理解是这最接近“正确”或“流星”的方式,但它显然也很冗长,我没有尝试过。本教程详细介绍了如何处理这样的事情:https://www.eventedmind.com/feed/aGHZygsphtTWELpKZ

第三,你可以简单地将用户列表粘贴到已经被动的Session对象中,然后根据Session发布你的Lists.find(),即:

Meteor.publish("lists", function(lists){/* find code goes here */});

Deps.autorun(function(){
  Meteor.subscribe("lists", Session.get("listsOfUser"));
});

最后一个可能是对Session对象的过度使用/滥用,特别是如果您的listsOfUser变大,但应该作为黑客工作。

答案 1 :(得分:1)

我知道这个问题已经过时了,但仍有人可能会寻求答案。答案是Meteor.publishComposite,可以使用发布复合包 - https://atmospherejs.com/reywood/publish-composite

那里有一个例子

Meteor.publishComposite('topTenPosts', {
    find: function() {
        // Find top ten highest scoring posts
        return Posts.find({}, { sort: { score: -1 }, limit: 10 });
    },
    children: [
        {
            find: function(post) {
                // Find post author. Even though we only want to return
                // one record here, we use "find" instead of "findOne"
                // since this function should return a cursor.
                return Meteor.users.find(
                    { _id: post.authorId },
                    { limit: 1, fields: { profile: 1 } });
            }
        },
        {
            find: function(post) {
                // Find top two comments on post
                return Comments.find(
                    { postId: post._id },
                    { sort: { score: -1 }, limit: 2 });
            },
            children: [
                {
                    find: function(comment, post) {
                        // Find user that authored comment.
                        return Meteor.users.find(
                            { _id: comment.authorId },
                            { limit: 1, fields: { profile: 1 } });
                    }
                }
            ]
        }
    ]
});

答案 2 :(得分:-1)

我对流星很新,但是,在你的服务器代码中,它不应该是:

var ListsOfUser = Meteor.users.findOne({_ id:Meteor.userId})。lists;

而不是:

var ListsOfUser = Meteor.users.findOne({_ id:Meteor.userId})。lists;