Does Ember have {{itemView}} helper that can be used with {{#collection}}? - collections

Actually, I am looking for methods that let the View be responsible for marking the first or the last item in a collection view, and I found this one How do I add a separator between elements in an {{#each}} loop except after the last element?
.But I don't want to define itemViewClass template's attrs(classNames for example) in javascript, can I just use a {{itemView}} helper or something to define a itemView template?
{{#collection contentBinding="dataList" tagName="ol" classNames="list" itemViewClass="ItemView"}}
{{#itemView classNames="item" classNamesBinding="isLastItem:last"}}
item templates
{{/itemView}}
{{/collection}}
Although, this can be solved in another ways, I just want to know if I can find a built-in support. And I do search for a long time, just can't find Ember.Handlebars.collection's document, it's not in the latest API doc.

starting with the master version of Emberjs, you may try using a custom Handlebars "bound" helper.
it would look something like this :
{{#each item in collection}}
{{#isLastElement item}}
template if last
{{else}}
template if not
{{/isLastElement}}
both templates
{{/each}}
Ember.Handlebars.registerBoundHelper('islastElement',
function(item, options) {
if(functionTellingIfThisIsLastElement(item))
return options.fn(this);
else
return options.inverse(this);
}
);
This is just another way to see things, with functionTellingIfThisIsLastElement either looking in the collection or a property in your item. To access the collection you would have to change some stuffs here, to get the good context + parameters, depending on your code.

Related

Meteor/Blaze - Simple string concatenate in parameter

I know how to do this via the more expanded, and probably "proper" method, but I was wondering if there are any simpler ways to do this.
{{> alert message="My message"}}
Basically, I have a template that takes a value message. I want to give it the message Logged in as: samanime, where samanime is whatever the value of currentUser.username.
I know I can do this by creating a helper to concatenate the two parts for me, but is there some way that I can do it without an extra helper? Maybe something like:
{{> alert message="Logged in as: ${currentUser.username}"}}
After playing around with a couple ideas, I think I've come up with a reasonable solution that doesn't violate (too much) the separate of view and logic.
First, to do the actual concatenation, I made a concat helper:
// Takes any number of arguments and returns them concatenated.
UI.registerHelper('concat', function () {
return Array.prototype.slice.call(arguments, 0, -1).join('');
});
Example usage:
{{ concat "a" "b" "c" }}
{{! output: "abc" }}
Then, I made a new block helper, withResult:
Helper:
UI.registerHelper('withResult', function () {
Template._withResult.helpers({result: this.valueOf()});
return Template._withResult;
});
Template:
<template name="_withResult">
{{> UI.contentBlock result=result}}
</template>
Example usage:
{{#withResult concat "a" "b" "c"}}
{{> alert message=result}}
{{/withResult}}
Basically, it takes the result of whatever is the block call (in this case, concat) and puts it in the variable result available to the template.
I think this is a fairly clean and flexible way to not only concat, but also to get other simple multiple-parameter values across. The only negative at this point is it only allows you to create one variable (as each scope puts it in the same spot). However, that can be overcome by having other helpers return objects.
Of course, this is something that you don't want to abuse, as it would blur the clean line that we try to keep between view and logic.

Meteor - idiomatic way of re rendering template

I have a simple template which has a search box in it:
<template name="search">
<input type="text" name="search" id="search" />
{{> search_results}}
</template>
and obviously, the search results template:
<template name="search_results">
<ul>
{{#each results}}
<li>{{this.resultValue}}</li>
{{/each}}
</ul>
</template>
There is an event for keyp on the search input:
Template.search.events({
'keyup input#search': function (e) {
// fetch result from db
}
});
My problem is, where i have the comment: fetch result from db how do i get the search results template to auto update with the results from the db query?
To be clear: i can get the results fine, just cant see how to get the results template to update.
Essentially, something in the template you want to rerender (search_results) has to be reactive and register a changed event. Out of the box, that means it could be any of the data sources listed here. Since this isn't happening for you, I assume that when you "fetch results from db" you're not actually returning the result of a minimongo query, as this would be reactive by default.
If so, this kind of problem is often most easily solved by rolling your own reactivity using the Deps.Dependency prototype, which (as I understand it) underpins all the other reactive data sources other than the minimongo itself.
If you set var resultsChanged = new Deps.Dependency(), you get an object with two methods: depends and changed. Invoke the former in any computation you want to rerun when it changes and the latter to register a change. Session variables are basically just key/value stores with these Deps.Dependency methods attached to their get and set methods respectively.
So if you have resultsChanged initialised, you just need to make sure your search_results template depends on it, either by adding a new helper function (if results is in the data context as opposed to being a call to a helper), or amending the results helper itself. I'll assume the latter, but adding a new helper instead would be equally trivial.
Template.search_results.helpers({
results: function() {
resultsChanged.depend();
// get your results from wherever
return results;
}
});
That will rerun every time resultsChanged changes, so you just have to add the appropriate code in your callback when you fetch the results:
'keyup input#search': function (e) {
// fetch result from db, passing "resultsChanged.changed()" in a callback
}
Obviously, if the result fetching is synchronous, you don't even need to pass a callback, you can just do it on the next line.
I hope that is vaguely helpful - please let me know if it's not. If my assumption that you're not just pulling something out of the database is wrong, then this is probably on totally the wrong track, so I would recommend posting your actual "fetch result from db" code in your question.

How to make Meteor reactive on sub-items, but not parent

I've got a Newsfeed idea that I wanted to build with Meteor, but I'm having a bit of a struggle figuring out how to make the news feed itself constant, that is not reactive, but update the sub-items (comments, likes, etc) as soon as they're updated.
I've got everything stored in a single collection, and I'd like to keep it that way if possible. So the collection is setup like this:
[
{
title: 'A random title',
date_created: '01/01/2001',
comments:
[
{'message': 'Lorem ipsum', date_created: '01/01/2001'},
[...]
]
},
[...]
]
So what I'd like to do is have the newsfeed non-reactive, so that when a new news item is inserted or updated, the template holding the list of news won't get re-rendered. But if a comment is added, deleted, or someone likes the news feed, I'd want that to get updated right away in the template.
I've been trying to figure out how to use {{#isolate}} and {{#constant}} but to no prevail.
Here's my client side JS:
Template.group_feed.feed_data = function() {
var feed = Newsfeed.find({}, {
sort: {updated_time: -1},
limit: 10,
reactive: false
}).fetch();
return feed;
};
I set reactive: false so that it doesn't update the template, but that makes it static also when comments or likes are updated. So I'm guessing there's a better way to do this then to make the whole collection non-reactive.
Here's my template code:
<template name="group_feed">
<div id="feed-wrapper">
<ul>
{{#each feed_data}}
{{> group_feed_item}}
{{/each}}
</ul>
</div>
</template>
<template name="group_feed_item">
<li>
<h1>{{title}}</h1>
<div class="comments">
{{#each comments}}
<p>{{message}}</p>
{{/each}}
</div>
</li>
</template>
Anyone got a nice way of achieving this?
You have two options here. (I'll use coffeescript for my pseudocode)
use observeChanges:
NewsCursor = NewsItems.find()
NewsCursor.observeChanges
changed: (id, fields) ->
if fields.comments
Session.set ("commentsForId"+id), fields.comments
Split your data into two collections: one for the posts and one for the comments.
If you don't do one of those, {{isolate}} isn't going to help you. With your current data setup, Meteor just sees whether the post changed or not, and updates any templates when it does; it doesn't keep track of which part changed.
I did not test it, but I guess it would be most straightforward to limit the subscription of the client, thus reducing data transfer an eliminataing the need for the preserve:
it would be something like:
on server:
Meteor.publish('tenItemsBefore',function (time) {
Newsfeed.find({updated_time: {$lt time}}, {
sort: {updated_time: -1},
limit: 10
})}
on client, in reactive context eg. in Meteor.autorun():
Newsfeed.subscribe('tenItemsBefore',Session.get('lastUpdate'));
on client, triggered by an event eg. refresh using router package:
Session.set('lastUpdate', (new Date()).getTime());
hope that helps,
best, Jan
I'm pretty sure the problem is that you are returning an array rather than a cursor.
It seems that Spark behaves differently with the two.
Remove the .fetch() from the end of the feed query.
For more information about how this stuff works, I would strongly suggest looking at Chris Mather's great presentations at http://www.eventedmind.com/
Specifically, the one on reactivity in slow motion illustrates the difference between an array and a cursor:
http://www.eventedmind.com/posts/meteor-ui-reactivity-in-slow-motion
UPDATE: In my original answer I didn't fully understand problem - my apologies, so the bit about removing the fetch() will not help you. Here are a couple of options you might want to explore:
Using the Meteor.observe or Meteor.observeChanges to watch for changes to the comments field and updated the DOM with code ('by hand').
Create a custom collection on the server side that publishes just the comments from your original collection. So I'm not suggesting you change the data model as stored in mongo, just publish a different view of it. With this way it might be easier to continue to use templates to update the comments.
After initial loading of the collection, make a note of the viewed item ids, and then subscribe to the collection passing a list of ids to watch (or date range). This would require a new publish function that took a array of ids, and filtered the returned documents. This approach would prevent new documents disturbing what you had on screen, and you would still get comment changes, but you would also get deletions and field changes that affected non-comment fields.
Hopefully one of these approaches may fit the bill for you.
In case for some reason you don't want / cannot restrict the data seen by the client with publish or if new posts with earlier post time could be inserted and destroy the preservation the above solution wont work.
Therefore I suggest a different solution to more directly achieve preservation:
On opening the posts save the _id's of the watched posts in a non reactive way as by:
Session.set('watched',
_.map(
Newsfeed.find({}, {
sort: {updated_time: -1},
limit: 10,
reactive: false //not sure wheather you need both, as #StephenD
}).fetch(), // pointed out sth. fetched is non reactive by default
function (obj) {return obj._id;}
)
);
and:
Template.group_feed.feed_data = function () {
return Session.get('watched');
};
Template.group_feed_item.item = function () {
return Newsfeedd.findOne(this);
};
as well as a small update in the html (where with saves you from definining an extra template):
<template name="group_feed_item">
<li>
{{#with item}}
<h1>{{title}} - {{_id}}</h1>
<div class="comments">
{{#each comments}}
<p>{{message}}</p>
{{/each}}
</div>
{{/with}}
</li>
</template>
best, Jan
From the docs (emphasis mine) - http://docs.meteor.com/#find
Cursors are a reactive data source. On the client, the first time you
retrieve a cursor's documents with fetch, map, or forEach inside a
reactive computation (eg, a template or autorun), Meteor will register
a dependency on the underlying data. Any change to the collection that
changes the documents in a cursor will trigger a recomputation. To
disable this behavior, pass {reactive: false} as an option to find.
Note that when fields are specified, only changes to the included
fields will trigger callbacks in observe, observeChanges and
invalidations in reactive computations using this cursor. Careful use
of fields allows for more fine-grained reactivity for computations
that don't depend on an entire document.

Can Meteor Templates access Session variables directly?

In my Meteor app I find myself writing a lot of things like:
Templates.myTemplate1.isCurrentUser = function() {
return Session.get("isCurrentUser");
};
Templates.myTemplate2.isCurrentUser = function() {
return Session.get("isCurrentUser");
};
I need many different templates (I'm using handlebars) to access the same simple value stored inside Session.
Is there a way to avoid writing the same function over and over again?
Thanks
Building on #cioddi's answer, as you can pass parameters to the Handlebars helpers, you could make it a generic function so that you can easily retrieve any value dynamically, e.g.
Template.registerHelper('session',function(input){
return Session.get(input);
});
You can then call it in your template like this
{{session "isCurrentUser"}}
Note that the auth packages come with a global helper named CurrentUser that you can use to detect if the user is logged in:
{{#if currentUser}}
...
{{/if}}
As meteor is currently using handlebars as default templating engine you could just define a helper for that like:
if (Meteor.isClient) {
Template.registerHelper('isCurrentUser',function(input){
return Session.get("isCurrentUser");
});
}
you can do this in a new file e.g. called helpers.js to keep the app.js file cleaner.
Once this helper is registered you can use it in any template by inserting {{isCurrentUser}}
Just a heads up to everyone: With the release of 0.8.0, Handlebars.registerHelper has become deprecated. Using the new Blaze engine, UI.registerHelper would be the new method of accomplishing this.
Updated version of #cioddi 's code
UI.registerHelper('isCurrentUser',function(input){
return Session.get("isCurrentUser");
});
Actually now you can just use {{#if currentUser}}
It's a global included from the accounts/auth package..
http://docs.meteor.com/#template_currentuser
You'll want to check out these handlebar helpers for meteor: https://github.com/raix/Meteor-handlebar-helpers
There's a number of session helpers, one which does what you want. From the docs:
Is my session equal to 4?: {{$.Session.equals 'mySession' 4}}
You could add a isCurrentUserTemplate and include this in your other templates with
{{> isCurrentUserTemplate}}

Loop modulo condition with Handlebars.js

I am looping through objects using Handlebars. My issue is that there is no way I can easily add some code every x iterations (% 2 or % 3).
I tried the solution proposed on How can I create conditional row classes using Handlebars.js?. It doesn't work because I am using the framework called Meteor, and my "collection" is coming from a MongoDB query through Meteor. Using that solution, the "Context" is equal to the whole Meteor context and my collection is nowhere to be found...
My first idea was to create a new boolean property on my object called "eof" and a Handlebars condition would be triggered with some more code if it is equal to true... but I would like something cleaner and not dependent of the server-side.
How would you do it?
Thanks a lot!
See Tom Coleman's answer.
You could add a template helper that would look something like:
Template.tableWithDifferentColorRows.helpers({
modulo3: function() {
return (this.index % 3) === 0;
},
});
// in the html:
{{#each_with_index collection}}
<div {{#if modulo3}}style='color:purple'{{/if}}>
actual content...
</div>
{{/each}}

Resources