Meteor using Template.registerHelpers, - meteor

I am trying to combine 2 beginner classes together (https://www.meteor.com/tutorials/blaze/templates & http://meteortips.com/second-meteor-tutorial/iron-router-part-1/). I have been able to complete both classes individually with no problem.
I am using Iron:Router to route to various templates. I run in to issues when I try to access a template within a template. The proper results do not get displayed on the " aostasks"page.
.js:
Router.route('aostask', {
template: 'aostask'
});
Tasks = new Mongo.Collection("tasks");
if (Meteor.isClient) {
// This code only runs on the client
Template.registerHelpers('task', function () {
return tasks.find({});
})
};
.html:
<template name= "aostask">
<head>
<title>Todo List</title>
</head>
<body>
<div class="container">
<header>
<h1>Todo List</h1>
</header>
<ul>
{{#each tasks}}
{{> task}}
{{/each}}
</ul>
</div>
</body>
</template>
<template name="task">
<li>{{text}}</li>
</template>
Result:
A page with the correct headers.However, it does not list the tasks i have in my mongo collection. Is the issue that i am using the registerHelper function incorrectly? Should i be using some other function?
Thanks in advance.

There is typo in your helper name
Template.registerHelper('tasks', function () {
return tasks.find({});
})
};
tasks not task

Your helper is registered with the same name as your template. It should be just 'task'. Template.registerHelpers should be Template.registerHelper
if (Meteor.isClient) {
// This code only runs on the client
Template.registerHelper('task', function () {
return Tasks.find({});
})
};

Related

Setting up data contexts in Meteor and Iron Router

I was after some advice please on the best way to set up data contexts for a MongoDB database with Iron Router.
To explain I'm working on a fairly basic film reviews project, and deployed to Modulus at http://reviews-48062.onmodulus.net/
This returns a list of reviews, but would like to use routes to create other pages. I've installed Iron Router locally and have a local MongoDB collection (called tasks) with some data in it.
Have re-written some code to include routing information. This works successfully in displaying the routes, but doesn't seem to pull in any data into the {{each}} statement.
My JS code is as follows:
// define Mongodb collection
Tasks = new Mongo.Collection("tasks");
// set up home route and database query
Router.route('/', function () {
this.render('Home', {
tasks: function () { return Tasks.find({}, {sort: {title: 1}, limit: 10}); }
});
});
// define routes
Router.route('/one');
Router.route('/two');
Router.route('/three');
The HTML code is:
<head>
<title>Iron router sandbox</title>
</head>
<body>
</body>
<template name="Home">
{{> Nav}}
<h1>Home</h1>
<p>This is a test</p>
{{#each tasks}}
<li>
<strong>{{title}}</strong>
<p>Directed by {{director}}</p>
<p>{{review}}</p>
<p>Available on: {{format}}</p>
</li>
{{/each}}
</template>
<template name="One">
{{> Nav}}
<h1>Page One</h1>
<p>Some more text.</p>
</template>
<template name="Two">
{{> Nav}}
<h1>Page Two</h1>
<p>A bit more text.</p>
</template>
<template name="Three">
{{> Nav}}
<h1>Page Three</h1>
<p>Even more text.</p>
</template>
<template name="Nav">
<ul>
<li>
Home
</li>
<li>
Page One
</li>
<li>
Page Two
</li>
<li>
Page Three
</li>
</ul>
</template>
My understanding is that the data context is set up for the "Home" template, so not sure what's wrong with the code.
Any advice much appreciated.
tasks is not a valid property to send to that object. You need to set the data context for IronRouter using data instead. Change tasks to data, then set up a helper on your template to get tasks data:
Template.Home.helpers({
tasks() {
return Tasks.find({}, {sort: {title: 1}, limit: 10});
}
});
You can return a data context with i-r but it needs to be named data:
Router.route('/', function () {
this.render('Home', {
data: function () {
return { tasks: Tasks.find({}, {sort: {title: 1}, limit: 10}); } };
});
});
This will return a data context to your template and then the tasks key will contain a cursor of tasks.

Meteor stopped running template `rendered` functions

Im trying to run some jquery code after the template renders. But it's not working. When I run the code in the console manually, it works. Here's the js :
Template.PanelLayout.helpers({
restricted: function() {
return !Meteor.user();
},
authInProcess: function() {
return Meteor.loggingIn();
},
canShow: function() {
return !!Meteor.user();
}
});
Template.PanelLayout.rendered = function() {
$(document).ready(function() { // This is the code I want to run
$(".button-collapse").sideNav();
$('.collapsible').collapsible();
});
}
Template code :
<template name="PanelLayout">
{{#if restricted}}
{{> NotFound}}
{{else}}
{{#if authInProcess}}
{{> spinner}}
{{else}}
{{#if canShow}}
<header>
{{> PanelMainNav}}
</header>
<main id="panel-content">
{{> Template.dynamic template=content }}
</main>
{{/if}}
{{/if}}
{{/if}}
</template>
Im not sure but I think it's because I have added the if else statements to load the content only when the user is logged in? How can I fix this?
Probably it's because rendered function is called after document is ready, therefore your hook document.ready won't work, you should remove it, so it will look like this(also rendered function is deprecated, use onRendered instead):
Template.PanelLayout.onRendered(function() {
$(".button-collapse").sideNav();
$('.collapsible').collapsible();
});

Meteor iron-router - Can I have multiple data sources in the route?

I have an application built with Meteor that uses Iron Router. My layout uses multiple yield templates and I'd like to pass through different data to each one.
It successfully passes through tasks to the tasksList template, but doesn't pass through selectedTask to the taskDetail template.
Is it possible to have multiple data sources and is this the right way to go about it? And if yes, then why is it not working?
Thanks in advance! :-)
Router.map(function() {
this.route('tasksList', {
path: '/',
layoutTemplate: 'layout',
template: 'tasksList',
yieldTemplates: {
'taskDetail': {to: 'rightTemplate'}
},
data: {
tasks: function(){ return Tasks.find() },
selectedTask: function() { return Tasks.findOne() }
}
});
});
<template name="layout">
<section class="wrapper">
<div class="left-pane">
{{yield}}
</div>
<div class="right-pane">
{{yield 'rightTemplate'}}
</div>
</section>
</template>
<template name="tasksList">
<ul>
{{#each tasks}}
<li>{{detail}}</li>
{{/each}}
</ul>
</template>
<template name="taskDetail">
{{#each selectedTask}}
<div>{{detail}}</div>
{{/each}}
</template>
You are returning selectedTask as a single object (with findOne), but in the taskDetail template, you use {{#each selectedTask}}{{detail}}{{/each}}. What happens if you simply have {{detail}} as the body of that template?
Sorry, both those methods work for me now. I must have had a wrong template name or something similar.
You can have multiple data sources as shown in the examples above.

passing values to meteor partials

I'm learning meteor to build quick website prototypes.
I'm trying to understand how to generate a set of values to populate the site templates and partials.
I have a layout.html template
<template name="layout">
<div class="container">
<header role="banner">
{{>site-header}}
</header>
<h1>This is {{siteLogo}}</h1>
<main role="main">
{{ yield }}
</main>
<footer role="contentinfo">
{{> site-footer }}
</footer>
</div>
</template>
in main.js I define the following:
Meteor.startup(function(){
Session.set('siteLogo', 'the logo');
});
Template.site-header.helpers({
siteLogo: function(){ return Session.get('siteLogo'); }
});
Template.layout.helpers({
siteLogo: function(){ return Session.get('siteLogo'); }
});
With this i can pass the value of siteLogo to layout.html.
I have a site-header.html partial
<template name="site-header">
<h1>{{siteLogo}}</h1>
</template>
I can't seem to be able to pass the value of siteLogo to the partial. Is there a way to do that?
Is it necessary to create a Session variable to pre-fill some values or can i just create a json settings list and access the value globally?
something that would go in main.js, like the yaml config file in a jekyll site:
siteSettings = [
{
siteLogo: "some brand name",
otherValue: "something else"
}
]
update
I'm a bit confused, I'm must be doing something wrong.
I've created a quick new meteor app to test this.
I have main.html
<head>
<title>handlebar-helper</title>
</head>
<body>
{{> header}}
{{> hello}}
{{> footer}}
</body>
<template name="hello">
<h1>Hello World!</h1>
{{greeting}}
<input type="button" value="Click" />
</template>
<template name="header">
<header>
<h1>{{ headline }}</h1>
<p>tagline</p>
</header>
</template>
<template name="footer">
<footer role="contentinfo">
<h1>{{ headline }}</h1>
<small>copyright</small>
</footer>
</template>
And main.js
if (Meteor.isClient) {
Template.hello.greeting = function () {
return "Welcome to handlebar-helper.";
};
Template.hello.events({
'click input' : function () {
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});
Meteor.startup(function(){
Session.set('headline', 'My fancy headline');
});
Handlebars.registerHelper('headline', function(){
return Session.get('headline');
});
}
if (Meteor.isServer) {
// server code here
}
And i can't still pass the value of headline into >header of >footer
if I try to put the Session.set into the Meteor.isServer block, I get a syntax error, Session is not defined
Cheers
Do you have a Template.site-header.helpers function declared for siteLogo? If not it won't work - you can't use a helper from another template. If you need to use siteLogo in a variety of places, it's best to use a Handlebars block helper, as these can be accessed by any template.
UPDATE
The Handlebars helper would just look like this:
Handlebars.registerHelper('siteLogo', function() {
return Session.get('siteLogo');
});
However, if you've already got a siteLogo helper in the site-header Template, it suggests something else is wrong, like a typo in a template or helper name. Is there an error in the console?
UPDATE 2
If you want to use a dictionary-style structure to store reactive data, you can do something like this:
Session.set('myDict', {foo: 1, bar: 2});
Handlebars.registerHelper('myDict', function(key) {
return Session.get('myDict') ? Session.get('myDict')[key] : null;
});
And then use this in your template: {{myDict 'foo'}}. Obviously, the format above would work fine in a tempate helper as well, but it would only be accessible from within that template. The ternary operator is just to check that myDict has been initialised before it lets a template try to look up one of the keys, which is a common Meteor problem on page load.
Incidentally, if you're finding Session variables a cumbersome way to deal with reactive dictionary-like data structures, it's pretty easy to roll your own. This is the best introduction.

returning findOne object to template

Having troubles understanding how to return and use an object from findOne().
my code is this:
Html:
<head>
<title>count</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
{{showcount}}
</template>
Js:
var Database = new Meteor.Collection("counters");
if(Meteor.is_client) {
Template.hello.showcount = function () {
var c = Database.findOne();
return c;
};
}
if (Meteor.is_server) {
Meteor.startup(function () {
if(Database.find().count() === 0)
{
Database.insert({name: "counter", value: 0});
}
});
}
Now I'm wondering if there is any way I can access the data from my object.
changing from {{showcount}} to {{showcount.name}} doesn't seem to work at all.
This same issue got me a few times when I started out with Meteor...
When the Meteor client connects to the server the template is being rendered before the collections have finished being synchronised. i.e. The client collection is empty at the point you are calling findOne.
To see this in action stick a console.log(c) after your findOne call then try reloading the page. You will see two log entries; Once on initial page load and then again when the collection has finished being synchronised.
To fix this all you need to do is update your hello template to handle the fact the collection might not have been synchronised.
{{#if showcount}}
{{showcount.name}}
{{/if}}
I tested your code with the above change and it works.
The proper way to do this is with the #with tag.
<template name="hello">
{{#with showcount}}
{{name}}
{{/with}}
</template>
See the documentation below for more info on the #with tag
https://github.com/meteor/meteor/blob/devel/packages/spacebars/README.md

Resources