Meteor: Uncaught RangeError: Maximum call stack size exceeded when clicking button


#1

I am creating a simple web application in which a user can click the follow button on a photo story (post which stores an image, title, story, username) and start following that user. The problem is that when i click the button i get the:

Uncaught RangeError: Maximum call stack size es5-shim.js:888   exceeded
    at Object.toString (<anonymous>)
    at isArguments (es5-shim.js:888)
    at Function.keys (es5-shim.js:951)
    at Function._.each._.forEach (underscore.js:111)
    at Object.EJSON.clone (ejson.js:500)
    at ejson.js:501
    at Function._.each._.forEach (underscore.js:113)
    at Object.EJSON.clone (ejson.js:500)
    at ejson.js:501
    at Function._.each._.forEach (underscore.js:113)

The app is running meteor 1.2.1. I updated to 1.3 but it didn’t fix the problem. I updated to 1.4 and 1.5 but my code is not running in these versions of meteor.I also created the same project without the photos so the users can post only a text and the follow button works fine. So i don’t know where the problem is. Below i am providing a part of the code from the version with the photos and the lines that are different between the 2 versions.

The code: collections.js:

Post = new Mongo.Collection('post');

if(Meteor.isClient) {
  Template.postsList.helpers({
    posts: function(){
      return Post.find();
    }
  })
}

Images = new FS.Collection('Images', {
  stores: [new FS.Store.GridFS('Images')],
  filter: {
    allow: {
      contentTypes: ['image/*']
    },
    onInvalid: function(message) {
      FlashMessage.sendError(message);
    }
  }
});


Images.allow({
  insert:function(){return true;},
  update:function(){return true;},
  download:function(){return true;}
});

methods.js:

Meteor.methods({
  addImageInfo: function(imageId, title, story){
    if(!Meteor.userId()){
      throw new Meteor.Error('Not Authorized');
    }

    var username = Meteor.user().username;
    //var username2 = Meteor.user().profile.name;
    Post.insert({
      title: title,
      content: story,
      imageId: imageId,
      imageUrl: '/cfs/files/Images'+imageId,
      userId: Meteor.userId(),
      username: username,
      username2: Meteor.user().profile.name,
      createdAt: new Date()
    });
  },
  deleteImage: function(imageId){
    if(!Meteor.userId()){
      throw new Meteor.Error('Not Authorized');
    }

    Images.remove(imageId);
    //This removes the post's infomation and the image
    imageInfoId = Post.findOne({imageId:imageId})._id;
    Post.remove(imageInfoId);
  },
  follow: function(post){
    console.log(post);


  }
});

postForm.js:

Template.postForm.events({
  'submit .add-image-info': function(event){
    event.preventDefault();
    var title = document.getElementById('name').value;
    var story = document.getElementById('story').value;
    var imageId = Session.get('imageId');
    console.log(name);
    console.log(story);

    Meteor.call('addImageInfo', imageId, title, story)

    event.target.reset();
    Modal.hide('addInfo');

    FlashMessages.sendSuccess('Image Info Added');

    return false;
  }
})

images.js:

Template.postsList.helpers({
  images: function(){
    return Images.find({}, {sort:{uploadedAt: -1}});
  }
});

Template.image.helpers({
  'isOwn': function(imageId){

    var owner = Post.findOne({imageId: imageId}).userId;
    if(owner == Meteor.userId()) {
      return true;
    } else {
      return false;
    }
  }
});

Template.image.events({
  'click .remove-image': function(event){
    if(confirm('Are You sure?')){
      Meteor.call('deleteImage', this._id );
      FlashMessages.sendSuccess('Image Removed');
      return false;
    }
  }
});

imageInfo.js which i am getting the title the content and the username of the post

Template.imageInfo.helpers({

  getTitle: function(imageId) {
    return Post.findOne({imageId: imageId}).title;
  },
  getStory: function(imageId) {
    return Post.findOne({imageId: imageId}).content;
  },
  getUsername: function(imageId) {
    return Post.findOne({imageId: imageId}).username;
  },
  getUsername2: function(imageId) {
    return Post.findOne({imageId: imageId}).username2;
  }
});

//here i am creating an event listener on the 'follow' link
Template.imageInfo.events({
  'click .follow-link': function(event){
    event.preventDefault();


    Meteor.call('follow', this);
  }
});

imageInfo.html:

<template name="imageInfo">
  <div class="imageInfo">
    <div class="panel panel-default">
      <div class="panel-heading">
        <h4 class="panel-tittle">{{getTitle _id}} <span class="pull-right">Posted by: {{getUsername _id}} {{getUsername2 _id}}</span></h4>
        <a class="follow-link">Follow</a>
      </div>
      <div class="panel-body">
        {{getStory _id}}
      </div>
    </div>
  </div>
</template>

dropzone.js:

Template.dropzone.events({
  'dropped #dropzone': function(event){
    FS.Utility.eachFile(event, function(file){
      var newFile = new FS.File(file);
      Images.insert(newFile, function(error, result){
        if(error){
          FlashMessages.sendError('There was an issue with upload')
        } else {
          Session.set('imageId', result._id);
          FlashMessages.sendSuccess('Image Uploaded');
          //Modal.show('addInfo');
        }
      });
    });
  }
});

The difference between the two versions is that instead of using:

getTitle: function(imageId) {
    return Post.findOne({imageId: imageId}).title;
  },
  getStory: function(imageId) {
    return Post.findOne({imageId: imageId}).content;
  },
  getUsername: function(imageId) {
    return Post.findOne({imageId: imageId}).username;
  },
  getUsername2: function(imageId) {
    return Post.findOne({imageId: imageId}).username2;
  }

i am using for the simple version:

result = Post.find({}, {sort: {created: -1}});
and on the HTML instead of :

{{getTitle _id}} {{getStory}} and {{getUsername}}
i am using:

{{#each posts}}
{{content}}
{{username}}
{{/each}}

#2

Nothing jumps out at me in the code you’ve posted, but you could check the rest of your code for one idea.

I notice you’re using Session: along with ReactiveVar and ReactiveDict, it’s possible to set up a reactive infinite loop by getting and setting the same reactive variable in a reactive context (for example a template helper, or autorun).

So, it may be worth checking your helpers (and autoruns, if you have any) for areas where you get and set the same Session variable, for example, this would be an infinite loop:

Template.myTemplate.helpers({
  getNext: function() {
    var num = Session.get('someNumber');
    num = num + 1;
    Session.set('someNumber', num);
    return num;
  }
});

#3

Thanks for the answer. I found a solution. I just removed the code from methods.js and put it in the imageInfo.js file inside the template folder and with a few changes it worked.