0
votes

I'm currently having an issue with a collection insert in Meteor. I call a method to insert a new item into a collection. The server database shows the new item but the client side has no record of the collection. I've found if I refresh the page, my template referencing the collection populates and works.

Here is the method inserting the item located at 'lib/collections/items.js'

Items = new Mongo.Collection("items");

Meteor.methods({
  addItem: function (text) {
    if (! Meteor.userId()){
      throw new Meteor.Error("not-authorized");
    }
    console.log(text);
    console.log(Meteor.userId());
    console.log(Meteor.user().username);
    console.log(Meteor.user().household);
    Items.insert({
      text: text,
      createdAt: new Date(), //current time
      owner: Meteor.userId(),
      username: Meteor.user().username,
      household: Meteor.user().household
    });
  },

Here is the server publishing of the items located at '/server/main.js'

Meteor.publish("items", function(){
  if(typeof Meteor.users.findOne({'_id': this.userId}) === 'undefined') return null;
    return Items.find( {household : Meteor.users.findOne({'_id': this.userId}).household});
}); 

Here is the code calling the method located at 'client/main.js'

Template.body.events({
  "submit .new-task": function (event) {
    // This function is called when the new task form is submitted

    var text = event.target.text.value;
    text = text.trim();
    if(text)//Check for non-null, non-empty
      {
        text = capitalizeEachWord(text);
        console.log(text);
        Meteor.call("addItem",text);
      }

      //Clear form
      event.target.text.value = "";

      //Prevent default form submit
      return false;
    },

Here is where the collection is queried from to display in a template at 'client/templates/itemList.js'

Template.itemList.helpers({
  items: function() {
    console.log('Trying to subscribe');
    return Items.find({}, {sort : {checked: 1, createdAt: -1}});
  }

I'm just starting to learn Meteor. I appreciate any help!

Edit

Thanks for the responses,

I've tried these suggestions and still getting the same result. I've tried a different way of relating users to households now and still having the same issues. I can insert an item from the client, it exist on the server side, and yet it doesn't exist on the client side. I'm sure i'm missing something small here but for the life of me, can't find it.

I know this is a huge dump of info but here is my updated source code (still producing the same problem) and logs. I've tried to put everything I know about this issue. Thanks for any help! I'm just starting out with Meteor and I know no one who develops in it so troubleshooting is a bit tough right now.

Client Specific Code

Located at 'client/main.js'

Template.body.helpers({
  householdId: function() {
    //return Meteor.users.findOne({'_id': Meteor.userId()}).household;
    if(Meteor.userId() &&
      Households.findOne({users : {$regex : new RegExp(Meteor.userId())}}))
    {      
      console.log('Trying to get id');
      return Households.findOne({users : {$regex : new RegExp(Meteor.userId())}})._id;
    }
  }
});
Template.body.events({
  "submit .new-task": function (event) {
    // This function is called when the new task form is submitted

    var text = event.target.text.value;
    text = text.trim();
    if(text)//Check for non-null, non-empty
      {
        text = capitalizeEachWord(text);
        console.log(text);
        Meteor.call("addItem",text);
      }

      //Clear form
      event.target.text.value = "";

      //Prevent default form submit
      return false;
    },
    "submit .new-household": function (event) {
      // This function is called when the new task form is submitted
      var insertedId;

      var text = event.target.householdName.value;
      text = text.trim();
      if(text)//Check for non-null, non-empty
        {
          text = capitalizeEachWord(text);
          Meteor.call("createHousehold", text);
        }


        //Prevent default form submit
        return false;
      }
  });


Accounts.ui.config({
  passwordSignupFields: "USERNAME_ONLY",
});

Code to subscribe to my collections located at 'client/application.js'

//Sub to our collections
Meteor.subscribe('households');
Meteor.subscribe('items');

Code iterating through the items to display located at 'client/templates/itemList.js'

Template.itemList.helpers({
  items: function() {
    console.log('Trying to subscribe');
    return Items.find(
      {
        household : Households.findOne( {users : {$regex : new RegExp(this.userId)}})._id
      }
    , {sort : {checked: 1, createdAt: -1}});
  },
  householdId: function() {
    //return Meteor.users.findOne({'_id': Meteor.userId()}).household;
    if(Meteor.userId())
    {      
      console.log('Trying to get id from ');
      return Households.findOne({users : {$regex : new RegExp(Meteor.userId())}})._id;
    }
  }
});

Collection Code

Households collection code located at 'lib/collections/households.js'

Households = new Mongo.Collection("households");

Meteor.methods({
  createHousehold: function(text) {
    var insertedId;
    insertedId = Households.insert({
      householdName: text,
      createdAt: new Date(), //current time
      users: this.userId
    });
  }//Add user in the future
});

Items Collection code located at 'lib/collections/items.js'

Items = new Mongo.Collection("items");

Meteor.methods({
  addItem: function (text) {
    if (! Meteor.userId()){
      throw new Meteor.Error("not-authorized");
    }
    console.log('Inserting item')
    console.log('Item : ' + text);
    console.log('UserId : ' + Meteor.userId());
    console.log('Username : ' + Meteor.user().username);
    console.log('Household : ' + Households.findOne( {users : {$regex : new RegExp(Meteor.userId())}})._id);
    Items.insert({
      text: text,
      createdAt: new Date(), //current time
      owner: Meteor.userId(),
      username: Meteor.user().username,
      household: Households.findOne( {users : {$regex : new RegExp(Meteor.userId())}})._id
    });
    return true;
  },
  deleteItem: function (itemId) {
    Items.remove(itemId);
  },
  setChecked: function(itemId, setChecked) {
    Items.update(itemId, { $set: {checked: setChecked}});
  }
});

Server Code

The server code is located at 'server/main.js'

Meteor.publish("households", function(){
  console.log('Trying to subscribe');
  console.log(this.userId);
  if(this.userId)
  {
    //var query = { users : new RegExp("/"+this.userId+"/")};
    //console.log(Households.findOne( query ));
    console.log(Households.findOne( {users : {$regex : new RegExp(this.userId)}}));
    return Households.find( {users : {$regex : new RegExp(this.userId)}});
  }
  else
  {
    console.log('Too early');
    return null;
  }
});



Meteor.publish("items", function(){
  console.log('Trying to get items');
  if(!this.userId ||
    !Households.findOne( {users : {$regex : new RegExp(this.userId)}}))
    {
      console.log('Returning null');
      return null;
    }
    console.log(Items.findOne( {household : Households.findOne( {users : {$regex : new RegExp(this.userId)}}) }));
    console.log(this.userId);
    return Items.find( {household : Households.findOne( {users : {$regex : new RegExp(this.userId)}})._id });
  });

Logs

If I start the site on localhost using meteor. When the page comes up (no user logged in), this the server console output:

=> App running at: http://localhost:3000/
I20141209-10:26:50.719(-5)? Trying to subscribe
I20141209-10:26:50.766(-5)? null
I20141209-10:26:50.766(-5)? Too early
I20141209-10:26:50.766(-5)? Trying to get items
I20141209-10:26:50.767(-5)? Returning null

Next I successfully create a user account (using accounts-ui/accounts-password packages). This is the output to that point.

I20141209-10:31:59.562(-5)? Trying to subscribe
I20141209-10:31:59.565(-5)? null
I20141209-10:31:59.566(-5)? Too early
I20141209-10:31:59.566(-5)? Trying to get items
I20141209-10:31:59.566(-5)? Returning null
I20141209-10:32:16.145(-5)? Trying to subscribe
I20141209-10:32:16.145(-5)? 8Skhof4jL2pSguT8Q
I20141209-10:32:16.146(-5)? undefined
I20141209-10:32:16.147(-5)? Trying to get items
I20141209-10:32:16.148(-5)? Returning null

Next I create a household using the .new-household form. No new output on the server at this point but here is the output on the client side:

main.js?ba2ac06f3ff7f471a7fa97093ac9ed5c01e0c8cd:7 Trying to get id
main.js?ba2ac06f3ff7f471a7fa97093ac9ed5c01e0c8cd:7 Trying to get id
itemList.js?a224bda493b90eb94bff9b88b48bb22eaa8aefe1:3 Trying to subscribe
main.js?ba2ac06f3ff7f471a7fa97093ac9ed5c01e0c8cd:7 Trying to get id
main.js?ba2ac06f3ff7f471a7fa97093ac9ed5c01e0c8cd:7 Trying to get id
itemList.js?a224bda493b90eb94bff9b88b48bb22eaa8aefe1:3 Trying to subscribe

At this point I add an item using the .new-task form. The task displays for a blink on screen then disappears. Here is the server output at this point:

I20141209-10:37:09.171(-5)? Inserting item
I20141209-10:37:09.172(-5)? Item : Beans
I20141209-10:37:09.172(-5)? UserId : 8Skhof4jL2pSguT8Q
I20141209-10:37:09.172(-5)? Username : pedro
I20141209-10:37:09.173(-5)? Household : M5NckT6ndqhRKCeWo

Here is the client output at this point:

main.js?ba2ac06f3ff7f471a7fa97093ac9ed5c01e0c8cd:21 Beans
items.js?2ca606d1e71e2e55d91421d37f060bd5d8db98fe:8 Inserting item
items.js?2ca606d1e71e2e55d91421d37f060bd5d8db98fe:9 Item : Beans
items.js?2ca606d1e71e2e55d91421d37f060bd5d8db98fe:10 UserId : 8Skhof4jL2pSguT8Q
items.js?2ca606d1e71e2e55d91421d37f060bd5d8db98fe:11 Username : pedro
items.js?2ca606d1e71e2e55d91421d37f060bd5d8db98fe:12 Household : M5NckT6ndqhRKCeWo

At this point the Item exists within the server database, but if I, using the web console in chrome, execute the command Items.findOne(); it returns undefined.

3
I dropped your code into meteorpad.com and the web page updates for each item added without refreshing. Do you have any errors that show up in the dev console of your browser? Maybe something else is causing your error. - user728291

3 Answers

0
votes

There could be more to it, but I couldn't see there where you have included the Meteor.subscribe("items") on the client side as that is what will pass the records to the client from the publish on the server.

Also in your publish, you can use

if (this.userId) {
    //do stuff logged in
} else {
    this.stop();
}

instead of selecting the Meteor.user() record for the user ID to detect if the client is logged in or not.

0
votes

Your publication:

Meteor.publish("items", function(){
  if(user = Meteor.user()){
    return Items.find( {household : user.household});
  }
  else{
    this.ready();
  }
});

Your block helper:

Template.itemList.helpers({
  items: function() {
    return Items.find({}, {sort : {checked: 1, createdAt: -1}});
  }
});

Your subscription (client side): Meteor.subscribe("items");

If you are using iron:router. In the route you can define directly your helper and your subscription like this and just call {{items}} in your template:

    Router.route('myRoute', function(){
        this.render('myRouteTemplate');
    }, {
    path: '/myRoutePath',
    waitOn: function(){
        if(user = Meteor.user()) {
            var subscriptions = [];
            //
            // Subscribe to your subscription
            //===============================
            subscriptions.push(Meteor.subscribe('items'));

            return subscriptions;
        }
    },
    data: function(){
        var context;
        if(this.ready()) {
            if (user = Meteor.user()) {
            context = {
                items: Items.find( {household : user.household})
              };
            }           
        }
    }
    });

Always remember that when you publish a magazine, you need a subscriber process. Same thing for collections.

Cheers,

0
votes

I ended up fixing this issue by implementing Iron Router in my application and waiting on subscriptions to be ready before rendering a template. I believe that my issue before was some kind of race condition but i'm not positive on the specifics of it. Either way i'm satisfied with my solution because it fixes my issue and structures the flow of my site better and more understandably I think.