MeteorJS one-page-app navigation - meteor

MeteorJS absolute noob here, so be gentle... :)
I am trying to build a one page web app, and I am using the iron:router.
The idea is that there are a few divs in the page, lets say 3, one for the menu, one which never changes and one where the content should change based on the menu selection.
In a regular page I would just load the content in that one div. But with meteor/router I figured I would use the layoutTemplate feature and put the common parts in one template, then call another one with {{> yield}}.
Problem 1: That worked fine when I only had the /room template, but as soon as I introduced the /user template, it just won't load :( It loads for a moment and then skips back to the /room, visibly reloads the /room. Now, ofcourse I'm doing something wrong, I just don't see what :(
Problem 2: It actually reloads the entire page whenever it loads /user and then goes back to /room, which means it disconnects and reconnects every time, is there a way to avoid that and to actually keep it as one page app that stays connected and only loads some template into a div?
router.js:
Router.configure({
notFoundTemplate: "404"
});
Router.map(function () {
this.route('/', {
path: '/',
layoutTemplate: 'welcome',
onBeforeAction: function (pause) {
if (checkLogin()) {
Router.go('/room');
} else {
this.next();
}
}
}),
this.route('welcome', {
path: '/welcome',
layoutTemplate: 'welcome',
onBeforeAction: function (pause) {
if (checkLogin()) {
Router.go('/room');
} else {
this.next();
}
}
}),
this.route('room', {
path: '/room',
layoutTemplate: 'base',
onBeforeAction: baseAction
}),
this.route('user', {
path: '/user',
layoutTemplate: 'base',
onBeforeAction: baseAction
})
});
function baseAction(pause) {
if (!checkLogin()) {
Router.go('/welcome');
} else {
this.next();
}
}
function checkLogin() {
return Meteor.user() ? true : false;
}
base.js:
Template.base.events({
"click #menu-logout": function () {
Meteor.logout();
},
"click #menu-room": function () {
window.location.href = '/room';
},
"click #menu-user": function () {
window.location.href = '/user';
}
});
base.html:
<template name="base">
<div id="menu">
<table style="height: 33px; width: 100%;">
<tr>
<td id="menu-room"><img src="img/logo.png" /></td>
<td id="menu-user">me</td>
<td id="menu-logout">logout</td>
</tr>
</table>
</div>
<div id="content">{{> yield}}</div>
<div id="list">{{> tmpList}}</div>
</template>
room.html:
<template name="room">
room data
</template>
user.html:
<template name="user">
user data
</template>

Apparently, one should never give up :)
The solution to both problems listed is to not use window.location for navigation but to use Router.go instead.
This change in base.js fixed everything:
Template.base.events({
"click #menu-logout": function () {
Meteor.logout();
},
"click #menu-room": function () {
Router.go('/room');
},
"click #menu-user": function () {
Router.go('/user');
}
});

Related

Iron router Couldn’t find a template named... Are you sure you defined it?

I am trying to render a template on a given page but I get the following error:
Couldn't find a template named "adPage" or "adPage". Are you sure you defined it?
myapp/lib/router.js
This is how I defined the route:
Router.route("/dashboard/adpage/:_id", {
// name:"adPage",
template:"adPage",
data:function(){
return Ads.findOne({_id: this.params._id});
},
onBeforeAction : function()
{
if(Meteor.userId())
{
//user is loggedin continue the route
this.next();
}
else {
console.log("user not loggin, redirect to login page");
this.render('login');
}
},
onAfterAction: function(){
document.title = 'Annonce';
},
waitOn:function(){
return Meteor.subscribe("adDetails",this.params._id);
}
});
myapp/client/main.html
<template name="main">
...
{{> yield}}
...
</template>
myapp/client/adPage/adPage.html
<template name="adPage">
{{title}}
...
</template>
myapp/client/adPage/adPage.js
Template.adPage.onCreated(function() {
console.log("on created");
});
If I move the adPage template to my main.html file, I no longer have the error (I can see the title) but the onCreated is not called.
I tried to put everything in the same client folder and renaming adPage.js to _adPage.js and everything worked as expected.
So I suspect something wrong with the load order but can't figure out what...
Thanks !
If you are using an adblocker. Try to disable it. Its probably because of the filename

