Flow-router and rendering other template when user is signed into the application

FlowRouter.route('/', {
  action: function(params) {
    if (!Meteor.user()) {
      console.log("User is logged out."); // User is logged out.
      console.log(Meteor.user()); // undefined
      FlowLayout.render("welcome", { content: 'home' });
    } else {
      // It never comes down this path.
      FlowLayout.render("dashboard", { content: 'noteList' });
    }
  }
});

It’s a pretty simple use case. I want to render one template or the other depending on whether or not the user is signed in.

However it always goes down the anonymous user path despite being logged in properly.

Try checking for “Meteor.userId()” instead of “Meteor.user()”. That works for me. I think Meteor.user() only has a value once the user data has been loaded from the server and Meteor.userId() always has a value if the user is authenticated because it’s stored locally (just my guess, not sure that’s how it actually works). You could also wrap it in a Tacker.autorun so that it switches automatically when the user logs in/out.

2 Likes

@coniel Awesome, so I changed it to use Meteor.userId() and it’s rendering the correct layout and template!

It’s not reactive though, meaning if I log in it does not render the dashboard#notelist layout/template. Any ideas how I can achieve this? I’m fairly new to Meteor and especially new to Flow-Router.

How can I make it switch layouts/template combo automatically if the user is signed in or not?

Yeah, FlowRouter is not reactive but you can easily achieve it like so:

FlowRouter.route('/', {
    action: function(params) {
        Tracker.autorun(function() {
            if (!Meteor.userId()) {
              FlowLayout.render("welcome", { content: 'home' });
            } else {
              FlowLayout.render("dashboard", { content: 'noteList' });
            }
        });
      }
});
3 Likes

Oh my god it’s that easy?!

:heart_eyes: Meteor you’re spoiling me…

Thanks so much for your help man, I appreciate it a lot.

No problem, glad I could help!

As soon as you need to protect other routes, you will end up making all your routes reactive using Tracker.autorun but the whole point of flow router is being non-reactive and predictable.

Instead, handle logged in/out template switching within your layout.

