Meteor data-context with iron-router - meteor

I am new to Meteor and I'm trying to set the data context in a page that displays one passage. I need to access the data in passage_item.js Template.passageItem.rendered but no context is set at that point. I think I need something like {{#with passage}} but "passage" does not exist in one_passage.html.
Here are some code snippets. Thanks.
router.js
Router.map(function() {
this.route('passagesList', {path: '/'});
this.route('onePassage', {
path: '/passages/:_id',
data: function() { return Passages.findOne(this.params._id); }
});
});
one_passage.html
<template name="onePassage">
{{> passageItem}}
</template>
passage-item.html
<template name="passageItem">
<div class="passage">
<div class="one-passage">
<h4>{{title}}</h4>
<div class="passage-content">
{{content}}
</div>
</div>
</div>
passage_item.js
Template.passageItem.helpers({
});
Template.passageItem.rendered = function() {
Meteor.defer(function() {
$('.passage-content').lettering('words');
//I want to be able to access the data object here. I have a list of words that are highlighted
});
};

Collection
Assuming you created your Passages collection like this and you've got autopublish turned on (which it is by default):
Passages = new Meteor.Collection('passages');
Router Map
And you mapped your router like this:
Router.map(function() {
this.route('onePassage', {
path: '/passages/:_id',
template: 'passageItem' // <-- to be explicit
data: function() {
return Passages.findOne(this.params._id);
}
});
});
Template
And your template looks like the template below:
<template name="passageItem">
<div class="passage">
<div class="one-passage">
<h4>{{title}}</h4>
<div class="passage-content">
{{content}}
</div>
</div>
</div>
</template>
The scope of 'this' in the template will be set to document returned by the Passages.findOne selector.
If the template doesn't render that means you're either searching for passage that doesn't exist, or your passage is missing title or content fields.
Rendered Function
Now for the last part of your question. The scope of 'this' in a rendered function is set to the template instance. So if you need to access the template data try this:
Template.passageItem.rendered = function() {
console.log(this.data); // you should see your passage object in the console
};

As of Meteor 1.0.3.1, the new Iron Router data selector appears to be...
Template.TemplateName.rendered = function() {
console.log(UI.getData());
};

