I'm working on a multi-tenant application and I'm unsure how to load routes conditionally.
I had:
var tenant = resolveTenant();
if (tenant === null) {
Router.configure({
layoutTemplate: 'main',
notFoundTemplate: 'not-found'
})
Router.route('home', {
path: '/'
})
Router.route('newClient', {
path: 'signup'
})
} else {
Router.configure({
layoutTemplate: 'storeMain',
notFoundTemplate: 'not-found'
})
Router.route('storeHome', {
path: '/'
})
}
So if a tenant couldn't be resolved, load the main site.
This worked just fine, not pretty, but fine. However the issue is when a tenant doesn't exist.
Case:
In order to do this I need to do a call to the database. So I wrapped the conditions inside a method call with a callback:
Router.configure({
layoutTemplate: "loading"
});
var tenant = resolveTenant();
Meteor.call("tenant.exists", tenant, function(err, exists) {
if (tenant !== null && !exists) {
Router.configure({
layoutTemplate: 'noTenant'
})
Router.route('noTenant', {
path: '/'
})
} else if (tenant === null) {
Router.configure({
layoutTemplate: 'main',
notFoundTemplate: 'not-found'
})
Router.route('home', {
path: '/'
})
Router.route('newClient', {
path: 'signup'
})
} else {
Router.configure({
layoutTemplate: 'storeMain',
notFoundTemplate: 'not-found'
})
Router.route('storeHome', {
path: '/'
})
}
})
The problem is, is it won't move from the loading template configuration after the call has completed. And if I remove the loading template route, I get the iron router landing page.
For completion, here is the "tenant.exists" method:
"tenant.exists": function(url){
if(url === null){
return false
}
return Tenants.find({"url": url}).count() > 0;
},
Any ideas on how to achieve this?
Edit
I did try to remove the conditions from the method call and running the query on the client-side. However the count always returned 0 when it shouldn't.
if (tenant !== null && Tenants.find({"url": tenant}).count() === 0) {
Router.configure({
layoutTemplate: 'noTenant'
})
Router.route('noTenant', {
path: '/'
})
} else if (tenant === null) {
Router.configure({
layoutTemplate: 'main',
notFoundTemplate: 'not-found'
})
Router.route('home', {
path: '/'
})
Router.route('newClient', {
path: 'signup'
})
} else {
Router.configure({
layoutTemplate: 'storeMain',
notFoundTemplate: 'not-found'
})
Router.route('storeHome', {
path: '/'
})
}
Please allow me to suggest a much simpler approach that also lets you avoid publishing the names of all you tenants to every client while also avoiding the asynchronous side-effects of Meteor.call()
:
onBeforeAction
handler, see if there's a tenant record at all and proceed if so, otherwise render the appropriate page.Router:
var tenant = resolveTenant();
Router.route('/',{
name: 'storeHome',
onBeforeAction: function(){
if ( Tenants.findOne() ) this.next();
else if ( tenant ) this.render('noTenant');
else this.render('signup');
},
waitOn: function(){
return Meteor.subscribe('tenant', tenant);
}
});
Server:
Meteor.publish('tenant',name,function(){
check(name,String);
return Tenants.find({ url: tenant });
});
Taken from patterns here.