This is the routes templateData
this.route('editSession', {
path: '/dashboard/events/:_id/editSession/:_sid',
template: 'editSession',
layoutTemplate: 'dashboard_layout',
data: function () {
var sId = this.params._sid;
var eId = this.params._id;
var templateData = {
session: Sessions.findOne({ _id: sId }),
event: Events.findOne({ _id: eId })
};
return templateData;
}
})
I'm trying to use the events object being passed through the router, but when I use events._id in the template, nothing renders because it is within the #each tags for the session. How can I escape it so I am able to use another data object?
{{#each sessions}}
<h3>Session</h3>
<p>{{name}}</p>
<p>{{description}}</p>
<p>{{startTime}} - {{endTime}}</p>
<button class="btn btn-danger">Edit Session</button>
{{/each}}
Please help. So stuck. Would be really appreciated :)
In spacebars you can use ../ to jump up one context level. Assuming event is in the enclosing context of the #each you can do this:
<a href="/dashboard/events/{{../event._id}}/editSession/{{_id}}">
Note: I'm using the singular event, as specified in your router.
Related
I have a route and a template, that has the right data context as defined in the routes.js:
this.route('entrant_view_race', {
path: '/organise/entrants/:_id/',
yieldTemplates: navOrganise,
waitOn: function() {
return Meteor.subscribe('entrants');
},
data: function() {
return Races.findOne(this.params._id);
}
});
The data context is set to the data function above no problems. Within entrant_view_race template/route we have another template:
<div class="row">
<div class="col-md-4">
{{> chart_entrant_status}}
</div>
</div>
Now within chart_entrant_status is a subscription which passes a param which is defined in the data context:
Meteor.subscribe('entrantStatusCount', this._id);
but this._id is undefined. I believed that whatever the data context of the parent is passed to child templates unless you explicitly define such as {{> chart_entrant_status dataContext}} ? How do I pass the _id of the parent context to the child template (I don't want to use session variables).
EDIT:
chart_entrant_status template looks like this:
<template name="chart_entrant_status">
{{#chart_container title="Entrants" subtitle="Status" class="race-status"}}
{{_id}}
<div id="chart"></div>
{{> table_entrant_status}}
{{/chart_container}}
</template>
Note {{_id}} is rendered fine so the context is alive to that point. And the subscription when the template is rendered:
Template.chart_entrant_status.rendered = function() {
Meteor.subscribe('entrantStatusCount', this._id); // this._id is undefined - if I substitute for the actual id as a string Ba48j2tkWdP9CtBXL I succeed....
}
But no cigar... struggling to find where I lose the data context...
EDIT2: This returns this._id fine...
Template.chart_entrant_status.helpers({
stuff: function() {
return this._id; // returns the right id in {{stuff}} in the template
}
})
So is the data context not available to Template.chart_entrant_status.rendered?
EDIT4: solved. It's this.data._id.... ahhhh
this.data._id was the correct answer
i am farely new to meteor, so it might be obvious.
I am trying to define some routes:
Router.route('/translations', {name:'translation.index'});
Router.route('/translations/:_id', {name:'translation.show'});
I also have defined a Controller which should define how to get the Data:
TranslationIndexController = RouteController.extend({
template: 'TranslationIndex',
data: function () {
return Translations.find();
},
sayHello: function(){
return 'hello';
}
});
I have some Collection that is just fetched and some random tempalte helper. My template looks like this:
<template name="TranslationIndex">
TestOutput:
{{sayHello}}
{{#each data}}
<li>askljdfh</li>
{{/each}}
</template>
But neither my hello nor my collection is shown. PS: I have checked if my collection contains data by using Translations.find().count() in the console.
You need to call the controller in your route, e.g.,
Router.route('/translations', {
name:'translation.index',
controller: TranslationIndexController
});
The data should then be available.
So, I'm working on a Meteor project and I can't get this route to generate properly, or at all for that matter.
<template name="browseAll">
<h3>List of classes with books available!</h3>
<ul>
{{#each aggCount}}
<li>{{ _id }} ({{ count }})</li>
{{/each}}
</ul>
</template>
The data that is being iterated over is a result of aggregation using MongoInternals, and that is as follows:
(server/methods.js excerpt):
classCount: function() {
// Attempt aggregation of the books table to count by class, maybe.
var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;
var col = db.collection("books");
var aggregateSync = Meteor._wrapAsync(col.aggregate.bind(col));
var pipeline = [
{$group: {_id: "$class", count: {$sum: 1}}},
{$sort: {_id: 1}}
];
var theAnswer = aggregateSync(pipeline);
return theAnswer;
}
It seems that the data is coming through okay, and sample data from aggregation (coming into the template) looks like this:
[ { _id: 'ADNR1234', count: 2 }, { _id: 'ARTH1234', count: 1 } ]
That's the template code I've got, and this is the route that it's supposed to be working with:
this.route('browse-class', {
path: '/browse/:_class',
data: function() {
var booksCursor = Books.find({"class": this.params._class},{sort:{"createdAt": 1}});
return {
theClass: this.params._class,
numBooks: booksCursor.count(),
books: booksCursor
};
}
});
I don't understand it. The data is being SHOWN, and what I want to do is generate a URL for browse-class (route) that takes the value of {{ _id }} in the helper as a parameter, so as to generate something like this:
application.org/browse/CLSS
Be aware that {{pathFor}} must be called with a data context properly set :
{{#with class}}
{{pathFor "browse-class"}}
{{/with}}
Optionnaly it is possible to pass the data context as a parameter :
{{pathFor "browse-class" class}}
The data context provided to pathFor is used when generating the route path, if you defined a route path like this :
path: "/browse/:_id"
Then it will use the _id from the class to properly generate a URL.
For the text of the link, I doubt you want to display the _id, your class documents probably include a "label" so you could use this :
{{ label }}
In Iron-router, we can pass the data to a page in the data field. For example:
Router.map(function () {
this.route('myroute', {
path: '/route',
template: 'myTemplate',
data: function () {
return {
title: getTitle(),
description: getDescription(),
}
}
});
});
In the template, title and description are actually some value passed to subtemplates:
<template name="myTemplate">
{{> titleTemplate title}}
{{> descriptionTemplate description}}
</template>
Since the data field in the iron-router is reactive, whenever a session variable change, the data field is recalculated.
In this case, however, the session variable in getTitle function only changes the template "titleTemplate", and the session variable in getDescription() function only changes the template "descriptionTemplate".
If the session variable in the getTitle() function changes, I would like to only execute the getTitle() function, and do not execute the getDescription() function. If possible, I would also like to only render the "titleTemplate" and do not render "descriptionTemplate".
I wonder whether that is possible. If this is not the right way of writing the Meteor application, what is a better way to do it?
Thanks.
This is an interesting situation. Despite the fact that the getTitle and getDescription functions may be dependent on completely different reactive variables, they will both be recomputed whenever either one of them changes.
One possible solution is to pass the functions themselves instead of the result of calling the functions. That may or may not be convenient depending on how they are used in the sub-templates, but it will prevent them from both being run every time. Here is a simple example:
<template name="myTemplate">
{{> titleTemplate title}}
{{> descriptionTemplate description}}
</template>
<template name="titleTemplate">
<p>{{excitedTitle}}</p>
</template>
<template name="descriptionTemplate">
<p>{{this}}</p>
</template>
var getTitle = function() {
console.log('computed title');
return Session.get('title');
};
var getDescription = function() {
console.log('computed description');
return Session.get('description');
};
Router.map(function() {
this.route('home', {
path: '/',
template: 'myTemplate',
data: function() {
return {
title: getTitle,
description: getDescription
};
}
});
});
Meteor.startup(function() {
Session.setDefault('title', 'title1');
Session.setDefault('description', 'description1');
});
Template.titleTemplate.excitedTitle = function() {
return "" + (this.toUpperCase()) + "!";
};
From the console you can change the session variables (title and description) and you will see that only one function will be run at a time. I hope that helps.
One way to solve this is to not use the data context, but just use template specific helpers. Since I don't know what your getTitle and getDescription function do, I can't tell whether that is an option for you. It depends on whether you need to use the this object in those functions and need this to refer to the route object or not. If not, then the following seems like the better solution:
JS:
Router.map(function () {
this.route('myroute', {
path: '/route',
template: 'myTemplate'
});
});
Template.myTemplate.title = getTitle;
Template.myTemplate.description = getDescription;
HTML:
<template name="myTemplate">
{{> titleTemplate title}}
{{> descriptionTemplate description}}
</template>
If I have an {{# each}} binding in Meteor, and I want to update a property on only one instance of the template inside the #each. How would I do that? I've tried setting a value on the "template" object inside the events map, but that doesn't seem to be reactive. I've also tried binding to a Session property, but that will cause every instance to update instead of just the one I want...
for example:
{{#each dates}}
{{> dateTemplate}}
{{/each}}
<template name="dateTemplate">
{{date}}
<span style="color: red;">{{errorMsg}}</span> <--- how do i update errorMsg?
</template>
Template.dateTemplate.events({
'click': function(event, template) {
template.errorMsg = 'not valid'; <--- this doesn't do anything
}
});
EDIT TO ADDRESS ANSWER BELOW:
Template.dateTemplate.events({
'click': function(event, template) {
template.errorMsg = function() { return 'not valid';} <--- this also doesn't do anything
}
});
You don't have to use handlebars for this, because its not something that needs reactivity to pass the message through, reactive variables work best with db data, or data that would be updated by another client over the air.
You could use JQuery (included by default) to update it, it can also get a bit fancier:
<template name="dateTemplate">
{{date}}
<span style="color: red;display: none" class="errorMessage"></span>
</template>
Template.dateTemplate.events({
'click': function(event, template) {
$(template.find('.errorMessage')).html('Your Error Message').slideDown();
}
});
Ive edited it so the error is hidden by default, and slides down with an animation
I'm experimenting handling this by passing a different reactive object to each instance of the template. Then the template can bind to the reactive object (which is unique per instance) and we don't have any extra boilerplate.
It ends up looking like this:
Initial render:
Template.firstTemplateWithPoll(ContextProvider.getContext())
Template.secondTemplateWithPoll(ContextProvider.getContext())
// (I actually pass getContext an identifier so I always get the same context for the same template)
JS:
Template.poll.events = {
'click .yes' : function() {
this.reactive.set('selection', 'yes');
},
'click .no' : function() {
this.reactive.set('selection', 'no');
}
};
Template.poll.selection = function(arg) {
return this.reactive.get('selection');
}
Template:
<template name="poll">
<blockquote>
<p>
Your selection on this poll is {{selection}}
</p>
</blockquote>
<button class='yes'>YES</button>
<button class='no'>NO</button>
</template>
template.errorMsg should be a function that returns your error.
Template.dateTemplate.events({
'click': function(event, template) {
template.errorMsg = function() { return 'not valid'; };
}
});