Meteor Blaze.renderWithData how to InsertAfter is it possible? - meteor

I'm having a problem to insert a template after and not before a node. For example:
//Html looks like this
<div class="questions">
<div class="question"></div>
<div class="question"></div>
<div class="question"></div>
</div>
<template name="question">
<div class="question"></div>
</div>
<template name="questionExtraInfo">
<div class="extra"></div>
</template>
I'm trying to get the following:
<div class="questions">
<div class="question"></div>
<div class="extra"></div>
<div class="question"></div>
<div class="question"></div>
</div>
Calling blaze render inside question event
Template.question.events({
'click .more-details': function () {
var instance = Template.instance();
Blaze.renderWithData(Template.questionExtraInfo, {}, document.querySelector('.questions'), instance.find('.question')));
});
I can only figure out how render it before or inside how about after?
<div class="extra"></div>
<div class="question"></div>
<div class="question"><div class="extra"></div></div>

I think a better approach would be to take advantage of reactivity:
Change your questions template to:
<template name="question">
<div class="question"></div>
{{# if shouldIncludeExtra }}
{{> questionExtraInfo }}
{{/if}}
</template>
The above template should be inside an each loop.
Then in your js something like:
Template.question.helpers({
'shouldIncludeExtra': function() {
// replace 'n' with the actual index. I think `this.index` is
// provided within #each blocks, or you can use the new `#each` helper.
var index = n;
return Session.get('shouldIncludeExtra' + index);
}
});
Then, in your click event, you set a session var based on the index to true:
Template.questions.events({
'click .question': function(e, tpl) {
var question = e.currentTarget;
// You can probably come up with something better here..
var index = $(question).parent().find('> .question').index(question);
Session.set('shouldIncludeExtra' + index, true);
}
});
Because of reactivity, you would see the inserts right away when you fire the click event.
I realize this doesn't really answer the headline of your question, but it should get you the desired outcome.

Related

dropzone-meteor howto fire events

i'm new to Meteor and and i want to learn something about it. therefore i want to build a page were i can upload images via meteor-dropzone.
the upload is working with meteor-uploads.
now i want to get events, like 'addedfile' or 'drop' from the dropzone to fire some new functions.
HTML Page Profile2:
<template name="profile2">
<div class="ibox-content">
{{> dropzone url='http://localhost:3000/uploads' maxFilesize=5 addRemoveLinks=true acceptedFiles='image/*,jpg,jpeg,png' id='dropzoneDiv'}}
</div>
</template name="profile2">
In The JS File for Profile2 i wrote this:
Template.dropzone.events({
'addedfile #dropzoneDiv': function(e, template){
e.preventDefault();
console.log("Hello");
}
});
But i don't see something in the console.log output.
I'm sure i'm doing something wrong. But i have no i idea where the problem or the wrong understanding is.
Can somebody help me please.
Thanks.
Michael
after try and error. i found the solution. Maybe someone can explain it to me. because i don't understand it completely, why it's working now but so different to the normal Meteor event version.
Dropzone.options.dropzoneDiv = {
init: function() {
this.on("addedfile", function(file) { alert("Added file."); });
}
};
The Template like that:
<!-- Page heading -->
{{> pageHeading title='File upload' category='Forms' }}
<div class="wrapper wrapper-content animated fadeIn">
<div class="row">
<div class="col-lg-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>Dropzone Area</h5>
{{>iboxTools}}
</div>
<div class="ibox-content">
<!-- For more info about dropzone plugin see this: https://github.com/devonbarrett/meteor-dropzone/ -->
{{> dropzone url='/uploads' id='dropzoneDiv'}}
</div>
</div>
</div>
</div>
</div>
</template>
Try the dropped event:
'dropped #dropzoneDiv' (e, template) => {
e.preventDefault();
console.log(e.originalEvent.dataTransfer.files); // this will contain the list of files that were dropped
}

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!

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);
.....
});

Template helper crashing app

My app is crashing my browser after implementing this columnWidth helper method. All I'm trying to do is rotate between col-md-7 and col-md-5 (Bootstrap classes) so that no two consecutive posts are the same width.
columnWidth: function() {
if (Session.get('columnWidth') === 'col-md-7') {
Session.set('columnWidth', 'col-md-5');
} else {
Session.set('columnWidth', 'col-md-7');
}
return Session.get('columnWidth');
}
The post template:
{{#each this}}
<div class="{{columnWidth}}">
<img src="{{image}}" height="350" width="{{imageWidth}}" alt="">
<div class="content">
<h2>{{title}}</h2>
<p>{{content}}</p>
<span class="dateAuthored">{{date}}</span>
</div>
</div>
{{/each}}
this refers to:
data: function() {
return Articles.find();
}
Any ideas why this is happening? I'm not receiving any errors. The browser tab just becomes unresponsive. Thanks.
You are constantly setting the same reactive variable so for example with the first div when the helper is called it will set it to col-md-7, then when it is called for the 2nd row you are changing the same variable to col-md-5 which is problematic for 2 reasons:
a) the template will redraw the first column and so they will both be col-md-5
b) the same helpers will get called again. I believe your browser crashes because you have created an infinite loop. Try console logging something inside your columnWidth helper and see how many times it gets called.
To achieve what you want you need to get the index of the {{#each }} loop and then have the column class dependent on whether it's odd or even. Unfortunately getting the index in meteor handlebars is a little tricky.
Try:
{{#each articles}}
<div class="{{columnWidth index}}">
<img src="{{image}}" height="350" width="{{imageWidth}}" alt="">
<div class="content">
<h2>{{title}}</h2>
<p>{{content}}</p>
<span class="dateAuthored">{{date}}</span>
</div>
</div>
{{/each}}
Then the following helpers:
articles: function() {
//'this' in this context should work.
//if not just replace with Articles.find().map(...
var articles = this.map(function(article, index) {
var i = _.extend(article, {index: index});
return i;
});
return articles;
},
columnWidth: function(index) {
if (index % 2 === 0)
return "col-md-5";
else
return "col-md-7"
}

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>

Resources