Check if collection is empty in a meteor template

6,748 views
Skip to first unread message

Justin McCandless

unread,
Apr 16, 2013, 9:42:31 PM4/16/13
to meteo...@googlegroups.com
If I'm rendering a meteor template with the results of a collection find, how can I know if it returned nothing?

Say I am writing a forum app and have a template for a thread's page.  I select all posts on this thread with something like this: Posts.find({thread_id: myThreadId}), and pass that into my template.  In my template, I only want to display some heading if there is at least one post.  Something like this does not work:

{{#if posts}}
  <h2>Posts</h2>
  {{#each posts}}
    {{name}} says {{content}}
  <br />
{{/if}}


With this template code, the Posts heading will always show, even if there are no posts.  How do I know if posts is empty?  I realize that I could pass another variable into the template, but I'm thinking there must be a more elegant solution...


Thanks for any help!


Justin McCandless
justinmccandless.com
github.com/justinmc
@justinjmcc

Abigail Watson

unread,
Apr 16, 2013, 10:03:39 PM4/16/13
to meteo...@googlegroups.com
Try posts.count.  If 0, it should resolve as a falsey value, and not display; if greater than 0, it should resolve as true.  Haven't tried this myself, but I imagine it would work...

{{#if posts.count}}

  <h2>Posts</h2>
  {{#each posts}}
    {{name}} says {{content}}
  <br />
{{/if}}

Justin McCandless

unread,
Apr 16, 2013, 10:18:57 PM4/16/13
to meteo...@googlegroups.com
I'm pretttty sure I tried this and it gave me an error in the console, but I just ran to work where I don't have access to the project.  I'll try at lunch and reply with the results.  Thanks for the idea!



Justin McCandless
justinmccandless.com
github.com/justinmc
@justinjmcc



Justin McCandless

unread,
Apr 17, 2013, 9:33:29 AM4/17/13
to meteo...@googlegroups.com
Indeed, the template can't be rendered if I use .count.  In the console I get something like this:

Exception from Deps recompute: LocalCollection.Cursor.prototype.count

Followed by a long stack trace involving a lot of Handlebars related stuff.  Doing posts.count() crashes the app.  posts.length always evaluates to false.

Any other ideas?



Justin McCandless
justinmccandless.com
github.com/justinmc
@justinjmcc



Nate Strauser

unread,
Apr 17, 2013, 10:28:55 AM4/17/13
to meteo...@googlegroups.com
i use a custom handlebars helper


Handlebars.registerHelper('ifNotEmpty', function(item, options) {
if(item){
if(item instanceof Array){
if(item.length > 0){
return options.fn(this);
}else{
return options.inverse(this);
}
}else{
if(item.fetch().length > 0){
return options.fn(this);
} else {
return options.inverse(this);
}
}
}else{
return options.inverse(this);
}
});

then just
{{#ifNotEmpty collectionCursor}}
{{#each collectionCursor}}
<display each item>
{{/each}}
{{else}}
<empty message of some sort>
{{/ifNotEmpty}}

On Tuesday, April 16, 2013 9:42:31 PM UTC-4, Justin McCandless wrote:

Justin McCandless

unread,
Apr 17, 2013, 10:41:00 AM4/17/13
to meteo...@googlegroups.com
Very cool, I did not know you could do that.  It's unfortunate it's not as easy as posts.count, but this looks just as nice once the helper is declared.  Thanks for the solution.



Justin McCandless
justinmccandless.com
github.com/justinmc
@justinjmcc




Abigail Watson

unread,
Apr 17, 2013, 11:35:53 AM4/17/13
to meteo...@googlegroups.com
For what it's worth, I also use a similar helper function, and introduce that second variable into my code blocks.  


--
You received this message because you are subscribed to a topic in the Google Groups "meteor-talk" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/meteor-talk/Gumkz9VnLYY/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to meteor-talk...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Avital Oliver

unread,
Apr 17, 2013, 1:01:43 PM4/17/13
to meteo...@googlegroups.com
I just created a quick sample app and it worked fine.

Template.hello.things = function () {
  return Things.find();
}

<template name="hello">
  {{#if things.count}}
    Not empty
  {{else}}
    Empty
  {{/if}}
</template>



--
You received this message because you are subscribed to the Google Groups "meteor-talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to meteor-talk...@googlegroups.com.

Ken Yee

unread,
Apr 17, 2013, 9:09:38 PM4/17/13
to meteo...@googlegroups.com


On Tuesday, April 16, 2013 9:42:31 PM UTC-4, Justin McCandless wrote:
I select all posts on this thread with something like this: Posts.find({thread_id: myThreadId})

I hit this a while back too.
What I did is something like this:
   var posts = Posts.find({thread_id: myThreadId});
   return (posts.count > 0) ? posts : null;

Then I do {{#if posts}}
Yep...yuck...works though :-)

Justin McCandless

unread,
Apr 17, 2013, 10:51:09 PM4/17/13
to meteo...@googlegroups.com
Avital,

    I tried this myself and you are right!  The difference is passing a function that returns the collection cursor to the template, as opposed to passing the collection cursor directly.

    So your example above works, but this does not work and the template will not render at all:


Template.hello.things = Things.find();

<template name="hello">
  {{#if things.count}}
    Not empty
  {{else}}
    Empty
  {{/if}}
</template>


I even tried this by rendering the template manually inside of a Meteor.render and then appending to the DOM, and it still worked fine.

But why does this work and not the other approach?  Does the documentation tell us to only pass functions to templates somewhere?  Is this a bug in Meteor?

matt debergalis

unread,
Apr 18, 2013, 2:05:24 AM4/18/13
to meteo...@googlegroups.com
Those are not interchangeable syntaxes. One of them defines a
template helper to be a database cursor. The other defines the helper
to be a function that *when called* returns a database cursor.

To understand why these behave differently, work through the
implementation of `Handlebars.evaluate` and in particular its
`eval_value` helper function. One reason helpers should be functions
is that it lets Meteor call them at render time with `this` bound to
the current data context in the template. Another is that Meteor
(Spark, precisely) runs your helper in a reactive context so that it
can register a dependency on the database cursor you returned. That
way, Spark can rerun the template helper when its value might have
changed.

Anyway, you ~always want your helpers to be functions that the
Handlebars evaluator can call at render time, so use Avi's syntax.

Justin McCandless

unread,
Apr 18, 2013, 5:48:25 AM4/18/13
to meteo...@googlegroups.com
Got it, I'll use functions from now on.  This is the kind of answer I was looking for, so thanks to you and Avital.

Andre Honsberg

unread,
Aug 28, 2013, 9:59:06 AM8/28/13
to meteo...@googlegroups.com
Handlebars.registerHelper('ifNotEmpty', function(item, options) {
    if (item) {
        if (item instanceof Array) {
            if (item.length > 0) 
                return options.fn(this);
        } else {
            if (item.fetch().length > 0)
                return options.fn(this);
        }
    }
    return options.inverse(this);
});

Lloyd Dewolf

unread,
Apr 28, 2014, 1:45:07 PM4/28/14
to meteo...@googlegroups.com
On Tuesday, April 16, 2013 6:42:31 PM UTC-7, Justin McCandless wrote:
Something like this does not work:

{{#if posts}}
  <h2>Posts</h2>

I tripped on this today. This is going to forever trip up people new to Meteor and seems to need a semantic solution.

Thank you,
Lloyd
Reply all
Reply to author
Forward
0 new messages