I assume a passage consists of {'title':'', 'content':''}
Then this should work:
in router.js
Router.map(function() {
this.route('passagesList', {path: '/'});
this.route('onePassage', {
path: '/passages/:_id',
data: {
passage: function() { return Passages.findOne(this.params._id); }
}
});
});
in passage-item.html:
<template name="passageItem">
{{#each passage}}
<div class="passage">
<div class="one-passage">
<h4>{{title}}</h4>
<div class="passage-content">
{{content}}
</div>
</div>
</div>
{{/each}}
</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.

Is there a way to access Iron Router parameter from template in Meteor

I have a route that has a parameter in it and I need to access it from many different templates. Below is one example of the route, there are several routes that are very similar just after the _occasionnId parameter it changes:
For example:
Route 1: /occasions/:_occasionId/cards/
Router 2: /occasions/:_occasionId/tables/
Here is my full code for each route, the only thing that really changes is the route path and the template.
Router.route('/occasions/:_occasionId/cards/', {
template: 'cards',
data: function(){
//var currentoccasion = this.params._occasionId;
//console.log(currentoccasion);
},subscriptions : function(){
Meteor.subscribe('cards');
Meteor.subscribe('tables');
}
});
I need to get the _occasionId parameter into a template that has navigation which goes in on all of these pages. My goal is that from Route 1, you can go to Router 2. But I can't figure out how to add the correct URL in the template.
My template is:
<template name="occasionnav">
<nav>
<div class="nav-wrapper">
<ul class="right hide-on-med-and-down">
<li>cards</li>
<li>tables</li>
</ul>
</div>
</nav>
</template>
In the 'occasionnav' template by ":_occasionId" I need that to be the same parameter as the page currently being viewed be stuck into here.
If anyone has any insight or advice on the best way to approach this I would really appreciate it.
I recommend to use {{pathFor}} if you want to render an internal route in your Meteor application.
You just need to set the proper context and name your routes, for example:
<template name="occasionnav">
<nav>
<div class="nav-wrapper">
<ul class="right hide-on-med-and-down">
{{#with occasion}}
<li>cards</li>
<li>tables</li>
{{/with}}
</ul>
</div>
</nav>
</template>
Router.route('/occasions/:_id/cards/', {
template: 'cards',
name: 'occasions.cards',
data: function() {
return Cards.findOne({_id: this.params._id});
},
subscriptions: function() {
return Meteor.subscribe('cards', this.params._id);
}
});
Router.route('/occasions/:_id/tables/', {
template: 'tables',
name: 'occasions.tables',
data: function() {
return Tables.findOne({_id: this.params._id});
},
subscriptions: function() {
return Meteor.subscribe('tables', this.params._id);
}
});
However, you can also get the router parameters in your template via Router.current().params.
You can pass the _occasionId as a template helper and render it in jade like:
<li>cards</li>
You should try :
Router.route('/occasions/:_occasionId/cards/', function() {
this.layout("LayoutName");
this.render("cards", {
data: {
currentoccasion = this.params._occasionId;
}
});
});
When you use Router.map, it's not exactly the same syntax:
Router.map(function() {
this.route('<template name>', {
path: 'path/:_currentoccasion',
data: function () {
return {
currentoccasion: this.params._currentoccasion
}
}
});
And you can access just like that in your template :
Like an helper
{{ currentoccasion }}
Or on the onRendered and onCreated functions
Template.<template name>.onRendered({
this.data.currentoccasion

Reactive-Tables Meteor

I am trying to get reactive-tables to work but I am having no luck following the instructions on GitHub.
This is what I have:
In my Main.html:
{{> courseTable }}
in my course_table.html:
<template name="courseTable">
<div id="table">
{{ > reactiveTable collection=Courses}}
</div>
</template>
in courses.js:(works with autoForm)
Courses = new Meteor.Collection("courses", {
schema: {....
Is there something I am missing? From my understanding once these commands are used, the rest is done from within the package. I can't find any more information on this package anywhere.
What I have now just shows a blank screen.
Thanks in advance!
This is what I have: (I'm using Meteor framework and bootstrap-3 package)
in index.html
<template name="clientes">
<div class="container">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Clientes</h3>
</div>
<div class="panel-body">
{{> reactiveTable collection=tables settings=tableSettings}}
</div>
</div>
</div>
</template>
in index.js
var Clientes = new Meteor.Collection('clientes')
Template.clientes.tables = function () {
return Clientes;
}
Template.clientes.tableSettings = function () {
return {
rowsPerPage: 10,
showFilter: false,
showNavigation: 'auto',
fields: [
{ key: 'nombre', label: 'Nombre' },
{ key: 'apellido', label: 'Apellido' },
{ key: 'correoe', label: 'E-mail' }
],
useFontAwesome: true,
group: 'client'
};
}
With this I can display all the records in the collection.
I hope this help you to go any further.
Courses is the collection object. To get some courses, you need to query the courses with find:
Courses.find()
However, to make this accessible in the template, you need a helper function.
//course_table.js
Template.courseTable.helpers({
courses: function () {
return Courses.find()
}
});
Then you can can set the table collection using the helper method (I used a lowercase "courses" for the helper method for clarity):
{{> reactiveTable collection=courses}}

Meteor iron-router - Can I have multiple data sources in the route?

I have an application built with Meteor that uses Iron Router. My layout uses multiple yield templates and I'd like to pass through different data to each one.
It successfully passes through tasks to the tasksList template, but doesn't pass through selectedTask to the taskDetail template.
Is it possible to have multiple data sources and is this the right way to go about it? And if yes, then why is it not working?
Thanks in advance! :-)
Router.map(function() {
this.route('tasksList', {
path: '/',
layoutTemplate: 'layout',
template: 'tasksList',
yieldTemplates: {
'taskDetail': {to: 'rightTemplate'}
},
data: {
tasks: function(){ return Tasks.find() },
selectedTask: function() { return Tasks.findOne() }
}
});
});
<template name="layout">
<section class="wrapper">
<div class="left-pane">
{{yield}}
</div>
<div class="right-pane">
{{yield 'rightTemplate'}}
</div>
</section>
</template>
<template name="tasksList">
<ul>
{{#each tasks}}
<li>{{detail}}</li>
{{/each}}
</ul>
</template>
<template name="taskDetail">
{{#each selectedTask}}
<div>{{detail}}</div>
{{/each}}
</template>
You are returning selectedTask as a single object (with findOne), but in the taskDetail template, you use {{#each selectedTask}}{{detail}}{{/each}}. What happens if you simply have {{detail}} as the body of that template?
Sorry, both those methods work for me now. I must have had a wrong template name or something similar.
You can have multiple data sources as shown in the examples above.

passing values to meteor partials

I'm learning meteor to build quick website prototypes.
I'm trying to understand how to generate a set of values to populate the site templates and partials.
I have a layout.html template
<template name="layout">
<div class="container">
<header role="banner">
{{>site-header}}
</header>
<h1>This is {{siteLogo}}</h1>
<main role="main">
{{ yield }}
</main>
<footer role="contentinfo">
{{> site-footer }}
</footer>
</div>
</template>
in main.js I define the following:
Meteor.startup(function(){
Session.set('siteLogo', 'the logo');
});
Template.site-header.helpers({
siteLogo: function(){ return Session.get('siteLogo'); }
});
Template.layout.helpers({
siteLogo: function(){ return Session.get('siteLogo'); }
});
With this i can pass the value of siteLogo to layout.html.
I have a site-header.html partial
<template name="site-header">
<h1>{{siteLogo}}</h1>
</template>
I can't seem to be able to pass the value of siteLogo to the partial. Is there a way to do that?
Is it necessary to create a Session variable to pre-fill some values or can i just create a json settings list and access the value globally?
something that would go in main.js, like the yaml config file in a jekyll site:
siteSettings = [
{
siteLogo: "some brand name",
otherValue: "something else"
}
]
update
I'm a bit confused, I'm must be doing something wrong.
I've created a quick new meteor app to test this.
I have main.html
<head>
<title>handlebar-helper</title>
</head>
<body>
{{> header}}
{{> hello}}
{{> footer}}
</body>
<template name="hello">
<h1>Hello World!</h1>
{{greeting}}
<input type="button" value="Click" />
</template>
<template name="header">
<header>
<h1>{{ headline }}</h1>
<p>tagline</p>
</header>
</template>
<template name="footer">
<footer role="contentinfo">
<h1>{{ headline }}</h1>
<small>copyright</small>
</footer>
</template>
And main.js
if (Meteor.isClient) {
Template.hello.greeting = function () {
return "Welcome to handlebar-helper.";
};
Template.hello.events({
'click input' : function () {
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});
Meteor.startup(function(){
Session.set('headline', 'My fancy headline');
});
Handlebars.registerHelper('headline', function(){
return Session.get('headline');
});
}
if (Meteor.isServer) {
// server code here
}
And i can't still pass the value of headline into >header of >footer
if I try to put the Session.set into the Meteor.isServer block, I get a syntax error, Session is not defined
Cheers
Do you have a Template.site-header.helpers function declared for siteLogo? If not it won't work - you can't use a helper from another template. If you need to use siteLogo in a variety of places, it's best to use a Handlebars block helper, as these can be accessed by any template.
UPDATE
The Handlebars helper would just look like this:
Handlebars.registerHelper('siteLogo', function() {
return Session.get('siteLogo');
});
However, if you've already got a siteLogo helper in the site-header Template, it suggests something else is wrong, like a typo in a template or helper name. Is there an error in the console?
UPDATE 2
If you want to use a dictionary-style structure to store reactive data, you can do something like this:
Session.set('myDict', {foo: 1, bar: 2});
Handlebars.registerHelper('myDict', function(key) {
return Session.get('myDict') ? Session.get('myDict')[key] : null;
});
And then use this in your template: {{myDict 'foo'}}. Obviously, the format above would work fine in a tempate helper as well, but it would only be accessible from within that template. The ternary operator is just to check that myDict has been initialised before it lets a template try to look up one of the keys, which is a common Meteor problem on page load.
Incidentally, if you're finding Session variables a cumbersome way to deal with reactive dictionary-like data structures, it's pretty easy to roll your own. This is the best introduction.

Resources