Meteor template rendered twice - meteor

This Meteor client Template.payment.onRendered gets fired twice, which calls the server side method twice, How can I only get it to fire the backend method once if it must do the rendering twice, else how can it get it to render only once? thx
//client
Template.mainMenu.events({
'click .menuItem': (event) => {
let menuShortName = event.currentTarget.dataset.template;
Session.set('taskSelected', menuShortName);
Meteor.call('mainAction', menuShortName);
}
});
Template.index.helpers({
'taskInputs': function () {
let task = Session.get('taskSelected');
let tasks = task ? task.split(',') : [];
let data = DisplayCol.find({action: {$in: tasks}}, {sort: {createdAt: 1}}).fetch();
return {items: data};
}
});
//server
'mainAction': function (menuShortName) {
DisplayCol.remove({userId: this.userId});
lib.displayMakeUp({'action': menuShortName});
},
'displayMakeUp': (doc) => {
for (let i = 0; i < attrItems.length; i++) {
if (attrItems[i].action === doc.action || attrItems[i].action.indexOf(doc.action + '_') >= 0) {
let group = {};
group = attrItems[i];
group.userId = Meteor.userId();
console.log(JSON.stringify(group));
DisplayCol.insert(group);
}
}
},
Template.payment.onRendered(function () {
Meteor.call('getClientToken', function (error, clientToken) {
if (error) {
console.log(error);
} else {
braintree.setup(clientToken, "dropin", {
container: "payment-form", // Injecting into <div id="payment-form"></div>
onPaymentMethodReceived: function (response) {
var nonce = response.nonce;
console.log(nonce);
}
});
}
});
});
Templates:
<body>
{{> header}}
{{#if currentUser}}
{{#if isVerified}}
{{> index}} <-------------------------- (1)
{{else}}
<br><br><br><br>
<p>Check your email for your verification link!</p>
{{/if}}
{{else}}
{{> terms}}
{{/if}}
</body>
<template name="index">
<div id="main">
{{#if (display 'mainMenu')}}
{{> mainMenu}}
{{else}} {{#if (display 'content')}}
{{> Template.dynamic template="content" data=taskInputs}} <------------------- (2)
{{#if (session 'showFooter')}}
{{> footer}}
{{/if}}
{{/if}}{{/if}}
</div>
</template>
<template name="content">
{{> subMenu}}
<div id="main">
<div id="content">
<form>
<button type="submit" style="display:none"></button>
{{#if Template.subscriptionsReady}}
{{#each this.items}}
{{> sub}} <---------------------------------- (3)
{{/each}}
{{/if}}
</form>
</div>
</div>
</template>
<template name="sub">
{{#if isEqual element "payment"}}
{{> payment}} <--------------------------------------- (4)
{{/if}}
</template>
<template name="payment">
<form role="form">
<div class="row">
<div class="col-md-6 col-xs-12">
<br>
<div id="payment-form"></div>
<button type="submit" class="btn btn-success">Submit</button>
</div>
</div>
</form>
</template>

Based on what I see, it looks like you are rendering the template inside an each block. This will render one instance of your template for each each iteration.
{{#each this.items}}
{{> sub}} <---------------------------------- (3)
{{/each}}
Keep in mind that each template instance will fire all its callbacks (including onRendered). If you only want one instance then take it out of the each

Try using this package to limit rendering. You can also yield sub to payment (instead of checking the element) try checking if this is the first run inside your route.
//html
{{> yield sub}}
//router
if (Tracker.currentComputation.firstRun) {
this.render("payment",{to:"sub"});
}

Related

meteor database insert in global collection

The code below should insert the selected item value in the Tasks collection, retain the info for later use, and a headerLabel should show the task selected. I am not able to get the headerLabel to show the task when the click .menuItem function runs. Thanks
Tasks = new Mongo.Collection('tasks');
Template.mainMenu.events({
'click .menuItem': function(event){
Tasks.insert({menuItem: $(event.currentTarget).data('value')});
}
});
Template.header.helpers({
headerLabel: function( id ){
return Tasks.findOne({_id: id}).menuItem;
},
tasks: function(){
return Tasks.find();
}
});
<template name="mainMenu">
<div class="container">
<div class="row">
<section class="col-xs-12">
<div class="list-group">
{{#each menuItems}}
<a href="#" class="list-group-item menuItem" data-value={{menuItem}}>
<img src="/abc.png">
{{menuItem}} <span class="badge">></span>
</a>
{{/each}}
</div>
</section>
</div>
<template name="header">
<h1><button class="col-xs-2 mainMenu" type="button">☰</button></h1>
<h3>
<label class="col-xs-8 text-center">
{{#if headerLabel}} {{headerLabel}} {{else}} Select an item {{/if}}
</label>
</h3>
<h1><button class="col-xs-2" type="button">⋮</button></h1>
</template>
Assuming that the click can happen multiple times, you'll need to pass an _id for the appropriate task into your helper:
Tasks = new Mongo.Collection('tasks');
Template.mainMenu.events({
'click .menuItem': function(event){
Tasks.insert({menuItem: $(event.currentTarget).data('value')});
}
});
Template.header.helpers({
headerLabel: function( id ){
var task = Tasks.findOne({_id: id});
if( task ) {
return task.menuItem;
}
},
tasks: function() {
return Tasks.find();
}
});
So what I'm doing there is saying find one task's menuItem that has the ID passed to the helper. I also added a tasks helper to get all tasks. So your template might look something like:
<template name="mainMenu">
{{#each task in tasks}}
<h4>{{headerLabel task._id}}</h4>
<!-- Additional code here... -->
{{/each}}
</template>
You'll obviously need to customize the HTML to your specific situation, but that should give you the gist. Hope that helps!

MeteorJS update AutoForm not loading doc

I have a template with an update Autoform:
<template name = "editLocationPage">
<div class="flow-text">
{{#if Template.subscriptionsReady}}
<div>
<br>
{{#autoForm collection="Locations" doc=currentDoc id="editLocationPage" type="update"}}
<fieldset>
{{testDoc}}
<legend>Edit Location / Asset</legend>
{{> afQuickField name='id'}}
{{> afQuickField name='text'}}
{{> afQuickField name='description' rows=3}}
{{> afQuickField name='type'}}
{{> afQuickField name='parent'}}
</fieldset>
<button type="submit" class="btn waves-effect waves-light">Submit</button>
<button type="reset" class="btn waves-effect waves-light red">Reset</button>
{{/autoForm}}
</div>
{{/if}}
</div>
</template>
Some helpers to subscribe and pass the doc to the template:
Template.editLocationPage.onCreated(function() {
var self = this;
self.autorun(function() {
self.subscribe('singleLocation', Session.get("idTreeView").toString());
});
});
Template.editLocationPage.helpers({
currentDoc: function() {
return Locations.find({"id":Session.get("idTreeView").toString()}).fetch()[0];
}
});
A schema:
// Data subset subscribed to on client
Meteor.publish('locations', function() {
return Locations.find({}, {fields: {
text: true,
id: true,
type:true,
parent:true
}});
});
Meteor.publish('singleLocation', function(locationId) {
return Locations.find({id:locationId});
});
The document is ok (findOne returns a valid doc) but the form does not work. Any ideas?
I just found the problem. I needed to add the schema:
{{#autoForm schema=locationFormSchema id="editLocationPage" type="update" collection=Locations doc=currentDoc}}
with a helper:
Template.editLocationPage.helpers({
currentDoc: function() {
return Locations.findOne({"id":Session.get("idTreeView").toString()});
},
locationFormSchema: function() {
return Schema.locations;
}
});

Meteor Flow Router issue with update

I have a Meteor app and I'm transitioning from IronRouter to FlowRouter. So far so good, but there are aspects I don't understand yet.
I have a route as follows:
FlowRouter.route('/documents/:docId/edit', {
name: 'documentEdit',
subscriptions: function (params, queryParams) {
this.register('documentEdit', Meteor.subscribe('documentSingle', params.docId));
},
action: function (params, queryParams) {
BlazeLayout.render('layout', { top: 'header', main: 'documentEdit' });
},
});
First option:
Then I also have a template:
<template name="documentEdit">
<div class="container">
<h1>Edit document</h1>
{{#if isReady 'documentEdit'}}
{{#autoForm collection="Documents" doc=this id="documentForm" type="update" meteormethod="documentUpdateMethod"}}
<fieldset>
{{> afQuickField name='title'}}
{{> afQuickField name='content' rows=6}}
</fieldset>
<button type="submit" class="btn btn-primary">Update</button>
<a class="btn btn-link" role="button" href="{{pathFor 'documentsList'}}">Back</a>
{{/autoForm}}
{{/if}}
</div>
</template>
with a template helper as follows:
Template.documentEdit.helpers({
isReady: function(sub) {
if(sub) {
return FlowRouter.subsReady(sub);
} else {
return FlowRouter.subsReady();
}
}
});
This is as it is mentioned here, but I'm not getting the values pre-filled in the textboxes on the UI (which is normal when editing fields).
Second option:
When I do the following it works and I don't really understand why it works (found it browsing in different forums and tried it out):
<template name="documentEdit">
<div class="container">
<h1>Edit document</h1>
{{#with getDocument }}
{{#autoForm collection="Documents" doc=this id="documentForm" type="update" meteormethod="documentUpdateMethod"}}
<fieldset>
{{> afQuickField name='title'}}
{{> afQuickField name='content' rows=6}}
</fieldset>
<button type="submit" class="btn btn-primary">Update</button>
<a class="btn btn-link" role="button" href="{{pathFor 'documentsList'}}">Back</a>
{{/autoForm}}
{{/with}}
</div>
</template>
and the helper:
Template.documentEdit.helpers({
getDocument: function () {
return Documents.findOne();
}
});
So the questions are:
for the 1st option: any idea why it does not work. I would prefer that one as it's the documented way of doing things
for the 2nd option: not sure why I need (in the template helper) to do a Document.findOne() without even having to pass the id of the doc I want to edit:
You want to do template level subscriptions with Flow Router, that's one of the primary pattern changes.
So you'd do:
Setup the subscription at the template level. Autorun so it'll resubscribe on route changes.
Template.documentEdit.onCreated(function() {
var self = this;
this.autorun(function() {
var docId = FlowRouter.getParam('docId');
self.subscribe('documentSingle', docId));
};
};
Setup the template helper to pick up from the route, and grab the id and populate the helper/document.
Template.documentEdit.helpers({
getDocument: function () {
var docId = FlowRouter.getParam('docId');
var doc = Documents.findOne(docId) || {};
return doc;
}
});
Do a template level load check and if it's there render it, otherwise show loading...
<template name="documentEdit">
<div class="container">
<h1>Edit document</h1>
{{#if Template.subscriptionReady}}
{{#with getDocument }}
{{#autoForm collection="Documents" doc=this id="documentForm" type="update" meteormethod="documentUpdateMethod"}}
<fieldset>
{{> afQuickField name='title'}}
{{> afQuickField name='content' rows=6}}
</fieldset>
<button type="submit" class="btn btn-primary">Update</button>
<a class="btn btn-link" role="button" href="{{pathFor 'documentsList'}}">Back</a>
{{/autoForm}}
{{/with}}
{{else}}
Loading...
{{/if}}
</div>
</template>
Not knowing how this worked, seeing that all the tutorials I have read that deals with update used Iron router, I have spent 7 days worth of trying retrying, looking through others code, reading tutorials. Happy it now works.

Why does my template not reactive?

If I first open http://localhost:3000/, then click the test link, the roles labels will be displayed.
But if I directly open http://localhost:3000/test(Input the url in Chrome's address bar and hit enter), the roles labels will not be displayed.
Here is my code:
In client startup I subscribe to something:
Meteor.publish("Roles", function(){
return Roles.find();
});
Meteor.startup(function() {
if(Meteor.isClient) {
Meteor.subscribe('Roles');
}
});
And roles template:
Template.roles.helper( {
allRoles: function() {
return Roles.find();
}
})
html
<template name="roles">
<div>
{{#each allRoles}}
<label>test label</label>
{{/each}}
</div>
</template>
The problem is sometime roles template is rendered before the Roles is ready.
So these is no role labels displayed.
But according to Meteor document, helpers is a reactive computation, and Database queries on Collections is reactive data source. So after Roles is ready, the {{#with allRoles}} is reactive and should be displayed.
Why does roles not be displayed?
And then I rewrite my code to:
Meteor.startup(function() {
if(Meteor.isClient) {
roles_sub = Meteor.subscribe('Roles');
}
});
Template.roles.helper( {
allRoles: function() {
console.log(2);
return Roles.find();
},
isReady: function() {
console.log(1);
console.log(roles_sub.ready());
return roles_sub.ready();
}
})
html
<template name="roles">
<div>
{{#if isReady}}
{{#each allRoles}}
<label>test label</label>
{{/each}}
{{/if}}
</div>
</template>
And still role labels cannot be displayed.
And console gives me:
1
false
1
false
1
true
2
Which means isReady() is reactive? but why my roles labels remains blank?
Can somebody explain this?
use Template.subscriptionsReady
server/publish.js
Meteor.publish("Roles", function(){
return Roles.find();
});
client/client.js
Meteor.startup(function() {
Meteor.subscribe('Roles');
});
Template.roles.helpers({ // --> .helper change to .helpers
allRoles: function() {
return Roles.find();
}
})
client/templates.html
<template name="roles">
<div>
{{# if Template.subscriptionsReady }}
{{#with allRoles}}
<label>{{> role }}</label>
{{/with}}
{{ else }}
loading....
{{/if}}
</div>
</template>
<template name="role">
<div>{{ _id }}</div>
</template>
A reactive function that returns true when all of the subscriptions
https://github.com/meteor/meteor/blob/9fe2f4b442ec84eac5352b476d014c977c5ae4a2/packages/blaze/template.js#L424

Handlebars.js - how to pick a "logout" value and append to "div" element

In the Handlebars.js - template i am getting a senario to check the value of the array, and if the value is not equal to do a task and equal to do a taks.. i do like this, But it thrown a error, how to handle this scenario.. any one help me?
Or any one give the way to handle this properly.
myJson would be :
{
"links":[{"label":"x","link":"x"},
{"label":"y","link":"y"},
{"label":"Logout","link":"Logout"}]
}
{{#each links}}
{{#if !lable.Logout}}
<li>
{{label}}
{{#if subLinks}}
<ul>
{{#each subLinks}}
<li>{{label}}</li>
{{/each}}
</ul>
{{else}}
<div>{{label}}</div>
{{/if}}
{{/each}}
Handlebars alone cannot handle conditional if statements, you need to use a helper function.
<div id="myDiv"></div>
<script type="text/x-handlebars-template" id="handlebar">
{{#each links}}
{{#ifCond label "Logout" }}
<li>
{{label}}
{{#if subLinks}}
<ul>
{{#each subLinks}}
<li>{{label}}</li>
{{/each}}
</ul>
{{/if}}
{{else}}
<div>
{{label}}
</div>
{{/ifCond}}
{{/each}}
</script>
<script>
$( document ).ready(function() {
var source = $("#handlebar").html();
var template = Handlebars.compile(source);
var context = {
"links":[
{"label":"x","link":"x"},
{"label":"y","link":"y"},
{"label":"Logout","link":"Logout"}
]
};
var html = template(context);
$("#myDiv").html(html);
});
Handlebars.registerHelper('ifCond', function(v1, v2, options) {
if(v1 === v2) {
return options.fn(this);
}
return options.inverse(this);
});
</script>

Resources