Reactive collection query in a template - meteor

I have a Template named movies, that has a method that returns a list of objects from a collection.
The query to generate that list of objects is created dynamically using data from another template method.
I would like to re-render the template, or just the components associated with that specific template method, whenever the filter data changes.
Here are the two methods used:
Template.movies.filter = function () {
if (Session.equals("filter", undefined)) {
return {};
}
return Session.get("filter");
};
Template.movies.movies = function () {
return Movies.find(Template.movies.filter(), {sort: [["Poster", "desc"]]});
};
On the HTML side it's a simple {{#each movies}}{{> movie}}{{/each}} to show the results from the movies method.
The problem is that when Session.get("filter") changes and therefore so does Template.movies.filter(), the HTML component relying on Template.movies.movies() data won't be updated with the new query results.
How would I achieve that behavior?

The easiest way is to just make a javascript function that both helpers utilize:
var getFilter = function() {
if (Session.equals("filter", undefined)) {
return {};
}
return Session.get("filter")
}
Template.movies.filter = function() { return getFilter(); }
Template.movies.movies = function () {
return Movies.find(getFilter(), {sort: [["Poster", "desc"]]});
};
This will react as you expect.

Related

Meteor publications/subscriptions not working as expected

I have two publications.
The first pub implements a search. This search in particular.
/* publications.js */
Meteor.publish('patients.appointments.search', function (search) {
check(search, Match.OneOf(String, null, undefined));
var query = {},
projection = {
limit: 10,
sort: { 'profile.surname': 1 } };
if (search) {
var regex = new RegExp( search, 'i' );
query = {
$or: [
{'profile.first_name': regex},
{'profile.middle_name': regex},
{'profile.surname': regex}
]
};
projection.limit = 20;
}
return Patients.find(query, projection);
});
The second one basically returns some fields
/* publications.js */
Meteor.publish('patients.appointments', function () {
return Patients.find({}, {fields: {'profile.first_name': 1,
'profile.middle_name': 1,
'profile.surname': 1});
});
I've subscribed to each publication like so:
/* appointments.js */
Template.appointmentNewPatientSearch.onCreated(function () {
var template = Template.instance();
template.searchQuery = new ReactiveVar();
template.searching = new ReactiveVar(false);
template.autorun(function () {
template.subscribe('patients.appointments.search', template.searchQuery.get(), function () {
setTimeout(function () {
template.searching.set(false);
}, 300);
});
});
});
Template.appointmentNewPatientName.onCreated(function () {
this.subscribe('patients.appointments');
});
So here's my problem: When I use the second subscription (to appointments.patients), the first one doesn't work. When I comment the second subscription, the first one works again. I'm not sure what I'm doing wrong here.
The issue here is you have two sets of publications for the same Collection. So when you refer to the collection in client there is now way to specify which one of the publication it has to refer too.
What you can do is, publish all data collectively i.e. all fields you are going to need and then use code on client to perform queries on them.
Alternatively, the better approach will be to have two templates. A descriptive code:
<template name="template1">
//Code here
{{> template2}} //include template 2 here
</template>
<template name="template2">
//Code for template 2
</template>
Now, subscribe for one publication to template one and do the stuff there. Subscribe to second publication to template 2.
In the main template (template1) include template2 in it using the handlebar syntax {{> template2}}

Meteor - Using reactive variables in anonymous functions

How can I use reactive template variables (from Template.data) in an anonymous function within the template rendered function? (I want to keep it reactive).
Template.templateName.rendered = function() {
function testFunction(){
//Log the field 'title' associated with the current template
console.log(this.data.title);
}
});
Not sure exactly what you are trying to do (like printing this.data.title whenever it changes?), but you should:
use a Reactive variable (add reactive-var package, then create a var myVar = new ReactiveVar()
If necessary, wrap your function with Tracker.autorun (or this.autorun in a template creation / rendered event).
So you could have like:
Parent template HTML:
<template name="parentTemplateName">
{{> templateName title=myReactiveVar}}
</template>
Parent template JS:
Template.parentTemplateName.helpers({
myReactiveVar: function () {
return new ReactiveVar("My Title!");
}
});
Template JS:
Template.templateName.onRendered(function() {
// Re-run whenever a ReactiveVar in the function block changes.
this.autorun(function () {
//Print the field 'title' associated with the current template
console.log(getValue(this.data.title));
});
});
function getValue(variable) {
return (variable instanceof ReactiveVar) ? variable.get() : variable;
}
What worked for me was simple using autorun() AND using Template.currentData() to grab the values from within autorun():
let title;
Template.templateName.rendered = function() {
this.autorun(function(){
title = Template.currentData().title;
});
function testFunction(){
console.log(title);
}
});
Template.templateName.onRendered(function(){
console.log(this.data.title);
});