Iron-router + Fast-render, why is this an endless loop?

why is this an endless loop? [ Iron Router + Fast Render + Blaze]
Router.route("/:cycle_uri", {
name: "cycle"
,template: "home"
,onBeforeAction: function () {
console.log("is there a loop here?") // this is what confirms that it's a continuous loop
var cycle = Cycles.findOne({
"uri": this.params.cycle_uri
});
if (typeof cycle === "undefined") {
this.render("notFound"); return;
} else {
ActiveCycle.set(cycle); // if I remove this, there is no continuous loop anymore... but if I remove it I don't see how to have this info in the client
this.render("home");
}
}
,waitOn: function () {
Meteor.subscribe('featuredListsPub', {
'program_id': this.params.cycle_uri
});
}
,fastRender: true
});
I was trying to update ActiveCycle variable so I can read it in the frontend but it's not actually working... I'm certainly doing something wrong, but would like to first understand why updating the reactive var is creating a loop.
I've also tried
if (ActiveCycle.get() !== cycle) {
ActiveCycle.set(cycle);
}
but it also enters a loop... which I don't understand why
for your question in the comments:
How do you subscribe to two publications:
here is my answer:
waitOn: function () {
return [
Meteor.subscribe('subscription1'), Meteor.subscribe('subscription2')
];
}
However, i strongly recommend:
Create on publication and return two cursors
Use Template level subscriptions
Good Luck!
An example of Template level subscriptions:
Template.templatename.onCreated(function () {
Template.autorun(function () {
var subscription = Meteor.subscribe('some_publication');
if (subscription.ready()) {
// do something
}
});
});
and within the template
<template name="templatename">
{{#if Template.subscriptionsReady}}
<div>Your Template here...</div>
{{else}}
<p>Loading...</p>
{{/if}}
</template>
A nice article is right here.

Inserting Blank item to database then redirecting to my item view route causes duplicate template elements in DOM

Let's say I have two routes.
One route is a "new Item" route and one is a "view Item" route.
In order to create a blank form I was just inserting a "blank" object into the database and then redirecting to the item view.
I understand this may not be the best way to do this, and I'm open to suggestions because I don't find a lot of tutorials that deal with this paradigm. Most just have a separate input form (which I don't want).
So the problem I'm running into is that when I navigate to my /new route, it ends up duplicating all the template content in the dom for my /org route even though it's just getting redirected to my /org route. I can reload the /org route all day with different "org data" and it never duplicates anything in my DOM. But the instant I hit that /new route button, it will add another set of DOM elements in duplicate fashion.
Can someone help me shed some light on what's going on? Here's my /new route code...
Router.route('/new/', {
name: 'new',
action: function() {
var newId = Organizations.insert({
// Set some defaults
init_date: new Date(),
modified_date: new Date()
});
this.redirect('/org/' + newId);
}
});
Here's my /org route code:
Router.route('/org/:_id', {
name: 'org',
subscriptions: function() {
return [
Meteor.subscribe('contacts', this.params._id),
Meteor.subscribe('organization', this.params._id)
];
},
onBeforeAction: function() {
user = Meteor.user();
if (!Roles.userIsInRole(user, ['admin'])) {
this.redirect('/submit-ticket');
this.stop();
} else {
this.next();
}
},
action: function() {
if (this.ready()) {
Session.set('currentOrgId', this.params._id);
this.layout('appLayout');
this.render('orgHead', {
to: 'orghead',
data: function() {
return Organizations.findOne({
_id: this.params._id
});
}
});
this.render('orgInfo', {
to: 'content',
data: function() {
return Organizations.findOne({
_id: this.params._id
});
}
});
} else {
this.layout('appLayout');
this.render('loading', {
to: 'content'
});
}
}
});

What changes in the execution of an iron router route when coming in on a deep link?

I have a small meteor app going with iron router. Here's my routes.js, which is available to both client and server:
Router.configure({
layoutTemplate: 'defaultLayout',
loadingTemplate: 'loading'
});
// Map individual routes
Router.map(function() {
this.route('comicCreate', {
path: '/comic/create'
});
this.route('comicDetails', {
onBeforeAction: function () {
window.scrollTo(0, 0);
},
path: '/comic/:_id',
layoutTemplate: 'youtubeLayout',
data: function() { return Comics.findOne(this.params._id); }
});
this.route('frontPage', {
path: '/',
layoutTemplate: 'frontPageLayout'
});
this.route('notFound', {
path: '*',
where: 'server',
action: function() {
this.response.statusCode = 404;
this.response.end('Not Found!');
}
});
});
I need to feed some fields from my comic collection document into a package that wraps Youtube's IFrame API, and I'm doing this via the rendered function:
Template.comicDetails.rendered = function() {
var yt = new YTBackground();
if (this.data)
{
yt.startPlayer(document.getElementById('wrap'), {
videoIds: this.data.youtubeIds,
muteButtonClass: 'volume-mute',
volumeDownButtonClass: 'volume-down',
volumeUpButtonClass: 'volume-up'
});
}
};
This works great when I start by going to localhost:3000/ and then clicking on a link set to "{{pathFor 'comicDetails' _id}}". However, if I go directly to localhost:3000/comic/somevalidid, it doesn't, despite the fact that both routes end up pointing me at the same url.
The reason appears to be that when I go directly to the deep link, "this.data" is undefined during the execution of the rendered function. The template itself shows up fine (data correctly replaces the spacebars {{field}} tags in my template), but there's nothing in the "this" context for data when the rendered callback fires.
Is there something I'm doing wrong here?
try declaring the route in Meteor.startup ... not sure if that's the problem but worth a try

Meteor.renderList alway end up in [elseFunc]

I'm new to Meteor.
Trying to render items from collection but Meteor.renderList(observable, docFunc, [elseFunc]) alway go to elseFunc.
this.ComponentViewOrdersFlow = Backbone.View.extend({
template: null,
initialize: function() {
var frag;
Template.ordersFlow.events = {
"click a": function(e) {
return App.router.aReplace(e);
}
};
this.template = Meteor.render(function() {
return Template.ordersFlow();
});
console.log(Colors);
frag = Meteor.renderList(
Colors.find(),
function(color) {
console.log(color);
},
function() {
console.log('else consdition');
}
);
},
render: function() {
this.$el.html(this.template);
return this;
}
});
Initially I thought that Collection is empty, but console.log(Colors) shows that there are items in collection. Moreover if I use Meteor.render(... -> Template.colors({colors: Colors.find()}) ) it renders template end show Collection items there.
Meteor version 0.6.6.3 (Windows 7, 64bit)
Mongo - connected to MongoLab
Thank you for any help.
Jev.
Can't really explain this well in the comments, so here is a very, very simple example of using the Meteor template engine. This is a 100% functional app, showcasing basic reactivity. Note that I never call render() or renderList() anywhere.
All this app does is show a button, that when clicked, adds a number to a list. The number is reactively added to the list, even though I never do anything to make that reactivity explicit. Meteor's templates are automatically reactive! Try it out - this is all of the code.
numbers.html:
<body>
{{> numberList}}
</body>
<template name="numberList">
<ul>
{{#each numbers}}
<li>{{number}}</li>
{{/each}}
</ul>
<button>Click Me</button>
</template>
numbers.js:
var Numbers = new Meteor.Collection("numbers");
if (Meteor.isClient) {
Template.numberList.numbers = function() {
return Numbers.find();
};
var idx = 0;
Template.numberList.events({
"click button": function() {
Numbers.insert({
number: idx
});
idx++;
}
});
}

Resources