It seems so simple, but I'm trying to use Meteor's account system with Iron:Router to setup an admin system for a web app. The admin section is the only part of the app with accounts. The admin section is to be at /admin
and has a login template, and a data template which should be displayed after the admin logs in.
Base Routers
Router.route('/', {
template: 'user' // Not shown; works fine
});
Router.route('/admin', {
template: 'adminDataPage'
});
Templates
<template name="adminLoginPage">
<div class="cover-wrapper-outer page-admin login">
<div class="cover-wrapper-inner">
<div class="cover-container">
<div class="cover">
<form class="admin-login-form">
<div class="alert alert-danger login-failure">
The username or password is incorrect.
</div>
<input class="form-control username" name="username" type="text" placeholder="Username">
<input class="form-control password" name="password" type="password" placeholder="Password">
<input class="form-control submit" name="submit" type="submit" value="Log in">
</form>
</div>
</div>
</div>
</div>
</template>
<template name="adminDataPage">
<div class="container-fluid page-admin admin-content">
<div class="row">
<div class="col-xs-12">
<div class="scrolling-table">
<table class="table table-striped">
<thead>
<tr>
<th>Year</th>
<th>Make</th>
<th>Model</th>
<th>Engine</th>
</tr>
</thead>
<tbody>
<tr>
<td>Year</td>
<td>Make</td>
<td>Model</td>
<td>Engine</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</template>
<template name="adminLoggingIn">
Logging in...
</template>
(This is incomplete as of right now but it doesn't relate to the problem.)
The content inside the adminDataPage
template is never rendered, no matter what I do. However, anything that is not inside an HTML element is rendered. For example, this would render:
<template name="adminDataPage">
Hello world!
</template>
The world
in the following would not render:
<template name="adminDataPage">
Hello <span>world</span>!
</template>
The same goes with the adminLoggingIn
template. However, anything in the adminLoginPage
template works without an issue.
In essence, the login box is displayed, the client-side Javascript (not shown) takes care of logging in the user, the user sees Logging in...
, then the user sees nothing and nothing is shown in the browser's inspector (unless there is un-nested plain text in the adminDataPage
template).
If currentUser
<template name="adminDataPage">
{{#if currentUser}}
<div class="container-fluid page-admin admin-content">
<div class="row">
<div class="col-xs-12">
<div class="scrolling-table">
<table class="table table-striped">
<thead>
<tr>
<th>Year</th>
<th>Make</th>
<th>Model</th>
<th>Engine</th>
</tr>
</thead>
<tbody>
<tr>
<td>Year</td>
<td>Make</td>
<td>Model</td>
<td>Engine</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
{{else}}
<div class="cover-wrapper-outer page-admin login">
<div class="cover-wrapper-inner">
<div class="cover-container">
<div class="cover">
<form class="admin-login-form">
<div class="alert alert-danger login-failure">
The username or password is incorrect.
</div>
<input class="form-control username" name="username" type="text" placeholder="Username">
<input class="form-control password" name="password" type="password" placeholder="Password">
<input class="form-control submit" name="submit" type="submit" value="Log in">
</form>
</div>
</div>
</div>
</div>
{{/if}}
</template>
This will render the login page when not logged in, as expected. However, once logged in, nothing is rendered. I have also tried integrating {{#if loggingIn}}
but to no avail.
Iron:Router hooks
Router.route('/', {
template: 'user'
});
Router.route('/admin', {
template: 'adminDataPage'
});
var requireLogin = function() {
if (! Meteor.user()) {
if (Meteor.loggingIn()) {
this.render('adminLoggingIn');
} else {
this.render('adminLoginPage');
}
} else {
this.next();
}
};
Router.onBeforeAction(requireLogin, {only: 'admin'});
This will render the login page if not logged in, as expected, but will not render any content inside HTML elements within the adminLoggingIn
or adminLoginPage
templates.
Meteor v1.1.0.3
Iron:Router v1.0.9
I'm so very confused why Meteor (or Iron:Router) refuses to render certain content in certain contexts. Of course, if anybody has any ideas to help resolve this, please shoot; it'd be much appreciated.
Blaze.render()
appears to be at fault here. Using Blaze.render(Template.adminDataPage, document.body)
in the console will not render anything but—just like in my problem—plaintext nested directly inside the body element. It will not nest any DOM elements beneath body.
To test, I created a DOM element <div class="test"></div>
, nested it within the body of the page, and called Blaze.render(Template.adminDataPage, $('.test')[0])
and it worked. It wasn't formatted correctly, but it worked.
Adding a layout template for Iron:Router to use fixes the issue, like so:
<template name="adminPage">
<div class="test">
{{> yield}}
</div>
</template>
Router.route('/admin', {
name: 'admin',
template: 'adminDataPage',
layoutTemplate: 'adminPage'
});
var requireLogin = function() {
login_dep.depend();
if (! Meteor.user()) {
if (Meteor.loggingIn()) {
this.render('adminLoggingIn');
console.log('Logging in page');
} else {
this.render('adminLoginPage');
console.log('Login page');
}
} else {
this.next();
console.log('Data page');
}
};
Router.onBeforeAction(requireLogin, {only: 'admin'});
Also, nesting the {{#if currentUser}}
inside another element works:
<template name="adminPage">
<div class="test">
{{#if currentUser}}
<div class="container-fluid page-admin admin-content">
<div class="row">
<div class="col-xs-12">
<div class="scrolling-table">
<table class="table table-striped">
<thead>
<tr>
<th>Year</th>
<th>Make</th>
<th>Model</th>
<th>Engine</th>
</tr>
</thead>
<tbody>
<tr>
<td>Year</td>
<td>Make</td>
<td>Model</td>
<td>Engine</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
{{else}}
<div class="cover-wrapper-outer page-admin login">
<div class="cover-wrapper-inner">
<div class="cover-container">
<div class="cover">
<form class="admin-login-form">
<div class="alert alert-danger login-failure">
The username or password is incorrect.
</div>
<input class="form-control username" name="username" type="text" placeholder="Username">
<input class="form-control password" name="password" type="password" placeholder="Password">
<input class="form-control submit" name="submit" type="submit" value="Log in">
</form>
</div>
</div>
</div>
</div>
{{/if}}
</div>
</template>
Router.route('/admin', {
name: 'admin',
template: 'adminPage'
});
I'm not sure why Blaze refuses to attach certain elements to the body element, but these are some workarounds.
A better workaround I found is to add an element to which Blaze can prepend the template DOM. Blaze.render(Template.adminDataPage, document.body, $('.render-fix')[0])
where a hidden element with the class of render-fix
is placed in the DOM at the time of the initial render will work. In essence:
<template name="adminPage">
{{#if currentUser}}
<div>User logged in stuff</div>
{{else}}
<div>User not logged in stuff</div>
{{/if}}
<div class="render-fix" style="display: none"></div>
</template>
It appears to me that Blaze will not attach any templates to body after the initial render.