<template name="mainLayout">
  {{#if currentUser}}
      {{> Template.dynamic template=main}}
  {{else}}
    {{#if loggingIn}}
        {{> loading}}
    {{else}}
      {{> loginForm}}
    {{/if}}
  {{/if}}
</template>
5 Likes

Fully agree with this post - don’t try to make Flow Router work like Iron Router, it’s non-reactive for a reason.

If you need to have a Tracker to manage user state/redirect when they log out etc, it should be app wide to prevent repeated code in every action.

2 Likes

Good point. I use FlowRouter’s groups and triggers to achieve something similar:

MainController = FlowRouter.group();

PublicController = MainController.group();

AppController = MainController.group({
    triggersEnter: [function(context, redirect) {
        if (!Meteor.userId()) {
            // Used to come back to the requested route after successful login
            Session.set("loginRedirectContext", context);
            redirect('/login');
        }
    }]
});

AppController.route('/some-protected-route', {...});

Basically all of the routes which require login are part of the AppController group, so it doesn’t require any extra work. PublicController is used for routes which don’t require authentication. They are both “children” of MainController, in case I was to add some global functionality.

Although I just realised that with this approach, if the user has two tabs open and logs out in one of them, they will not be redirected to/shown the login view, instead a messed up version of the current view.

Yeah, I looked at using Triggers to manage login state, and as they only run on the way in and non-reactive, they can’t be used to manage redirects if you’re already viewing a route.

1 Like

Well, the gist is, if a functionality requires reactivity, it does not belong in the router :slight_smile:

1 Like

I wanted to thank you guys for your input. I refactored my layout and router files and they are much easier to reason about with no reactivity in them. They also work exactly how you’d expect! No more Tracker.run etc.

Thanks again!

2 Likes

Hi @serkandurusoy,

What if I need to render different LAYOUT for logged in/out user?

The reason is, non-login user LAYOUT do not have Side Menu/Drawer and logged-in user LAYOUT have Side Menu/Drawer.

Within the LAYOUT, I need multiple Dynamic Template zone.

What will be the solution for this case? Please advice. Thank you.

Regards

a layout is in essence a template and you can compose them however you like
by checking within the templates the user existence and deciding what to
show

@serkandurusoy,

I’m very new to Meteor/Blaze and etc. As follow:

After read Flow Router and Blaze Layout documentation, I have no idea how to handle this situation since Flow Router do not recommend to check user status in Flow Router ACTION.

{{#if statement}}
  {{> Layout1}}
{{else}}
  {{> Layout2}}
{{/if}}

If I put the above into my entry point HTML, where should I render the LAYOUT and TEMPLATE?

BlazeLayout.render('Layout1', { top: "Template1", main: "Component1" });

or

BlazeLayout.render('Layout2', { top: "Template2", main: "Component2" });

In that case, what is the proper way to achieve what I need? Please advice, thank you.

You should do the checking in the template itself, not the router action.

Please read this
https://kadira.io/academy/meteor-routing-guide/content/implementing-auth-logic-and-permissions

@serkandurusoy,

Ya I have read this before posting here and I do understand that the checking should be done in template level. As my example code in my previous post, I show example to check the true/false to decide which LAYOUT to render.

But what I don’t understand about the concept is, I know I need to call BlazeLayout.render to place the template into the Dynamic area.

So this part is what I don’t understand, I try to call BlazeLayout.render from Template.Layout1.onCreated() but I get a bunch of error message at console.

The error message in console:

Exception in defer callback: RangeError: Maximum call stack size exceeded
    at Object.Blaze._withCurrentView (http://localhost:3000/packages/blaze.js?hash=ef41aed769a8945fc99ac4954e8c9ec157a88cea:2214:12)
    at Object.Blaze._fireCallbacks (http://localhost:3000/packages/blaze.js?hash=ef41aed769a8945fc99ac4954e8c9ec157a88cea:1951:9)
    at Object.Blaze._createView (http://localhost:3000/packages/blaze.js?hash=ef41aed769a8945fc99ac4954e8c9ec157a88cea:1969:9)
    at Object.Blaze._materializeView (http://localhost:3000/packages/blaze.js?hash=ef41aed769a8945fc99ac4954e8c9ec157a88cea:2014:9)
    at Object.Blaze.render (http://localhost:3000/packages/blaze.js?hash=ef41aed769a8945fc99ac4954e8c9ec157a88cea:2313:9)
    at Object._render (http://localhost:3000/packages/kadira_blaze-layout.js?hash=493d22b2043f66d6b3694baa790b62a701aaec33:204:11)
    at http://localhost:3000/packages/kadira_blaze-layout.js?hash=493d22b2043f66d6b3694baa790b62a701aaec33:80:19
    at Object.Meteor.startup (http://localhost:3000/packages/meteor.js?hash=ae8b8affa9680bf9720bd8f7fa112f13a62f71c3:860:7)
    at Object.render (http://localhost:3000/packages/kadira_blaze-layout.js?hash=493d22b2043f66d6b3694baa790b62a701aaec33:71:10)
    at .<anonymous> (http://localhost:3000/app/app.js?hash=561a67a48dcc27dba8f5b17e193c782e657989af:80:15)

Some advice please. What I did wrong here?

Ok, it seems from the code that you are rendering Layout1 and asking the router to render Layout1 into Layout1 which creates a recursion hence the callstack error.

create two different layouts, have your router render the appropriate layout based on “route” information and have your layouts render content or warning based on the “user” information

@serkandurusoy,

Thanks for your help again, I understand your first paragraph about the error. But I don’t really understand your second paragraph.

Do you mind do give some simple example code? Sorry about my level of understanding. Too new to Meteor concept.

Thanks in advance.

oh no problem and I’d like to help but I need to run off now I’m terribly
sorry :frowning: But I am sure other helpful people will pick up from where I left