How to pass parameter data between templates in meteor flowlayout flowrouter - meteor

I have a template called : Orders which shows my orders collection of images :
{{#each images}}
<div class="images">
<img class="image" src="{{this.url }}" />
</div>
{{/each}}
No I want another tempate called order to show me only one item from collection that I click on: I try doing this way: 1. orders.js events for click on image:
"click .image": function() {
Images.find({_id:this._id});
and orders.html:
<img class="image" src="{{this.url }}" />
I also have routes.js :
FlowRouter.route("/orders", { **this part works fine**
action: function(){
FlowLayout.render("layout",{top:"orders", main:"test"});
}
FlowRouter.route('/order/', { **How do I do this part ????????**
action: function(){
FlowLayout.render("layout",{top:"order",data:image});
}
I used a dynamic layout template to show orders which shows fine.
How do I set the single order html , route and render ????

To answer your question:
Beginning by your route, you should pass the id parameter in the path like that :
FlowRouter.route('/order/:imageId', {
action: function() {
FlowLayout.render("layout",{ top: "order", main: "test" });
}
});
Looking at the rendering on order.html file which contains the template order, something like:
<template name="order">
{{#with image}}
<!-- whatever you want to do -->
<img src="{{url}}" />
{{/with}}
</template>
This template uses order.js with a subscription to your collection for one image only and a helper called image which look for the parameter imageId you transmitted in your route via the function FlowRouter.getParam:
Template.order.onCreated(function() {
var imageId = FlowRouter.getParam('imagedId');
if ( imageId !== undefined ) {
Meteor.subscribe('oneImage', imageId);
}
}
Template.order.helpers({
image: function() {
var imageId = FlowRouter.getParam('imagedId');
if ( imageId !== undefined ) {
return Images.findOne({ _id: imageId });
}
}
}
And to conclude, server side, you shall do a publication:
Meteor.publish('oneImage', function(imageId) {
return Images.findOne({ _id: imageId });
}
Following this way, you don't need anymore your event click .image and you optimized your performance! ;)
Btw on orders.html in your {{#each}} loop, you don't need to write {{this.url}} nor {{this._id}}, {{url}} and {{_id}} are fine ;)

To retrieve request parameter you can use:
FlowRouter.current().route._params.keys.id

Related

Meteor, publish:composite. how to access joined data in the template?

So I used publishComposite to do a collection join in Meteor. I have a parent collection (Subscriptions) with a user_id foreign key. I look up the user name in the Meteor.users collection to get the actual username, but how do I actually print this in the html template. My subscription data is there but how do I actually refer to the username?
Here is the publish code:
//publish subscriptions course view
Meteor.publishComposite('adminCourseSubscriptions', function(courseId){
return {
//get the subs for the selected course
find: function(){
return Subscriptions.find(
{course_id: courseId}
);
},
children:
[
{
//get the subscriber details for the course
find: function(sub){
return Meteor.users.find({_id:sub.user_id});
}
}
]
};
});
here are the template subdcriptions:
Template.adminCourseDetail.helpers({
courseDetail: function(id){
var id = FlowRouter.getParam('id');
return Courses.findOne({ _id: id });
},
courseSubscriptions: function(){
var id = FlowRouter.getParam('id');
return Subscriptions.find({course_id:id})
},
users: function(){
return Meteor.users.find();
}
});
and the template (which is garbage) ps the course details come from a separate collection. It was easier and I think more performant to get the details separately and this works fine. It's just the username that I cannot display correctly:
<template name="adminCourseDetail">
<h1>Course Details</h1>
<p>Title: {{courseDetail.title}}</p>
<p>Description: {{courseDetail.description}}</p>
<p>Start Date: {{courseDetail.startDate}}</p>
<p>Number of sessions: {{courseDetail.sessions}}</p>
<p>Duration: {{courseDetail.duration}}</p>
<p>Price: {{courseDetail.price}}</p>
<p>{{userTest}}</p>
edit
delete
<h2>Course Subscriptions</h2>
{{#each courseSubscriptions}}
<div class="row">
<div class="col-md-3">{{username}}</div>
<div class="col-md-3">{{sub_date}}</div>
</div>
{{/each}}
</template>
Thanks in advance for any suggestions!
As far as I understand your question, documents of the Subscriptions collection contain only the attribute user_id, referencing the corresponding user document in the Meteor.users collection. If this is the case, then you need to add an additional template helper which returns the username:
Template.adminCourseDetail.helpers({
// ...
getUsername: function() {
if (this.user_id) {
let user = Meteor.users.find({
_id: this.user_id
});
return user && user.username;
}
return "Anonymous";
}
// ...
});
After that, just replace {{username}} with {{getUsername}}:
<template name="adminCourseDetail">
<!-- ... -->
<h2>Course Subscriptions</h2>
{{#each courseSubscriptions}}
<div class="row">
<div class="col-md-3">{{getUsername}}</div>
<div class="col-md-3">{{sub_date}}</div>
</div>
{{/each}}
<!-- ... -->
</template>
Probably you misunderstood the concept of the reywood:publish-composite package. Using Meteor.publishComposite(...) will just publish a reactive join, but it will not return a new set of joined data.
For anyone else having a similar issue and looking at my specfic example. In my case the following code worked. Based on Matthias' answer:
In the template helper:
getUsername: function() {
let user = Meteor.users.findOne({
_id: this.user_id
});
return user;
}
and then in the template:
{{getUsername.username}}
My each block is looping through the cursor returned from the subscriptions collection rather than the course collection which is why it is simpler than the code Matthias provided.

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

Iron Router / User details issue in Meteor

I am relatively new to Meteor and have been stuck on an issue for awhile. I have a /users/:_id route that is supposed to display details specific to that user id. However, whenever I hit that route, it displays information for the currently logged in user, NOT of the user whose details I want to view.
Here's my route:
Router.route('/users/:_id', {name: 'Users', controller: 'usersDetailController'});
Here's my usersDetailController:
usersDetailController = RouteController.extend({
waitOn: function () {
Meteor.subscribe('userProfileExtended', this.params._id);
},
onBeforeAction: function () {
var currUserId = Meteor.userId();
var currUser = Meteor.users.findOne({_id: currUserId});
console.log('admin? ' + currUser.isAdmin);
if (!currUser.isAdmin) {
this.render('accessDenied');
} else {
this.next();
}
},
action: function() {
this.render('Users');
}
});
And here's my server/publish:
Meteor.publish('userProfileExtended', function() {
return Meteor.users.find({_id: this.userId});
});
User Details template:
<template name="Users">
<form>
{{#with user}}
<div class="panel panel-default">
<div class="panel-heading">{{profile.companyName}} Details</div>
<div class="row">
<div class="col-md-4">
<div class="panel-body">
<p><label>Company: </label><input id="Company" type="text" value={{profile.companyName}}></p>
<p><label>Email: </label><input id="Email" type="text" value={{emails.address}}></p>
<p><label>Phone: </label><input id="Phone" type="text" value={{profile.phoneNum}}></p>
<p><label>Tire Markup: </label><input id = "tireMarkup" type="text" value={{profile.tireMarkup}}></p>
<p><button class="saveUserDetails">Save</button></p>
<p><button class="deleteUser">Delete User</button></p>
</div>
</div>
</div>
</div>
{{/with}}
Here's my Template Helper:
Template.Users.helpers({
user: function() {
return Meteor.users.findOne();
}
});
Can someone help? I think the issue is the way i reference "this.userId"...
Thank you!!
You need to change your publish function to use the userId parameter you specify when subscribing :
Meteor.publish('userProfileExtended', function(userId) {
return Meteor.users.find(userId,{
fields:{
'username':1,
'profile.firstName':1,
'profile.lastName'
}
});
});
In the publish function, userId will equal whatever value you call Meteor.subscribe with, in this case it will hold this.params._id.
Beware of using the proper syntax for route parameters, if you declare a path of /users/:_id, you need to reference the param using this.params._id.
Also note that it's insecure to publish the whole user document to the client if you only need to show specific fields in the interface, that's why you want to use the fields option of Collection.find to only publish a subset of user documents.
EDIT :
I would recommend using the route data function to specify the data context you want to apply when rendering your template, something like this :
data: function(){
return {
user: Meteor.users.findOne(this.params._id)
};
}

Meteor data-context with iron-router

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>

Template reuse in meteor

I'm trying to reuse some control elements in my Meteor app. I'd like the following two templates to toggle visibility and submission of different forms.
<template name='addControl'>
<img class='add' src='/images/icon-plus.png' />
</template>
<template name='okCancelControl'>
<img class='submit' src='/images/icon-submit.png' />
<img class='cancel' src='/images/icon-cancel.png' />
</template>
I'll call these templates in another:
<template name='insectForm'>
{{#if editing}}
<!-- form elements -->
{{> okCancelControl}}
{{else}}
{{> addControl}}
{{/if}}
</template>
editing is a Session boolean.
What's a good way to wire up the controls to show, hide and "submit" the form?
The main problem is finding the addInsect template (where the data is) from the control templates (where the "submit" event fires). Here's what I did:
First, the controls:
<template name='addControl'>
<section class='controls'>
<span class="add icon-plus"></span>
</section>
</template>
<template name='okCancelControl'>
<section class='controls'>
<span class="submit icon-publish"></span>
<span class="cancel icon-cancel"></span>
</section>
</template>
Now the javascripts. They simply invoke a callback when clicked.
Template.addControl.events({
'click .add': function(event, template) {
if (this.add != null) {
this.add(event, template);
}
}
});
Template.okCancelControl.events({
'click .cancel': function(event, template) {
if (this.cancel != null) {
this.cancel(event, template);
}
},
'click .submit': function(event, template) {
if (this.submit != null) {
this.submit(event, template);
}
}
});
I then connected the callbacks using handlebars' #with block helper.
<template name='addInsect'>
{{#with controlCallbacks}}
{{#if addingInsect}}
<section class='form'>
{{> insectErrors}}
<label for='scientificName'>Scientific Name <input type='text' id='scientificName' /></label>
<label for='commonName'>Common Name <input type='text' id='commonName' /></label>
{{> okCancelControl}}
</section>
{{else}}
{{> addControl}}
{{/if}}
{{/with}}
</template>
And the corresponding javascript that creates the callbacks relevant to this form.
Session.set('addingInsect', false);
Template.addInsect.controlCallbacks = {
add: function() {
Session.set('addingInsect', true);
Session.set('addInsectErrors', null);
},
cancel: function() {
Session.set('addingInsect', false);
Session.set('addInsectErrors', null);
},
submit: function() {
var attrs, errors;
attrs = {
commonName: DomUtils.find(document, 'input#commonName').value,
scientificName: DomUtils.find(document, 'input#scientificName').value
};
errors = Insects.validate(attrs);
Session.set('addInsectErrors', errors);
if (errors == null) {
Session.set('addingInsect', false);
Meteor.call('newInsect', attrs);
}
}
};
Template.addInsect.addingInsect = function() {
Session.get('addingInsect');
};
Template.addInsect.events = {
'keyup #scientificName, keyup #commonName': function(event, template) {
if (event.which === 13) {
this.submit();
}
}
};
In the submit callback I had to use DomUtils.find rather than template.find because template is an instance of okCancelControl, not addInsect.
You can use Session for this. You Just need a template helper that returns a boolean flag that indicates whether you are editing the form fields. And manipulate the DOM based on the Session value set by this template helper.
Assume you have one text input, now when you are entering text in it, set the Session flag as true. This will trigger the helper to return true flag, Based on that, one of your two templates will be rendered in the DOM.
The isEditing is the helper that triggers whenever you change the Session value.
This helper function is the main part here, it returns true/false based on the session value you have set.
Template.insectForm.isEditing = function(){
if(Session.get('isEditing')){
return true;
}
else{
return false;
}
}
Remember to set the Session to false at the start-up as:
$(document).ready(function(){
Session.set('isEditing', false);
})
This will render the default add template in the html, Now when you click on ADD, you need to display another template, for that, set Session to true as:
'click .add' : function(){
Session.set('isEditing', true);
}
Accordingly when you click on CANCEL, set the session to false, this will make the isEditing to return false and the default add template will be displayed.
So your complete html will look something like this:
<template name='insectForm'>
{{#if isEditing}}
<!-- form elements -->
<input type="text" id="text" value="">
{{> okCancelControl}}
{{else}}
{{> addControl}}
{{/if}}
</template>
<template name='addControl'>
<img class='add' src='/images/icon-plus.png' />
</template>
<template name='okCancelControl'>
<img class='submit' src='/images/icon-submit.png' />
<img class='cancel' src='/images/icon-cancel.png' />
</template>
[UPDATE]
To get the instance of the template, you'll need to pass the additional parameter in the event handler that represents the template.
So update your event handler as:
Template.insectForm.events = {
'click .submit' : function(event, template){
//your event handling code
}
}
The parameter template is the instance of the template from which the event originates.
Note that, although the event fires form the image that is inside the okCancelControl template, the parameter will still contain the instance of the insectForm template. This is because we are calling the event handler as Template.insectForm.events = {} .
Also see this answer for template instances.

Resources