Fullhdpixel
Fullhdpixel

Reputation: 813

Meteor: use 2 different layouts (iron:router)

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.

Upvotes: 3

Views: 2711

Answers (3)

Keith Dawson
Keith Dawson

Reputation: 1495

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.

Upvotes: 3

Ethaan
Ethaan

Reputation: 11376

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>

Upvotes: 4

pfkurtz
pfkurtz

Reputation: 514

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();
    }
}

Upvotes: 0

Related Questions