Keep reactive helper from updating a meteor template

I'm looking at how to turn off reactivity within a template helper function. I only want the data rendered when the template is initially populated and not when the underlying data changes.
My current helper has two reactive variables: one is a Session.get() and the other is a Collection.findOne(). I've tried to wrap the Session.get() in a Tracker.nonreactive() call and set the reactive option to false on the Collection.findOne() but I'm still seeing the reactive behavior.
Template.characterSkills.helpers({
data : function () {
var characterID = Tracker.nonreactive(function () {
return Session.get("currentCharacterID");
});
if(characterID) {
var record = Characters.findOne(
{ _id : characterID },
{
reactive : false,
fields : { skills : 1 }
}
);
if(record && record.skills)
return record.skills;
}
}
});
I've been trying to work this issue for about half a day now. Any help would be greatly appreciated.
You're using the name data for your helper which is reserved. So if you have another helper or an iron router route with data in it, or this template is a subtemplate of another template its likely to redraw too.
Try renaming it to something else.
You can use a closure to find and set the record when the template is rendered and then return a static object in the template helper:
Template.characterSkills.helpers({
dataItem: function(){
return record.get();
}
});
Template.characterSkills.onCreated(function(){
record.set(Session.get("currentCharacterID"));
});
var record = function(){
var local = {};
return {
set: function(characterId){
if ( characterID) local = Characters.findOne({_id: characterID},{fields : { skills : 1 }});
else local = null;
},
get: function(){
return local;
}
}();
Although even this feels like too much work. Surely there's an easier way.

Meteor: rendered function called before documents are loaded

I have a template with a rendered function like this:
Template.config.rendered = function () {
var barfoo = BarFoo.findOne({....});
if (barfoo) {
$('[start-date]').datepicker({ ... });
}
}
The problem is that the collection BarFoo is still empty at the time the rendered function is called.
I'm using Iron Router so I can wait until specific subscriptions have loaded their documents:
HomeController = RouteController.extend({
...
waitOn: function () {
return Meteor.subscribe('barfoo');
}
});
So the question is, how is it possible that the rendered function is called before the data of barfoo is loaded ?
You could use onData in your routecontroller. If you use the data param:
data: function() {
bar: BarFoo.findOne({....})
}
onData: function() {
if(BarFoo.findOne({....})) {
$('[start-date]').datepicker({ ... });
}
}
onData is run every time the return value of data changes, so you can check if there is a value there first, it can also be reactively changed if your subscription changes.
Another option is to add a loading template, which is shown while your subscription completes:
loadingTemplate : 'template'

backbone.js: add an element to a collection without re-rendering all the collection

I have a view that render itself from a Collection:
render: function() {
$(this.el).html(JST['templates/menu']({collection: this.collection }));
$('#nav').html(this.el);
}
In the view initializer, i bind the add event of the collection on the render function of the view:
initialize: function() {
this.render();
var self = this;
this.collection.bind("add", function(event){
self.render();
});
}
elsewhere in the application, I add an item to the collection.
bookSubscription_collection.add(model);
The problem with that code is, if I add a new item to the collection, then all the items in the collection are re-rendered.
Is there a way to add a new item to a collection without re-render all the other items, but just render the new item?
This a simplified version of how I'm doing it. reset adds all the models to the UI and add adds a single model to the UI. addAll basically has a loop that calls addOne for each model. It could probably be optimized better but it works well enough.
initialize: function() {
_.bindAll(this, 'render', 'addOne', 'addAll');
leads.bind('add', this.addOne, this);
leads.bind('reset', this.addAll, this);
},
render: function() {
this.addAll();
return this;
},
addOne: function(model) {
// add the model to HTML
},
addAll: function(options) {
leads.each(this.addOne);
return this;
}
Instead of binding the collection's add event to the render function, bind it to some other function that accepts the model that was added, and modifies the DOM with data from the model that was added.
abraham wrote a good example. Ive been also using it like this
initialize: ->
self = #
#model.bind "add", (task) ->
$(self.el).append new app.TaskIndexItemView(model: task).render().el
but i think the addOne is better solution

Resources