On the client I want to render two different layouts.
Router.route('/', {
template: 'register',
layoutTemplate: 'home'
});
Router.route('/main', {
layoutTemplate: 'layout'
});
In this case only the first Router.route function works. What seems to be the problem in this code example?
In the console, this error pops up:
Exception in defer callback: TypeError: Cannot read property 'handler' of undefined
Additionally I don't want users to access the 'main'-template if they are not logged in. The iron:router documentation doesn't provide me enough information on how to handle both these issues.
This is how your routes should look.
//Layout Configuration.
Router.configure({
layoutTemplate:"register"
});
Router.configure({
layoutTemplate:"layout"
});
//Simple Routes Config.
Router.route('/', {
layoutTemplate: 'register'
});
Router.route('/main', {
layoutTemplate: 'layout'
});
And The /client/views/layout.html
<template name="layout">
{{> yield}}
</template>
<template name="register">
{{> yield}}
</template>
I definitely think that the issue with your routing code above is the fact that you have not specified a value for the template attribute of your route with the path '/main'. At the very least, I would suggest that you define that attribute if nothing else for a given route. The first thing that iron-router will attempt to do to determine which template to use for the route is to look at the path of the URL, and that is not always obvious to determine for a given route. I would suggest defining your routes above like so:
Router.route('/', function() {
template: 'register',
layoutTemplate: 'home'
});
Router.route('/main', function() {
template: 'main',
layoutTemplate: 'layout'
});
With this routing logic defined, you would then also want to make sure to define your templates properly.
For your layout templates, do something like the following:
<template name="home">
{{> yield}}
</template>
<template name="layout">
{{> yield}}
</template>
The {{> yield}} parts are necessary here in the layout templates because that is where iron-router will render the template that you have defined for the route through the template attribute.
For your regular templates, do something like the following:
<template name="register">
<h1>Register</h1>
</template>
<template name="main">
<h1>Main</h1>
</template>
After doing this, do your routes work? Alternatively, you could use Route.configure to set global route attributes, or a series of RouteController objects to define your routes so that common routes can take advantage of common attribute definitions.
As for your second issue, I would suggest using one of the two following methods:
In order to define your described logic for just the '/main' route, do the following:
Router.route('/main', function() {
template: 'main',
layoutTemplate: 'layout',
onBeforeAction: function() {
if(!Meteor.userId()) {
this.render('register'); // Use either this one
this.redirect('register'); // or this one
} else {
this.next();
}
}
});
Going with this option will require that you add an additional attribute to your '/' route, name: 'register'. Alternatively you can directly reference the actual URL path of the desired route in the this.render() or this.redirect() function calls and then you do not have to define the name attribute for your templates. Personally, I prefer being obvious and open with my route definitions by giving them all names and referencing them using their names.
The second option is to define your desired logic globally so that it applies not only to the '/main' route, but to any other routes that you happen to define in the future as well. To do this, do the following:
Router.onBeforeAction(function() {
if(!Meteor.userId()) {
this.render('register'); // Use either this one
this.redirect('register'); // or this one
} else {
this.next();
}
},
{
except: ['register']
});
Once again, this option requires the definition of the name attribute for your '/' route, but you can do what I described above if you prefer not to do that. Finally, notice the except attribute definition at the end of this option. This notifies iron-router to not run this globally-defined logic for the array of specified routes. Obviously, you would not want to run this logic on the very page to which you are redirecting users who are not currently logged in, hence my except attribute definition above.
My guess is you need to define the template for the '/main' route as well--that would be what is undefined in your error. If you have a template with the name="main" attribute, I would need to see more code, particularly any template handlers.
A redirect could be accomplished with the onBeforeAction callback:
onBeforeAction: function() {
if (!Meteor.userId()) {
this.redirect('/');
} else {
this.next();
}
}
Related
I have an iron route defined like this (pay attention to onAfterAction and initProfileComponent)
var initProfileComponent = function () {
var elements = document.querySelectorAll('.sliderComponent');
//... do stuff and decorate the elements
}
Router.route('newProfile', {
path: '/new-profile/:_id',
onAfterAction: function () {
initProfileComponent();
},
data: function () {
return {profile: Profile.findOne({_id: this.params._id})};
}
});
The function initProfileComponent wishes to find using document.querySelectorAll elements created by meteor/mustache publish mechanism, that is why is called in onAfterAction, but obviously it doesn't work, since, when it is called the elements are not yet rendered.
The templates are defined like this :
<template name="newProfile">
<div class="main-title new-profile">
new profile
</div>
{{#each profile.properties}}
{{> sliderComponent}}
{{/each}}
</template>
<template name="sliderComponent">
<div class="sliderComponent dragdealer">
<div class="handle">{{label}}</div>
</div>
</template>
Other than onAfterAction which does "buritos" for my use case, where/how can one define an callback that ensures that the template was fully rendered. Or, to what should I subscribe?
Ideally this should be done in your template. In Meteor all templates have onCreated and onRendered callbacks. If you use onRendered, you can be sure that all DOM elements are already there.
Template.sliderComponent.onRendered(function(){
var elements = $('.sliderComponent');
//... do stuff and decorate the elements
});
It is good practice to initialize your template like this. Use Iron Router to parameterize your templates, as you are already doing.
I want to insert a component into controller template without using the handlebars helper (component "component-name"... or component-name). Or through a controller in an outlet (or as long as the solution works for a component that wants to insert another component, then it's fine, I don't think outlets work in components).
In other words:
App.IndexController = Ember.Controller.extend({
actions: {
insertComponent: function() {
var component = this.container.lookup("component:my-inserted", { singleton: false });
component.set("layoutName", "components/my-inserted");
// to be like handlebars-inserted component, what do i do here?
}
}
});
You can use test with this: http://emberjs.jsbin.com/popozanare/4/edit?html,js,output
Why?
Thinking of a way of to have clean modal syntax, such as the "openModal" syntax described in the Ember Cookbook: http://guides.emberjs.com/v1.10.0/cookbook/user_interface_and_interaction/using_modal_dialogs/.
The problem is that the source context is lost, as the modal is within the ApplicationRoute. I want the same syntax when calling a modal, but keeping the hierarchy. You can keep the hierarchy using https://github.com/yapplabs/ember-modal-dialog, which requires a mapping of variables... which i don't like either (but will likely implement if I have no other choice).
TD;LR: Want to open modal within the controller/component (context) that called it without scaffolding in the controller/component that called it (mapping variables, etc).
Edit:
On second thought, using a container view might be cleaner than mapping variables, found in this solution: http://jsbin.com/hahohi/1/edit?html,js,output. Still needs scaffolding though. Thanks #user3568719.
That cookbook is a bit outdated, but if you are looking for a "clean" way to handling modals in your app I would suggest named outlets.
Add it to your application or auth template {{outlet "modal"}} and when you want to bring up the modal you can catch the action on the corresponding route and then render into that named outlet like so:
this.render('your-desired-modal-template', {
into: 'auth',
outlet: 'modal'
});
And when you want to dismiss it simply disconnectOutlet like so:
this.disconnectOutlet({
outlet: 'modal',
parentView: 'auth'
});
This is the way we've been going about it, I m open to suggestions/better methods.
How can I use the same template name multiple times across different files? I have the same template naming pattern for every page, and the problem is when for example <template name="defaultContent"></template> was already used on another page, it can't be used again.
example URLs:
/home
/home/first
/home/second
/home/third
homePage.html
/template: homePage
template: default
template: first
template: second
template: third
userPage.html
/template: userPage
template: default
template: first
template: second
template: third
Iron router code:
// mainpage
Router.route('/home', function () {
this.render('homePage');
this.render('default', {to: 'content'});
});
// subpages
Router.route('/home/:content', function () {
this.render('homePage');
var templateName = this.params.content;
if(Template[templateName]){
this.render(templateName, {to: 'content'});
};
});
[update] By the way, that's how Meteor kitchen solved this problem:
<template name="CoolPageSubPageBSubPageB1LoremIpsum">
You can't define multiple templates with the same name.
Meteor defines your templates in a global object, Template (for example, Template.homePage). An object can't have multiple times the same field, so defining multiple times a single template would lead to errors (possibly silent ones).
Plus, how could the router know which defaultContent you are talking about?
How could you?
Instead of shadowing templates, you could simply define multiple templates (Template.homeDefault, Template.userDefault) which would allow you to debug them and refer to them easily.
You need to wrap it in one parent template per page. You add then your template inside the parent template in its HTML using {{> defaultContent}} if the template you want to reuse is called "defaultContent".
I have a base template:
<template name="ApplicationLayout">
{{> yield}}
</template>
and I route templates into it using Iron Router, like so:
Router.configure({
layoutTemplate: 'ApplicationLayout',
notFoundTemplate: 'home'
});
Router.route('/', function () {
this.render('home');
});
Whenever someone arrives at the site, they'll always be shown the agegate template. Once they click the submit button, I want to change to a different page (home).
Template.agegate.events({
"click #submit": function (event) {
this.render('home');
}
});
The routing actually works (I get to the desired page), but it throws an error:
Uncaught TypeError: undefined is not a function
I'm assuming this is because the this in this.render('home') refers to the current template, whereas it needs to refer to the parent template (the ApplicationLayout template). However I don't know how to do that, or if that's even relevant.
So, how to can I correctly route inside a Template event function?
Try using Router.go('/'), this should suffice
I currently render my templates manually like so:
App.IndexRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('index');
this.render('nav', {
outlet: 'nav',
into : 'index'
});
this.render('welcome', {
outlet: 'welcome',
into : 'index'
});
}
});
Now this works fine, but its been brought to my attention that best ember practices avoid manually rendered templates.
So I can understand that my index template should and would be rendered automatically dude to Ember awesomeness. However, how do I control the rendering of my nav and welcome templates inside of my index template? Is that something I handle in the router, controller? Or should I do it with handlebar partials?
Thanks.
in the template where you have your outlets replace it with template renders
instead of
{{outlet nav}}
use
{{render 'nav'}}
and you can completely remove the render section for nav. After you've done the same for welcome you can delete the renderTemplate hook altogether.
Additionally, there is nothing wrong with using the renderTemplate hook, it's a very accepted practice, granted in your case unnecessary.
read more about the render helper here http://emberjs.com/guides/templates/rendering-with-helpers/#toc_the-code-render-code-helper