I'm really struggling to understand what's going on here. I have a template
<template name="gallery">
<div>
<a href="/">1</a><a href="/gallery/2">2</a><a href="/gallery/3">3</a>
</div>
{{#each art}}
{{name}}
{{/each}}
</template>
Then I have routes like this
Router.map(function() {
this.route('/', {
template: 'gallery',
data: function() {
var page = 1;
return {
page: page,
};
},
});
this.route('/gallery/:_page', {
template: 'gallery',
data: function() {
var page = parseInt(this.params._page);
return {
page: page,
};
},
});
});
Following this article I'm using template subscriptions like this
var G_PAGE_SIZE = 10;
Template.gallery.onCreated(function() {
var instance = this;
var route = Router.current();
instance.loaded = new ReactiveVar(0);
instance.autorun(function() {
var pageId = parseInt(route.params._page);
var page = pageId - 1;
var skip = page * G_PAGE_SIZE;
var subscription = instance.subscribe('artForGrid', skip, G_PAGE_SIZE);
if (subscription.ready()) {
instance.loaded.set(1);
}
});
instance.art = function() {
return Art.find({});
};
});
Template.gallery.helpers({
art: function() {
return Template.instance().art();
},
});
It works but when I click one of the links changing the route the page doesn't re-render
So ... some other answer on here said that's because there's no reactive connection between data changing on the route through the router and the template.
So I tried use route.state
which as a ReactiveDict (not sure where that came from). If it's part of iron:router or if it's part of reactive-var
Anyway I changed the code to
Router.map(function() {
this.route('/', {
template: 'gallery',
data: function() {
var page = 1;
this.state.set('page', page); // <------------ADDED
return {
page: page,
};
},
});
this.route('/gallery/:_page', {
template: 'gallery',
data: function() {
var page = parseInt(this.params._page);
this.state.set('page', page); // <------------ADDED
return {
page: page,
};
},
});
});
In the onCreated
Template.gallery.onCreated(function() {
var instance = this;
var route = Router.current();
instance.loaded = new ReactiveVar(0);
instance.autorun(function() {
var pageId = route.state.get('page'); // <--------- CHANGED
var page = pageId - 1;
var skip = page * G_PAGE_SIZE;
var subscription = instance.subscribe('artForGrid', skip, G_PAGE_SIZE);
if (subscription.ready()) {
instance.loaded.set(1);
}
});
instance.art = function() {
return Art.find({});
};
});
Template.gallery.helpers({
art: function() {
return Template.instance().art();
},
});
That doesn't work either except sometimes. In the interest of debugging I added a console.log
to the route
Router.map(function() {
this.route('/', {
template: 'gallery',
data: function() {
var page = 1;
this.state.set('page', page);
console.log("/root"); // <------------ADDED
return {
page: page,
};
},
});
this.route('/gallery/:_page', {
template: 'gallery',
data: function() {
var page = parseInt(this.params._page);
this.state.set('page', page);
console.log("/gallery/" + page); // <------------ADDED
return {
page: page,
};
},
});
});
Suddenly it starts working !???!@!? I remove the console.logs and it stops
I also tried adding actions without the console.logs
Router.map(function() {
this.route('/', {
template: 'gallery',
data: function() {
var page = 1;
this.state.set('page', page);
return {
page: page,
};
},
action: function() { // <- added
var page = 1; // <- added
this.state.set('page', page); // <- added
this.render(); // <- added
}, // <- added
});
this.route('/gallery/:_page', {
template: 'gallery',
data: function() {
var page = parseInt(this.params._page);
this.state.set('page', page);
return {
page: page,
};
},
action: function() { // <- added
var page = parseInt(this.params._page); // <- added
this.state.set('page', page); // <- added
this.render(); // <- added
}, // <- added
});
});
That doesn't work either.
So using Router.current().data().???
seems to make it work. It's now this
Router.map(function() {
this.route('/', {
template: 'gallery',
data: function() {
var page = 1;
// <--- removed this.state() stuff
return {
page: page,
};
},
// <---- removed action stuff
});
this.route('/gallery/:_page', {
template: 'gallery',
data: function() {
var page = parseInt(this.params._page);
// <--- removed this.state() stuff
return {
page: page,
};
},
// <---- removed action stuff
});
});
helper
Template.gallery.onCreated(function() {
var instance = this;
instance.loaded = new ReactiveVar(0);
instance.autorun(function() {
var route = Router.current(); // <----- Moved from 3 lines up
var pageId = route.data().page; // <--------- CHANGED
var page = pageId - 1;
var skip = page * G_PAGE_SIZE;
var subscription = instance.subscribe('artForGrid', skip, G_PAGE_SIZE);
if (subscription.ready()) {
instance.loaded.set(1);
}
});
instance.art = function() {
return Art.find({});
};
});
Template.gallery.helpers({
art: function() {
return Template.instance().art();
},
});
No idea if this is now correct or if I'm just getting lucky like with the console.log editions
My guess is that your autorun
function is not being re-run because you are not using any reactive variables. I'm not sure if Router.current()
or Router.current().data()
are reactive, but if they're not, then this explains the issue clearly for me. To test this, try putting some console logging in your autorun
function.
Now, I would proposed some revisions that may work for you.
Routing code
Redirect to '/'
URL to '/gallery/1'
so you can re-use routing code
Router.route('/', function(){
this.redirect('/gallery/1');
});
Sending the page number in the data object to your template is important so that we can use this object in our autorun
function
Router.route('/gallery/:_page', {
template: 'gallery',
data: function() {
return {
page: this.params._page,
};
}
});
Also, you don't need to use parseInt
method unless you are trying to re-route or handle that exception in your routing code.
Gallery onCreated
and helpers
There is a Template.subscriptionsReady
helper provided by Meteor so you don't have to manually check if the subscription is loaded unless you really want it. Here's the Template.subscriptionsReady documentation
Here's a simplified version of your onCreated
method using the template.data
object which was built by the routing code above. Using the autorun
wrapper is important so that it will re-run when template.data
is changed by your router.
var G_PAGE_SIZE = 10;
Template.gallery.onCreated(function() {
var instance = this;
instance.autorun(function() {
// access the data object built by your route code
var pageId = instance.data.page;
var page = pageId - 1;
var skip = page * G_PAGE_SIZE;
instance.subscribe('artForGrid', skip, G_PAGE_SIZE);
});
});
Define access to your reactive data sources using the template.helpers
method.
Template.gallery.helpers({
art: function() {
return Art.find({});
}
});
Note: Not necessary to define your helper in the onCreated
and template.helpers