How to clear all yields when route is changed in iron-router - meteor

Iron router allows to render different content into layout template, or load smaller templates into yields, but when user goes to different path, a content from previous yields does not unload templates inside them, until you force router to render null as a template into yields.
I have written a simple loop, which unload all yields before rendering new view, but i think there should be easier solution.
Router.route('/docs/container/', function () {
// render base layout for view
this.render('view');
// clear all parts
var yields = [
'viewSideLeft',
'viewSideRight',
'viewFooter',
'viewTopbar',
'pageContent',
'pageSideLeft',
'pageSideRight',
'pageFooter',
'pageTopbar'
];
for (var i = 0; i < yields.length; i++) {
this.render(null, {to: yields[i]});
}
// and render only this parts which you need:
this.render('viewDocsSideLeft', {to: 'viewSideLeft'});
this.render('viewDocsSideRight', {to: 'viewSideRight'});
this.render('viewDocsFooter', {to: 'viewFooter'});
this.render('viewDocsTopbar', {to: 'viewTopbar'});
this.render('pageDocsContainer', {to: 'pageContent'});
// this.render('pageDocsContainerSideLeft', {to: 'pageSideLeft'});
// this.render('pageDocsContainerSideRight', {to: 'pageSideRight'});
// this.render('pageDocsContainerFooter', {to: 'pageFooter'});
// this.render('pageDocsContainerTopbar', {to: 'pageTopbar'});
});
Here is a github issue related to this problem and it seems it has not been solved yet. https://github.com/iron-meteor/iron-router/issues/174

Related

Meteor Iron-Router: Wait for Subscription sequential

I have the following route defined in my iron-router:
this.route("/example/:id", {
name: "example",
template: "example",
action: function () {
this.wait(Meteor.subscribe('sub1', this.params.id));
this.wait(Meteor.subscribe('sub2', <<data of sub1 needed here>>));
if (this.ready()) {
this.render();
} else {
this.render('Loading');
}
}
});
I want to wait for sub1 and sub2 before rendering my actual template. The problem is that I need a piece of data which is part of the result of sub1 for the sub2 subscription.
How can I wait sequential for subscriptions? So that I can split the wait in two steps and wait for my first subscription to be finished. Then start the second subscription and then set this.ready() to render the template?
A workaround that I thought of was to use Reactive-Vars for the subscriptions and dont use .wait and .ready which is provided by iron-router. But I would like to use a more convenient solution provided by iron-router or Meteor itself. Do you know a better solution for this?
Thanks for your answers!
Publish Composite Package:
If the second subscription is reactively dependent on certain fields from the first dataset -- and if there will be a many-to-many "join" association, it might be worth looking into reywood:publish-composite package:
It provides a clean and easy way to manage associated subscriptions for collections with hierarchical relations.
Publication:
Meteor.publishComposite('compositeSub', function(id) {
return {
find: function() {
// return all documents from FirstCollection filtered by 'this.params.id' passed to subscription
return FirstCollection.find({ _id: id });
},
children: [
find: function(item) {
// return data from second collection filtered by using reference of each item's _id from results of first subscription
// you can also use any other field from 'item' as reference here, as per your database relations
return SecondCollection.find({ itemId: item._id });
}
]
}
});
Subscription:
Then you can just subscribe in the router using:
Meteor.subscribe('compositeSub', this.params.id);
Router hooks:
As a suggestion, hooks in iron-router are really useful, as they take care of a lot of things for you. So why not use the waitOn hook that manages this.wait and loading states neatly?
this.route('/example/:id', {
name: "example",
template: "example",
// this template will be rendered until the subscriptions are ready
loadingTemplate: 'loading',
waitOn: function () {
// return one handle, a function, or an array
return Meteor.subscribe('compositeSub', this.params.id);
// FYI, this can also return an array of subscriptions
},
action: function () {
this.render();
}
});
You can use the configure option to add a template for loading event:
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading'
});
Note regarding the comment in question:
If both subscriptions only depend on the same id parameter passed to it, you can use the following, as mentioned by #abj27 in the comment above -- however, this does not seem to be the case, going by your example:
Publication:
Meteor.publish("subOneAndTwo", function (exampleId) {
check(exampleId, String);
return [
FirstCollection.find({ _id: exampleId });
SecondCollection.find({ firstId: exampleId })
];
});
Subscription:
Meteor.subscribe('subOneAndTwo', this.params.id);
So just check what you need and use a solution accordingly.
https://github.com/kadirahq/subs-manager
With this package, you can assign a subscription to a variable. Then, you can check that variable's ready state. I just got this working... after years of trying to understand.
Here is my code snippet that works, but oddly I had to wrap it in a 1ms timeout to work...
```
Router.route('/activity/:activityId', function (params) {
var params = this.params;
setTimeout(function(){
var thePage = window.location.href.split("/");;
window.thePage = thePage[4];
dbSubscriptionActivites.clear();
window.thisDbSubscriptionActivites = "";
window.thisDbSubscriptionActivites = dbSubscriptionActivites.subscribe("activityByPage", window.thePage);
Tracker.autorun(function() {
if(window.thisDbSubscriptionActivites.ready()) {
dbSubscriptionComments.clear();
window.thisDbSubscriptionComments = "";
window.thisDbSubscriptionComments = dbSubscriptionComments.subscribe('fetchComments', "activity", Activities.findOne({})._id);
BlazeLayout.render("activity");
$('body').removeClass("shards-app-promo-page--1");
}
});
},1); // Must wait for DOM?
});
```
Examine: window.thisDbSubscriptionActivites = dbSubscriptionActivites.subscribe("activityByPage", window.thePage);
I'm setting as a window variable, but you could do a const mySub = ...
Then, you check that in the autorun function later.
You can see there is where I am doing subscriptions.
I suppose I really should move the BlazeLayout render in to another .ready() check for the comments.

