I'm using Flow Router and Blaze Renderer for a simple website (think blog / brochureware).
I'm using FlowRouter.path() to create links on my menu elements. The url changes as expected when these links are clicked and the action() method on the route is fired. However the templates don't seem to be refreshed and the template helpers aren't fired.
The route in my /lib/route.js file is
const about = FlowRouter.group({
prefix: '/about'
});
about.route('/:pageSlug/:subSectionSlug', {
action: (params) => {
console.log('action() fired :' + params.pageSlug + ' :' + params.subSectionSlug);
BlazeLayout.render( 'applicationLayout', {
main: 'basicPage',
});
},
name: 'pageSubsection',
});
Then my templates looks like -
<template name="applicationLayout">
{{> Template.dynamic template=main}}
</template>
<template name="basicPage">
<div id="pageWrapper">
...
<aside class="leftBar subSectionMenu">
{{> sidebar }}
</aside>
...
</div>
</template>
<template name="sidebar">
<ul class="pageMenu">
{{#each subSections }}
{{> sidebarItem}}
{{/each}}
</ul>
</template>
<template name="sidebarItem">
<a class="sidebarItemAnchor" href="{{ href }}">
<li class="sidebarItem .hoverItem {{#if isSelected }} selected {{/if}}">
{{title}}
<span class="sidebarArrow"></span>
</li>
</a>
</template>
With a simple helper to add the selected class to the li element -
Template.sidebarItem.helpers({
href() {
const subSection = this;
const params = {
pageSlug: subSection.pageSlug,
subSectionSlug: subSection.slug,
}
return FlowRouter.path('pageSubsection', params, {});
},
isSelected() {
const slug = FlowRouter.current().params.subSectionSlug;
console.log('running isSelected with ' + slug);
if (this.slug === slug) {
return true;
} else {
return false;
}
}
});
I think I must be misunderstanding how (and when) templates are rendered.
What do I need to do to re-render these templates when the route changes?
Flow Router was designed to work like this. It doesn't automatically re-render.
A simple fix is to add FlowRouter.watchPathChange(); into all Template helpers that depend on the params of a route.
So in this case update the sidebar.js -
Template.sidebarItem.helpers({
isSelected() {
FlowRouter.watchPathChange();
const slug = FlowRouter.current().params.subSectionSlug;
if (this.slug === slug) {
return true;
} else {
return false;
}
},
});
When that helper is used in the sidebarItem template it is now updated whenever the path changes.
Related
I would like to use the name of a meteor template from inside:
<template name="blaModal">
<div class="modal fade" id="{{templateName}}">
</div>
</template>
How can I do this?
You can create a global helper, that resolves the current Template's instance and returns it's view-name (with Template. prefix removed):
/imports/startup/client/helpers.js
Template.registerHelper('templateName', function () {
const instance = Template.instance()
const { view } = instance
const { name } = view
return name.replace('Template.', '')
})
I'm trying to display profile images in a list of users. The list populates all names correctly however I'm having trouble displaying the profile images. The profile images have a separate collection and they're being stored using slingshot S3. The collection is publishing correctly because I can see all the data using meteortoys:allthings. I assume it my js file or how I'm trying to access them in the template. Let me know if you need more info.
Path: userList.js
Template.userList.helpers({
userList: ()=> {
return Meteor.users.find({_id: { $ne: Meteor.userId() }});
},
profileImg: function(){
return Files.find({userId: this._id});
}
});
Path: userList.html
<template name="userList">
{{#each userList}}
{{#if profileImg url}}
<img src="{{url}}" alt="{{url}}" class="profileImg">
{{/if}}
{{profile.firstName}} {{profile.familyName}}
{{/each}}
</template>
userList.js
Template.userList.helpers({
userList() {
return Meteor.users.find({ _id: { $ne: Meteor.userId() } });
},
profileImg() {
return Files.findOne({ userId: this._id });
},
});
userList.html
<template name="userList">
{{#each userList}}
{{#with profileImg}}
<img src="{{url}}" alt="{{url}}" class="profileImg">
{{/with}}
{{profile.firstName}} {{profile.familyName}}
{{/each}}
</template>
The with will change the context for the img such that it actually has a url property. Returning the result of findOne in profileImg is also necessary here.
In meteors guide i found the code below and I was wondering if todo.tags could be sorted somehow, maybe by a helpers method?
{{#each todo in todos}}
{{#each tag in todo.tags}}
<!-- in here, both todo and tag are in scope -->
{{/each}}
{{/each}}
One option would be to create a separate template helper which sorts all elements in your tags array. To sort the elements in an ascending order, you could use _.sortBy(list, iteratee, [context]), for example:
if (Meteor.isClient) {
Template.todos.helpers({
todos: function() {
return Todos.find();
},
sortedTags: function(todo) {
return _.sortBy(todo.tags, function(tag) {
return tag;
});
}
});
}
if (Meteor.isServer) {
Meteor.startup(function() {
if (Todos.find().count() === 0) {
Todos.insert({
name: "homework",
tags: ["school", "college", "university"]
});
}
});
}
<template name="todos">
{{#each todo in todos}}
{{todo.name}}
<ul>
{{#each tag in sortedTags todo}}
<li>{{tag}}</li>
{{/each}}
</ul>
{{/each}}
</template>
This can also be implemented with your sample data structure, provided in the comments:
{
"gtext":"Money",
"owner":"qDqGDaXjaHXNhX95u",
"username":"prsz",
"order":0,
"tasks":[
{
"taskName":"Test subtask1",
"taskOrder":3
},
{
"taskName":"Test subtask2",
"taskOrder":1
}
]
}
<template name="goals">
{{#each goal in goals}}
{{goal.gtext}}
<ul>
{{#each task in sortedTasks goal}}
<li>{{task.taskName}}</li>
{{/each}}
</ul>
{{/each}}
</template>
if (Meteor.isClient) {
Template.goals.helpers({
goals: function() {
return Goals.find();
},
sortedTasks: function(goal) {
return _.sortBy(goal.tasks, function(task) {
return task.taskOrder;
});
}
});
}
Here's a MeteorPad.
Is there a way in Meteor to stop rendering template in some case? I found that this works:
Template.test.created = function () {
if (/* conditions */) {
this.view.template.renderFunction = function () {
return null;
};
}
};
But... maybe anyone can do this better?
It would be better to accomplish this within the template/helpers itself.
<template name="sometimesVisible">
{{#if visible}}
<!-- content here -->
{{/if}}
</template>
Template.sometimesVisible.helpers({
visible: function() {
// conditions here
}
]);
I would like to define a default error page in meteor. That is if application is crashing or other error occurs the user should be redirected to a "friendly" page that says something like : system is unavailable , please contact etc etc.
Is there any way to accomplish this or something similar ?
Thank you
You have to use BackboneJS(Backbone Router) for routing. With this code the session variable 'page_type' let's you know if you are on a wrong url.
var BackboneRouter = Backbone.Router.extend({
routes: {
"/": "default",
":error": "list"
},
default: function () {
Session.set("page_type", "default");
},
error: function () {
Session.set("page_type", "error");
}
});
Router = new BackboneRouter;
Meteor.startup(function () {
Backbone.history.start({pushState: true});
});
Now you can use the 'page_type' to tell the template engine which template to load.
Template.tmp.route = function () {
if (Session.get("page_type") == "default") {
return true;
} else {
return false;
}
<template name="tmp">
{{#if route}}
{{> default}}
{{else}}
{{> error}}
{{/if}}
</template>