Meteor blaze, Use helper / variable in Content Blocks (contentFor) - meteor

I have a template (navBarContent) that I is passed a "title" when I insert it.
I am then able to access {{title}} within that template, but it is not possible to access it within a {{#contentFor}} block embedded in the navBarContent template:
<template name="map">
{{>navBarContent title="MAP"}}
... other content ...
<template>
<template name="navBarContent ">
{{title}}
{{#contentFor "headerTitle"}}
<h1 class="title">{{title}}</h1>
{{/contentFor}}
</template>
I already tried to "forward" the title:
<template name="navBarContent ">
{{title}}
{{#contentFor "headerTitle" title="MAP"}}
<h1 class="title">{{title}}</h1>
{{/contentFor}}
</template>
which produces the following error:
First argument must be a function, to be called on the rest of the arguments;
EDIT:
Ok. I think the data scopes are a the following:
<template name="layout">
{{> yield "headerTitleRenderedInLayout"}}
{{> yield}}
</template>
<template name='map'>
{{> yield "headerTitleRenderedInTemplate"}}
{{>navBarContent title="PARAMETER_TITLE"}}
</template>
<template name="navBarContent">
{{title}} <!-- output: PARAMETER_TITLE -->
{{#contentFor "headerTitleRenderedInLayout"}}
<h1 class="title">{{title}}</h1> <!-- output: LAYOUT_DATA_TITLE -->
{{/contentFor}}
{{#contentFor "headerTitleRenderedInTemplate"}}
<h1 class="title">{{title}}</h1> <!-- output: TEMPLATE_DATA_TITLE -->
{{/contentFor}}
</template>
Above outputs are produced when I use the following router options:
Router.route('/map', function () {
this.layout("layout", {
data: function() {
return { title: "LAYOUT_DATA_TITLE" }
}
});
this.render('map', {
data: function() {
return { title: "TEMPLATE_DATA_TITLE" }
}
});
});
My app has a navbar that is defined in my main layout and I therefore need to set the datacontext for the layout in my route. So far so good, but I want to set that data context based on the value that I pass via:
{{>navBarContent title="PARAMETER_TITLE"}}
This is just a cosmetic thing, because I prefer to define my navbar content in the different templates rather than the routes.

It works when you pass the data context to the render function because Iron Router handles the contentFor block:
Router.route('/', function() {
this.render('map', {
data: function() {
return { title: 'MAP' }
}
});
});
Then the following template shows MAP twice:
<template name="map">
{{> navBarContent }}
{{> yield 'anotherBlock'}}
</template>
<template name="navBarContent">
<div>
In "map": {{title}}
</div>
{{#contentFor 'anotherBlock'}}
<div>
In contentFor: {{title}}
</div>
{{/contentFor}}
</template>

Related

Flow Router Footer Flashes to Top Before Data Loads

I'm loading a grid of data with flow router, but when I view the page the footer always flashes to the top before the data loads. What is the best way to fix this?
Here is the route:
AdminRoutes.route('/dashboard', {
name: 'adminDashboard',
action() {
BlazeLayout.render('AppLayout', {page: 'AdminDashboard'});
}
});
Here is the js file:
import { Template } from 'meteor/templating'
import Stores from '../../../../api/stores/stores.js'
import './AdminDashboard.html'
Template.AdminDashboard.onCreated(function() {
var self = this;
self.autorun(function() {
self.subscribe('stores.names.links');
});
});
Template.AdminDashboard.helpers({
stores: function () {
return Stores.find();
}
});
Here is the html layout file:
<template name='AppLayout'>
{{#if Template.subscriptionsReady}}
{{> Header }}
{{> Template.dynamic template=page}}
{{> Footer }}
{{/if}}
</template>
Here is the dashboard html file:
<template name='AdminDashboard'>
<div class='admin-dashboard-page'>
<section class='stores-grid'>
{{#each stores}}
<div class='store'>
<h2 class='store-name'>{{name}}</h2>
<a href='/admin/dashboard/{{link}}' class='store-button'>Edit</a>
</div>
{{/each}}
</section>
</div>
</template>
I'd try displaying the footer AFTER .stores-grid loads. Create a reactiveVar with a handler that is set to true when your data is loaded and return it's value on a helper, then wrap the footer in an if block on your template.
It would be something like the following...
First create a reactiveVar with a value of false:
Template.AdminDashboard.onCreated(function() {
this.isDataLoaded = new ReactiveVar(false);
var self = this;
self.autorun(function() {
self.subscribe('stores.names.links');
});
});
Set the value to true when the collection is loaded:
Template.AdminDashboard.helpers({
stores: function () {
let data = Stores.find()
if(data) {
Template.Instance().isDataLoaded.set(true)
}
return data ;
},
dataLoaded: function () {
return Template.Instance().isDataLoaded.get();
}
});
Finally, wrap your footer so it's only displayed after data is loaded:
<template name='AppLayout'>
{{#if Template.subscriptionsReady}}
{{> Header }}
{{> Template.dynamic template=page}}
{{#if dataLoaded}}
{{> Footer }}
{{/if}}
{{/if}}
</template>
A simple fix would be to set a minimum height on the div your content will be loaded into, pushing the footer down while the content loads. This may or may not work for you, depending on what the expected height of your content will be.
You could also install a loading screen/animation to hide the footer while the data loads.

Meteor Pagination with alethes:pages

I'm using alethes:pagination but I don't understand how to make it work. I have 4 posts in my collection and I want to display 2 per page. Here's the code.
app.js
BlogPosts = new Mongo.Collection("blogPosts");
if (Meteor.isClient) {
Template.body.helpers({
blogPosts: function() {
return BlogPosts.find({});
}
});
}
Pages = new Meteor.Pagination(BlogPosts, {
itemTemplate: "post",
perPage: 2
});
app.html
<head>
<title>pagination</title>
</head>
<body>
{{> pages}}
{{> pagesNav}}
</body>
<template name="post">
{{title}}
</template>
You have to provide 2 templates name to the alethes backed collection.
One for the page itself, and another for the items of your paginated content.
In your JS
PaginatedStuff = new Meteor.Pagination(Polls, {
perPage:6,
templateName: 'paginatedStuff',
itemTemplate: 'stuffListItem'
});
Where you want to have your paginated content :
{{>paginatedStuff}}
And finally the two templates
<template name="paginatedStuff">
{{> pages}}
{{> pagesNav}}<!-- bottom navigation -->
</template>
<template name="stuffListItem">
<!-- stuff to display your item here -->
</template>

Template.dynamic is not passing data context

I have a list template (#each) in a package that I plan to use across many different collections. Since the template is in a package they are not easily customizable. So I figured this was a great example to use Template.dynamic. Everything works except passing data.
.. I pull the data into the routed page and manipulate the data to match the dynamic template.
Template.usersIndex.helpers({
items: function() {
var users = Meteor.users.find({}).fetch();
var items = users.filter(function(user) {
return user;
}).map(function(user){
return {
name: user.profile.name,
description: user.emails[0].address,
tidbit: "hello"
};
});
return items
}
});
... the data passes perfectly to the usersIndex template.
<template name="usersIndex">
<div id="gc-users-index-navbar">
<h2>Title</h2>
</div>
<div id="gc-users-index" class="inner-content">
{{> Template.dynamic template="strataIndexItem" data="items" }}
</div>
</template>
... But no dice, the dynamic template is rendered but no data.
<template name="themeIndex">
<div class="list-group">
{{#each items }}
<div class="list-group-item">
<div class="row-content">
<div class="least-content">{{tidbit}}</div>
<h4 class="list-group-item-heading">{{name}}</h4>
<p class="list-group-item-text">{{description}}</p>
</div>
</div>
<div class="list-group-separator"></div>
{{/each}}
</div>
</template>
You pass data as string?
{{> Template.dynamic template="strataIndexItem" data="items" }}
You should pass data as variable, without ""
{{> Template.dynamic template="strataIndexItem" data=items }}
Also check if your strataIndexItem template is named strataIndexItem:
<template name="strataIndexItem">
...
</template>

How to do data binding in polymer with meteor

I'm very new to data binding and the two frameworks. Right now I'm pretty stuck at how to bind the data within a polymer element.
For example, I have a book list with books' name. If I only use blaze to do the rendering, I would do it in the follow way:
//app.js
Template.bookList.helpers({
books: function () {
return Books.find({});
}
});
//app.html
<template name="bookList">
<h1>List</h1>
<ul>
{{#each books}}
{{> book}}
{{/each}}
</ul>
</template>
<template name="book">
<li>{{name}}</li>
</template>
Now I'm using it with polymer, I do:
//my-book-list.html
<polymer-element name="my-book-list">
<template>
<h1>List</h1>
<content></content>
</template>
</polymer-element>
//app.html
<template name="bookList">
<my-book-list>
<ul>
{{#each books}}
{{> book}}
{{/each}}
</ul>
</my-book-list>
</template>
<template name="book">
<li>{{name}}</li>
</template>
So I place the dynamic data inside of the polymer item through the content block. Although it still does the job, I don't want it that way. I want to do the data-binding inside the polymer element, something like(I hope it makes sense to you):
//my-book-list.html
<polymer-element name="my-book-list">
<template bind="{{books}}">
<h1>List</h1>
<ul>
<template repeat>
<li>{{name}}</li>
</template>
</ul>
</template>
</polymer-element>
//app.html
<template name="bookList">
<my-book-list></my-book-list>
</template>
Is there a way to do it? Thanks in advance.
Progress:
I now can put books purely inside the polymer element, the problem now is that it doesn't seem to react when the data change because polymer doesn't observe change of a object, and I am struggling in finding a way to observe all the nested values inside a object:
<polymer-element name="my-book-list">
<template bind="{{books | mapBooks}}">
<h1>List</h1>
<ul>
<template repeat>
<li>{{name}}</li>
</template>
</ul>
</template>
<script>
Polymer("my-book-list", {
books: Books.find(),
mapBooks : function(booksCursor) {
return booksCursor.map(function(p) { return {id: p.id, name: p.name}})
}
});
</script>
</polymer-element>
Finally got a hacky solution, but I don't know if this is the best practice or not:
<polymer-element name="my-book-list">
//Force polymer to update DOM when books.lastUpdate change
<template bind="{{books.lastUpdate | getBooks}}">
<h1>List</h1>
<ul>
<template repeat>
<li>{{name}}</li>
</template>
</ul>
</template>
<script>
Polymer("my-book-list", {
ready: function() {
var books = this.books;
this.books.observeChanges( //Observe the change of cursor and update a field
{
added: function(id, fields) {
console.log("Item added");
books.lastUpdate = new Date();
},
changed: function(id, fields) {
console.log("Item changed");
books.lastUpdate = new Date();
},
removed: function(id, fields) {
console.log("Item deleted");
books.lastUpdate = new Date();
}
}
),
books: Books.find(),
getBooks : function() {
return Books.find().map(function(p) { return {id: p.id, name: p.name}})
}
});
</script>
</polymer-element>

Dynamic templates and routing in Meteor.js

I am developing a meteor.js application. I have two main templates/pages, home.html and groups.html. I render these templates according to the selection of main menu. In home template, I just show basic informations about the website. But in groups template, I have side menu which has timeline, chat, group blog options, and according to the selection of side menu, I render timeline or chat or groupblog templates inside groups template. I succeeded this with two different methods.
Can you please tell me if any of them is correct approach, and if they both are, which one is better to use?
In the first approach, is there any way to wait to render sub-template until data is ready, like waitOn property in iron-router?
First approach: I am using dynamic template, and rendering and setting data contex of sub-templates by using Session variable.
layout.html
<template name="layout">
<div>
{{> header}}
<div id="main" class="container">
{{> yield}}
</div>
</div>
</template>
groups.html
<template name="groups">
<div class="row">
<div class="row-fluid">
<div class="col-md-9 col-lg-9">
{{> dynamictemplate}}
</div>
</div>
</div>
</template>
dynamictemplate.html
<template name="dynamictemplate">
{{#DynamicTemplate template=getTemplate data=getData}}
Default content when there is no template.
{{/DynamicTemplate}}
</template>
dynamictemplate.js
Template.dynamictemplate.helpers({
getData: function () {
var tempname="timeline";
if(Session.get("menuitemselected")!=null){
tempname =Session.get("menuitemselected");
}
if(tempname=="timeline"){
return {test:123};
}else if(tempname=="calendar"){
return {groupId:this._id};
}
},
getTemplate: function () {
var tempname="timeline";
if(Session.get("menuitemselected")!=null){
tempname =Session.get("menuitemselected");
}
return tempname;
}
});
Second approach, I am defining new routes for all sub-templates in routes, and I am using yield with region property in home template.(I do not know if i am supposed to use yield in any place other than layout.html)
layout.html
<template name="layout">
<div>
{{> header}}
<div id="main" class="container">
{{> yield}}
</div>
</div>
</template>
groups.html
<template name="groups">
<div>
<div class="col-md-3">
{{> sidemenu}}
</div>
<div id="main" class="col-md-9">
{{> yield region="detailrender"}}
</div>
</div>
</template>
router.js
.....
this.route('groupdetail', {
path: '/groupsmain/:_id',
layoutTemplate: 'layout',
yieldTemplates: {
'timeline': {to: 'detailrender'}
},
data: function() { return Groups.findOne(this.params._id);
}
});
this.route('groupdetailcalendar', {
path: '/groupsmain/:_id/calendar',
layoutTemplate: 'layout',
template: 'groupdetail',
yieldTemplates: {
'calendar': {to: 'detailrender'}
},
data: function() { return Groups.findOne(this.params._id);
.....
});

Resources