Meteor Iron Router. One Route, Multiple Templates. How?

I'm using the latest Meteor and Iron Router. Imagine have a 'customComputer' collection. Computers have three different states: 'ordering', 'building', 'shipped'.
Right now, I'm using three different routes for this, each with a different template
/o/_id
/b/_id
/s/_id
A computer can't be in 2 states at once, so I'd like to have one route. How do I wrangle the templates?
/c/_id
The best I can come up with is to make a "main" template that links to the others. Is this a best practice?
{{#if isOrder}}{{>orderTemplate}}{{/if}}
{{#if isBuilding}}{{>buildingTemplate}}{{/if}}
{{#if isShipped}}{{>shippedTemplate}}{{/if}}
Or dynamic templates
Here's the route:
Router.route('order', {
path: '/o/:b/:computerId',
onAfterAction: function() {
if (this.title) document.title = this.title;
},
data: function() {
if(!this.ready()) return;
var o = Computer.findOne(this.params.computerId);
if(!o) throw new Meteor.Error('Computer not found');
return o;
},
waitOn: function() {
if (!Meteor.userId()) return this.next(); // Don't subscribe if not logged in.
return [
Meteor.subscribe('computerById', this.params.computerId),
Meteor.subscribe('myProfile'),
];
},
});
Is there a better way?
I'd do your main template idea, or the dynamic template.
dynamic template tends to be better when you have quite a few options that can be dynamically configured.
But the main template I think ends up being more obvious when you only have a couple of choices.
Either way can be converted easily to the other if you think you need the other option.

Iron-router set notFoundTemplate when no data found in database

How can I set a layout when my data function returns null.
For example, in the route below, when chefs is null, I would like to render my 'notFound' template.
Router.route('/vendors/chefs/:_url', {
template: 'chefs',
data: function() {
var chefs = Chef_db.findOne({url: this.params._url});
return chefs;
}
});
Take a look at the notFoundTemplate in the documentation: https://github.com/iron-meteor/iron-router/blob/devel/Guide.md
You can apply it globally:
Router.plugin('dataNotFound', {notFoundTemplate: 'notFound'});
Or you can apply it to specific routes using the except/only options:
Router.plugin('dataNotFound', {
notFoundTemplate: 'NotFound',
except: ['server.route']
// or only: ['routeOne', 'routeTwo']
});
There's a built-in plugin for that. It's called dataNotFound. It's mentioned in the iron:router guide.

Set a reactive layout in Meteor with Iron Router

Is there a way to set a reactive layout in meteor with iron router?
For example:
Router.configure({
loadingTemplate: 'loading',
layoutTemplate: Session.get('fullscreen') ? 'layoutFull' : 'layout'
});
Then a link in both layouts with:
Toggle Fullscreen
And then in both layouts something like this:
Template.layoutFull.events({
'click [data-action=toggleFullscreen]': function() {
Session.set('fullscreen', !Session.get('fullscreen'));
}
});
Template.layout.events({
'click [data-action=toggleFullscreen]': function() {
Session.set('fullscreen', !Session.get('fullscreen'));
}
});
The issue I'm running into is Router.configure isn't setting layoutTemplate reactively. Is there a way to do this so it affects all routes?
It has to be a function to be reactive:
layoutTemplate: function(){
return Session.get('fullscreen') ? 'layoutFull' : 'layout'
}
Also, why two functions?
You need just one since this is the negation of clicked Session

Add multiple Templates in one Yield

I want to add multiple Bootstrap Modals in one Yield.
Router.route('test', {
yieldTemplates: {
'contentTemplate': {to: 'content'},
'modal1Template': {to: 'modal'},
'modal2Template': {to: 'modal'}
}
});
This code adds only the last template to the yield -modal-. Is it possible to add multiple templates to one yield or do anybody know a best practice for this situation?

Resources