Iron Router (meteor): how to set default route? - meteor

I've 4 views (templates):
start
showScannedData
dashboard
loading
I defined routing in client_server/lib/routing.js:
Router.configure({
loadingTemplate: 'loading'
});
Router.route('/', {
name:'start',
template:'start'
});
Router.route('/mobile', {
name:'mobileStart',
template:'showScannedData'
});
Router.route('/desktop', {
name:'desktopStart',
template:'dashboard'
});
In client/app.js I put:
Router.go('/start');
It works. However the template showScannedData is rendered twice, as if I set it as templateLayout too.
As for my application I don't need a default layout but single individuals view to be loaded either on start (template: start) or on a specific action performed by the user (templates: showScannedData / dashboard).
What am I doing wrongly?

Thanks to #piscator and #Keith for giving me a key hint.
I figure out that, while I wasn't rendering the template twice, I put
{{>> showScannedData}}
in project_name.html file (i.e. the file rendered when routing is not used).
So I basically removed the line above from the file and used Iron's router to go to starter template by:
Router.go("/")
in client/app.js, which is defined in client_server/lib/routing.js as
Router.route('/', {
name:'start',
template:'start'
});
corresponding to
<template name="start">
<input id="goToMobileBtn" type="button" value="Go to mobile view"/><br><br>
<input id="goToDesktopBtn" type="button" value="Go to desktop view"/>
</template>
Hint: I found out that since "/" route is the first one defined in routing.js, the Router.go is optional.

Related

How to access the subscriptions inside the HTML file?

