Meteor Iron:Router Template not Rendering - meteor

I have a main page which lists a few text items ("Ideas"), which are clickable links. Clicking on them should take you to a page where you can edit them. Here's my html:
<head>
<title>Ideas</title>
</head>
<body>
</body>
<template name="Ideas">
<ul>
{{#each ideas}}
{{> idea}}
{{/each}}
</ul>
</template>
<template name="idea">
<li>{{text}}</li>
</template>
<template name="ShowIdea">'
<div class="editable" contentEditable="true">{{text}}</div>
</template>
I've added Iron:Router to my project to allow for moving between the pages. Here's the javascript:
Ideas = new Mongo.Collection("ideas");
if (Meteor.isClient) {
Router.route('/', function() {
this.render('Ideas');
});
Router.route('/idea/:_id', function() {
var idea = Ideas.findOne({_id: this.params._id});
this.render('ShowIdea', {text: idea.text});
});
Template.Ideas.helpers({
ideas: function () {
return Ideas.find({});
}
});
}
I inserted a single idea to my Mongo DB using the Meteor Mongo command line tool. That single item shows up properly on my main page. Here's what the HTML looks like in my debugger for the main page:
<html>
<head>...</head>
<body>
<ul>
<li>
The first idea ever
</li>
</ul>
</body>
</html>
Clicking on that link takes me to a new page with an address of:
http://localhost:3000/idea/ObjectID(%22550b7da0a68cb03381840feb%22)
But nothing shows up on the page. In the debugger console I see this error message + stack trace, but it means nothing to me since it all seems to be pertaining to iron-router and meteor, not code which I actually wrote:
Exception in callback of async function: http://localhost:3000/Idea.js?2fd83048a1b04d74305beae2ff40f2ea7741d40d:10:44
boundNext#http://localhost:3000/packages/iron_middleware-stack.js?0e0f6983a838a6516556b08e62894f89720e2c44:424:35
http://localhost:3000/packages/meteor.js?e53378596562e8922a6369c955bab1e047fa866b:978:27
onRerun#http://localhost:3000/packages/iron_router.js?a427868585af16bb88b7c9996b2449aebb8dbf51:520:13
boundNext#http://localhost:3000/packages/iron_middleware-stack.js?0e0f6983a838a6516556b08e62894f89720e2c44:424:35
http://localhost:3000/packages/meteor.js?e53378596562e8922a6369c955bab1e047fa866b:978:27
onRun#http://localhost:3000/packages/iron_router.js?a427868585af16bb88b7c9996b2449aebb8dbf51:505:15
boundNext#http://localhost:3000/packages/iron_middleware-stack.js?0e0f6983a838a6516556b08e62894f89720e2c44:424:35
http://localhost:3000/packages/meteor.js?e53378596562e8922a6369c955bab1e047fa866b:978:27
dispatch#http://localhost:3000/packages/iron_middleware-stack.js?0e0f6983a838a6516556b08e62894f89720e2c44:448:7
_runRoute#http://localhost:3000/packages/iron_router.js?a427868585af16bb88b7c9996b2449aebb8dbf51:543:17
dispatch#http://localhost:3000/packages/iron_router.js?a427868585af16bb88b7c9996b2449aebb8dbf51:844:27
route#http://localhost:3000/packages/iron_router.js?a427868585af16bb88b7c9996b2449aebb8dbf51:710:19
boundNext#http://localhost:3000/packages/iron_middleware-stack.js?0e0f6983a838a6516556b08e62894f89720e2c44:424:35
http://localhost:3000/packages/meteor.js?e53378596562e8922a6369c955bab1e047fa866b:978:27
boundNext#http://localhost:3000/packages/iron_middleware-stack.js?0e0f6983a838a6516556b08e62894f89720e2c44:371:18
http://localhost:3000/packages/meteor.js?e53378596562e8922a6369c955bab1e047fa866b:978:27
dispatch#http://localhost:3000/packages/iron_middleware-stack.js?0e0f6983a838a6516556b08e62894f89720e2c44:448:7
http://localhost:3000/packages/iron_router.js?a427868585af16bb88b7c9996b2449aebb8dbf51:390:21
_compute#http://localhost:3000/packages/tracker.js?21f0f4306879f57e10ad3a97efe9ea521c5b5775:308:36
Computation#http://localhost:3000/packages/tracker.js?21f0f4306879f57e10ad3a97efe9ea521c5b5775:224:18
autorun#http://localhost:3000/packages/tracker.js?21f0f4306879f57e10ad3a97efe9ea521c5b5775:499:34
http://localhost:3000/packages/iron_router.js?a427868585af16bb88b7c9996b2449aebb8dbf51:388:17
nonreactive#http://localhost:3000/packages/tracker.js?21f0f4306879f57e10ad3a97efe9ea521c5b5775:525:13
dispatch#http://localhost:3000/packages/iron_router.js?a427868585af16bb88b7c9996b2449aebb8dbf51:387:19
dispatch#http://localhost:3000/packages/iron_router.js?a427868585af16bb88b7c9996b2449aebb8dbf51:1688:22
onLocationChange#http://localhost:3000/packages/iron_router.js?a427868585af16bb88b7c9996b2449aebb8dbf51:1772:33
_compute#http://localhost:3000/packages/tracker.js?21f0f4306879f57e10ad3a97efe9ea521c5b5775:308:36
_recompute#http://localhost:3000/packages/tracker.js?21f0f4306879f57e10ad3a97efe9ea521c5b5775:322:22
flush#http://localhost:3000/packages/tracker.js?21f0f4306879f57e10ad3a97efe9ea521c5b5775:452:24
And then it ends with this warning message:
Route dispatch never rendered. Did you forget to call this.next() in an onBeforeAction?
I don't have an onBeforeAction (I'm not even sure what that is)... so I don't think that message pertains to me?
I just started using Meteor the other day and just added iron-router not 24 hours ago, so I'm a bit lost here. Any pointers on how I can debug and fix this would be great.

Two things need fixing:
When you insert documents from the shell they are assigned _id values which are mongo ObjectIDs, whereas meteor defaults to using strings. This explains the weird URL. To avoid this problem, it's generally best to initialize your data from the server. Here's an example:
if (Meteor.isServer) {
Meteor.startup(function() {
if (Ideas.find().count() === 0) {
Ideas.insert({text: 'feed the cat'});
}
});
}
Now after a $ meteor reset you will always start with one cat-related idea.
If you wish to pass a context to your template, you'll need to use the data attribute like so:
Router.route('/idea/:_id', function() {
this.render('ShowIdea', {
data: function () {return Ideas.findOne({_id: this.params._id})}
});
});
See this example from the docs. After making those changes, the code worked correctly for me.

Related

Defined two meteor local collections and helpers exactly the same. One helper works. The other doesn't

I create these two local collections (the code is actually written one after the other exactly like below):
ShoppingCartCollection = new Meteor.Collection(null);
CurrentPricesCollection = new Meteor.Collection(null);
Inside Template.myTemplate.rendered I add some initial info into these collections (again, code is one after the other):
ShoppingCartCollection.insert({"sqft" : "not yet entered"});
CurrentPricesCollection.insert({"hdrPhotos" : 100});
I've got these two global helpers in helpers.js (defined one after the other)
Handlebars.registerHelper("shoppingCart", function() {
return ShoppingCartCollection.findOne();
});
Handlebars.registerHelper("currentPrice", function() {
return CurrentPricesCollection.findOne();
});
When I load the page I immediately run these commands in the console:
> ShoppingCartCollection.findOne();
Object {sqft: "not yet entered", _id: "xcNmqJvMqqD5j7wwn"}
> CurrentPricesCollection.findOne();
Object {hdrPhotos: 100, _id: "LP38E3MZgzuYjvSec"}
In my template I use these helpers, but...
{{currentPrice.hdrPhotos}} //displays nothing
{{shoppingCart.sqft}} //displays "not yet entered"
How... what... ? How can this be? Are there some kind of gotchas that I could be missing? Some kind of dependency or load order that I'm not aware of?
The code you posted is working fine here.
Suggest comparing this code to the exact details of what you are doing. Also, look
for other problems, typos, etc.
Below is the exact test procedure I used:
From nothing, at the linux console:
meteor create sodebug
Note that this will produce files for a "hello world" type program.
Check the version:
meteor --version
Release 0.8.1.1
Edit sodebug/sodebug.js:
if (Meteor.isClient) {
// code autogenerated by meteor create
Template.hello.greeting = function () {
return "Welcome to sodebug.";
};
Template.hello.events({
'click input': function () {
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});
// add your code here
ShoppingCartCollection = new Meteor.Collection(null);
CurrentPricesCollection = new Meteor.Collection(null);
ShoppingCartCollection.insert({"sqft" : "not yet entered"});
CurrentPricesCollection.insert({"hdrPhotos" : 100});
Handlebars.registerHelper("shoppingCart", function() {
return ShoppingCartCollection.findOne();
});
Handlebars.registerHelper("currentPrice", function() {
return CurrentPricesCollection.findOne();
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
Edit sodebug.html:
<head>
<title>sodebug</title>
</head>
<body>
{{> hello}}
{{> T1 }}
{{> T2 }}
</body>
<template name="T1">
<p>
{{shoppingCart.sqft}}
</p>
</template>
<template name="T2">
<p>
{{currentPrice.hdrPhotos}}
</p>
</template>
<template name="hello">
<h1>Hello World!</h1>
{{greeting}}
<input type="button" value="Click" />
</template>
Run: meteor run
Manual tests:
Fire up chromium browser at localhost:3000
Check web browser console for collections data. PASS
Check web browser screen for templates data. PASS
Reorder templates in sodebug.html file, check web browser screen. PASS

Pass data to a dynamic template

With meteor updates up to 0.8 my old code stopped working.
Handlebars.registerHelper('getTemplate', function(id, context) {
return Template[id](context);
});
<template name="main">
....
{{{getTemplate templateName context}}}
....
</template>
//somewhere in other template
Template.main.context = {name:value};
This way I was able to render a custom template with custom data. Now I can't find the way to pass context to the dynamic template. With blaze both templateName and context is undefined. Any advice?
Meteor >= 0.8.2
You can use the UI.dynamic helper render a template with a context which are both specified dynamically. For more details, check out this issue.
Meteor < 0.8.2
Both of these issues are addressed on this page in the meteor wiki.
Handlebars.registerHelper is now UI.registerHelper as seen here.
Examples of how to dynamically render templates are shown here.
update
Actually, given the requirements, a solution doesn't seem very obvious to me. If you are willing to use session variables to set the template name and context, AND only have one dynamic template in your main template. You could do something like this:
<body>
{{> main}}
</body>
<template name="main">
{{> getTemplate context}}
</template>
<template name="dogs">
<p>There are {{animals}} dogs!</p>
</template>
<template name="cats">
<p>There are {{animals}} cats!</p>
</template>
Session.setDefault('templateName', 'dogs');
Session.setDefault('templateContext', {animals: 10});
Template.main.getTemplate = function() {
return Template[Session.get('templateName')];
};
Template.main.context = function() {
return Session.get('templateContext');
};
This was brought up on the meteor-core list and #dgreensp, MDG core dev working on Blaze, opened Ticket #2007 - How to render a template to HTML with data so they definitely know about this and I'd expect a fix to land soon after 0.8.0.
He also included the following workaround:
var toHTMLWithData = function (kind, data) {
return UI.toHTML(kind.extend({data: function () { return data; }}));
};
The github ticket has further discussion and alternate code snippets that you may find useful.

Meteor changing variables in html [simple]

I know this is completely simple, but it's also completely is stumping me on why it isn't working. This gets to the point of rendering the html and showing Hello World with a message below "Welcome to chat" and a button "say hello back" but what it ISN'T doing is then change the message to "work".
I have a .js file which is:
if (Meteor.isClient) {
var message="welcome to chat";
function template(message){
Template.hello.greeting = function () {
return message;
};};
template(message);
Template.hello.events({
'click input' : function () {
template("work ");
}
});
}
and a html follow as shown:
<head>
<title>chat</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
<h1>Hello World!</h1>
{{greeting}}
<button value="Click">Say Hello Back!</button>
</template>
And it's embarrassingly simple but I just can't figure out what I'm doing wrong. I know I shouldn't re render the page because the whole point of using meteor is for it's live html so what do I do have to do?
I figured out the main problem!
For my html I was using a button class but I should've been using a input type="button" instead!
To make it "reactive" you should use Session that meteor provides. You can simplify your code to make it easier to read and understand.
Session provides a global object on the client that you can use to
store an arbitrary set of key-value pairs. Use it to store things like
the currently selected item in a list.
You set the session variable to "welcome to chat" first. In your click event you would set the Session variable to "work". Then you would set the template to return the value of the Session variable when it changes. Your javascript could look something like this.
if (Meteor.isClient) {
Session.set("message", "welcome to chat");
Template.hello.greeting = function () {
return Session.get("message");
}
Template.hello.events({
'click input' : function () {
Session.set("message", "work");
}
});
}
This is untested but give it a try.
i am not quite sure about what do you mean by "not working". But i am sure you will have to do following.
List item you need to prevent calling default event by the browser.
ex:-
Template.hello.events({
'click input' : function () {
//your code here
return false;
}
});
2 . use meteor methods instead of having template()

Setting {reactive: false} when searching a collection in Meteor still updates the template

I've got a news feed idea I'm trying out in Meteor, but I'm having issues with making the damn thing behave :) I want it to load up the news feed on page load / refresh, but not when the data changes. I found in the documentation that adding {reactive: false} to the find method of a collection should make it stick to the results generated on render, but it doesn't seem to work for me. Meteor keeps updating the template right away.
Here's the code I've got:
On the server side:
Meteor.publish("newsfeed", function () {
return Newsfeed.find({});
});
On the client side:
Meteor.subscribe('newsfeed');
Template.feed.feed_data = function() {
var feed = Newsfeed.find({}, {
sort: {updated_time: -1},
limit: 10,
reactive: false
});
return feed;
};
In the template:
<template name="feed">
<div id="feed-wrapper">
<ul>
{{#each feed_data}}
<li>
<div class="message">{{message}}</div>
</li>
{{/each}}
</ul>
</div>
</template>
If I then run Newsfeed.update({_id: 'some_random_id'}, {$set: {date_created: 'some_random_date'}}) in Dev Tools, the template updates the sorting of my news feed and changes it.
How do I make it not do that? :D
This is arguably a bug in Meteor. Passing reactive: false means that minimongo itself doesn't set up some code to say "observe and if it changes, invalidate". But #each has its own separate observeChanges call which uses the observe callbacks directly (not a reactive "invalidate and recalculate") to update the list. Probably we should not do this if the cursor has reactive: false on it. Track this in https://github.com/meteor/meteor/issues/771 !
Thats a bit odd, it ought to work. You could also use preserve:
Try adding this line in your client js
Template.feed.preserve(['#feed-wrapper']);
Btw is the template name="feed" in another template? Does this template have any reactive variables in it?

Reactive updates not received in other browsertabs

Meteor promises reactive updates, so that views are auto-updated when data changes. The included leaderboard example demonstrates this. It runs fine when I test it: data is updated across several browsertabs in different browsers, as expected.
All set and go, I started coding with meteor and progress was being made, but when I tested for reactive updates across browertabs, I noticed that only after a short while the updates across tabs stopped.
I boiled down the problem to the following code, based on a new empty meteor project:
updatebug.html
<head>
<title>updatebug</title>
</head>
<body>
{{> form}}
</body>
<template name="form">
<form onsubmit="return false;">
{{#each items}}
{{> form_item }}
{{/each}}
</form>
</template>
<template name="form_item">
<div>
<label>{{name}}
<input type="text" name="{{name}}" value="{{value}}">
</label>
</div>
</template>
updatebug.js:
Items = new Meteor.Collection("items");
if (Meteor.is_client) {
Template.form.items = function () {
return Items.find();
};
Template.form_item.events = {
'blur input': function(e) {
var newValue = $(e.target).val();
console.log('update', this.name, this.value, newValue);
Items.update({_id: this._id}, {$set: {value: newValue}});
},
};
}
if (Meteor.is_server) {
Meteor.startup(function () {
if (Items.find().count() === 0) {
Items.insert({name: 'item1', value: 'something'});
}
});
}
Run in multiple browsertabs, start changing the value of the input in one tab. The other tabs will reflect the change. Goto the next tab and change the value. Repeat a couple of times.
After a while, no more updates are received by any other tabs. It seems that once a tab has changed the value, it does not receive/show any more updates.
Differences compared to the leaderboard example (since it's very similar):
The leaderboard uses no form controls
The leaderboard example does an increment operation on update, not a set
I am about to file a bug report, but want to be sure I am not doing anything stupid here, or missing an essential part of the Meteor Collection mechanics (yes, autopublish package is installed).
The issue here is input element preservation. Meteor will preserve the input state of any form field with an id or name attribute across a template redraw. The redraw is preserving the old text in your form element, because you wouldn't want to interrupt another user typing in the same field. If you remove the name attribute from the text box, each tab will update on blur.
In fact, I'm not sure why the first update works in your example. That may actually be the bug!
You can see it's not a data problem by opening the console in each browser. On each blur event you will get an updated document in every open tab. (Type Items.find().fetch())

Resources