Usually when I use helpers, I can access the returned values as below:
Template.oveview.helpers({
item: function () {
return Requests.find({});
},
Then in the client side I can use {{#each item}}, But I don't know how to display them in the .html when using publish and subscribe
Here is my publish:
Meteor.startup(() => {
Meteor.publish('requests', function queryRequests() {
return Requests.find({});
});
});
And here is my subscribe:
Template.overview.onCreated(function() {
Meteor.subscibe('requests');
});
How can I display the returned value from publish in the client side?
You can use it in a few different ways. You can use Meteor templates to insert a HTML snippet for every item in-between existing HTML:
{{#each item}}
{{> htmlTemplateName}}
{{/each}}
Or you can just place raw HTML in the {{#each}} loop:
{{#each item}}
<p>{{propertyX}}</p>
<p>{{propertyY}}</p>
{{/each}}
You might run into problems with the pubsub, depending on load order (I don't know load orders, I'm afraid). I used the iron-router package in my project, to bind certain end-points to specific HTML files. iron-router has this nice parameter you can set for every page, called waitOn, where I placed my subscriptions. This means that subscribing to a certain collection happens before anything else.
Router.configure({
layoutTemplate: '_layoutTemplate',
name: 'myTemplateName',
waitOn: function() {
return [
Meteor.subscribe('requests'),
//Add other subscriptions here
];
}
});

Meteor Iron:Router Template not Rendering

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.

Meteor - How to properly display spinner while template loads

I've got a template that has an intentional delay in it using http://deelay.me so I can test a slow server.
<template name="aboutPage">
<img src="http://deelay.me/2000/http://i.telegraph.co.uk/multimedia/archive/02830/cat_2830677b.jpg">
</template>
On my router page:
Router.configure
layoutTemplate: 'layout'
loadingTemplate: 'loading'
Router.route '/', name: 'homePage'
Router.route '/about', name: 'aboutPage'
loading template:
<template name="loading">
{{> spinner}}
</template>
For some reason, when I hit the About page, I get a missing image icon as the image loads. Am I missing something?
I made it works with this.
Router.route('/about', {
name: 'about',
data: function() {
if(this.ready()){
this.render()
}else{
this.render('loading')
}
}
});
Take a look, here we ensure that the all data on template is ready, if not we render the 'loading' template.
That works for me
I suggest you checking this package: https://atmospherejs.com/mrt/iron-router-progress
It let's you delay the progress from showing up on fast routes.
Router.configure
progressDelay : 100
Regarding the "missing image icon", I tried to reproduce the situation but I couldn't. However, I noticed that using the deelay.me technic won't work for testing the loading between routes. That's because aboutPage is already loaded when the image request starts. Therefore, the loading spinner already disappeared.
Check this repo por a simple example that actually tracks the image loading: https://github.com/humberaquino/meteor-imageloading-example
I hope this helps :)

meteor app: how to properly use a button to influence the route

I'm working on a survey/wizard type of meteor application.
The interface will only have two navigation buttons, who's caption/value will be determined dynamically based on whatever step a given user is currently at.
My question is: what markup syntax should I use within each of these two button's definitions in order to direct the Iron Router to change the route. Another words, where in the button definition should I put this: this.redirect('/anotherpath') ?
If you have a button in your template. If you are using a hrefs check answer from f3rland.
html:
<button #id="mybutton>m<Button</button>
you can catch the buttonpress event with javascript
js
Template.myTemplate.events({
'click #myButton': function(event, template) {
event.preventDefault();
Router.go('anotherpath');
}
});
The router should look like:
Router.map(function() {
this.route('anotherpath', {
path: '/',
layoutTemplate: 'myLayout',
controller: myController
});
});
I suggest you to use Iron-Router pathFor helper in your template
{{buttonText}}
That will redirect the user to the corresponding page/template.
You should also take a look at new blaze pattern to define custom block helper
Edit:
You could also use a custom helper for dynamic route as mentioned here
UI.registerHelper("_foo", function fooHelper() {
var templateName = figureOutNameDynamically(this.context.criticalInfo);
return Template[templateName];
});
<template name="foo">
{{> _foo context=.. atts=this}}
</template>
you can do this/change the route by using the action option which will automatically cal this.render().
you can do it yourself by calling the action function but I guess it is better to use onBeforeAction/after hooks

Meteor template gets rendered twice

My template is getting rendered twice on first load. I notice this because in
Template.home.rendered = function() {
console.log('rendered'); // => this is printed out twice
console.log(Data.find({}).count()); // => this is initially 0, then 1 the second time
}
Furthermore, on the first load, no Data is available. Yet on the second load, the Data is there.
Does anyone know what this problem might be, and why the data only appears the second time?
You need to find a way to render the template when your data is available.
Using this template structure, the if block content, which happens to be the template displaying your data, will be rendered only when the myDataIsReady helper returns true. (thus triggering the rendered callback only once, with data immediately available).
<template name="displayData">
<p>This is my data : {{this}}</p>
</template>
<template name="home">
{{#if myDataIsReady}}
{{#each data}}
{{> displayData}}
{{/each}}
{{/if}}
</template>
You have to define a subscription handle (an object returned by Meteor.subscribe) in order to use it's reactive ready method : we'll reference it in the myDataIsReady helper to track data availability, and the helper will automatically rerun when the state of ready changes.
Meteor.startup(function(){
// this subscription should return your data subset
myDataHandle=Meteor.subscribe("myData");
});
Template.home.myDataIsReady=function(){
return myDataHandle.ready();
}
Template.home.data=function(){
return Data.find({});
}
But that's quite annoying for such a simple task.
That's why I suggest using the Iron Router which makes things way simpler !
Add it to your project using "mrt add iron-router", then in a client/router.js and client/router.html, use this boilerplate code :
Router.configure({
loadingTemplate:"loading"
});
Router.map(function(){
this.route("home",{
path:"/",
// we indicate which subscription has to be marked ready in order to load the template
waitOn:function(){
return Meteor.subscribe("myData");
}
// the result of this function will become our target template data context
data:function(){
return Data.find({});
}
});
});
<template name="home">
{{#each this}}
{{> displayData}}
{{/each}}
</template>
<template name="loading">
<p>Data isn't ready yet...</p>
</template>
As you can see, the Iron Router allows us to specify simply what we painfully achieved manually in the first code example (waiting on a particular subscription to render a template), and of course we get free routing, loading mechanisme, layout management, etc...
Search the web for a complete iron-router tutorial (my code is untested, but I hope it is ok and should get you started), it's so awesome that it's gonna be merged to Meteor ultimately.
I had a body.html in /client and a appBody.html in /client/templates, with, in iron router:
Router.configure({
layoutTemplate: 'appBody',
});
Both body templates were rendered (and happened to be the same). Obviously, the body.html in /client needed to be removed.

Resources