Cannot insert JavaScript variable into Jade - meteor

I'm trying to insert a normal JavaScript expression into a Jade file. But my app keep getting error message when I add isAccepted variable into my code.This is the error message.
Your app is crashing. Here's the latest log.
Errors prevented startup:
While building the application:
jade-test.jade: Jade syntax error: Expected identifier, number, string, >boolean, or null
{{isAccepted ? 'class1' : 'class2...
^
packages/compileJade/plugin/handler.js:44:1: Cannot read property 'head' >of undefined (compiling jade-test.jade) (at fileModeHandler)
Your application has errors. Waiting for file change.
This is my code:
jade-test.jade
template(name="layout")
h1 This is layout template
h2 This is h2 paragraph
h3 This is h3 paragraph
+hello
template(name="hello")
h4 This is home template
button Click me
p You have click me #{counter} times.
- var isAccepted = true
button(class=isAccepted ? 'class1' : 'class2') I'm the right choice.
jade-test.js
if (Meteor.isClient) {
// counter starts at 0
Session.setDefault('counter', 0);
Template.hello.helpers({
counter: function () {
return Session.get('counter');
}
});
Template.hello.events({
'click button': function () {
// increment the counter when button is clicked
Session.set('counter', Session.get('counter') + 1);
}
});
Router.route('/', function() {
this.render('layout');
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}

Not sure if inserting javascript code like this in jade is supported, you should probably use a helper instead.
I don't think the ternary expression syntax is valid too, again, use a helper.
JADE
template(name="hello")
h4 This is home template
button Click me
p You have click me #{counter} times.
button(class=isAccepted) I'm the right choice.
JS
Template.hello.helpers({
isAccepted: function(){
var isAccepted = true;
return isAccepted ? "class1" : "class2";
}
});

Related

Meteor - Using reactive variables in anonymous functions

How can I use reactive template variables (from Template.data) in an anonymous function within the template rendered function? (I want to keep it reactive).
Template.templateName.rendered = function() {
function testFunction(){
//Log the field 'title' associated with the current template
console.log(this.data.title);
}
});
Not sure exactly what you are trying to do (like printing this.data.title whenever it changes?), but you should:
use a Reactive variable (add reactive-var package, then create a var myVar = new ReactiveVar()
If necessary, wrap your function with Tracker.autorun (or this.autorun in a template creation / rendered event).
So you could have like:
Parent template HTML:
<template name="parentTemplateName">
{{> templateName title=myReactiveVar}}
</template>
Parent template JS:
Template.parentTemplateName.helpers({
myReactiveVar: function () {
return new ReactiveVar("My Title!");
}
});
Template JS:
Template.templateName.onRendered(function() {
// Re-run whenever a ReactiveVar in the function block changes.
this.autorun(function () {
//Print the field 'title' associated with the current template
console.log(getValue(this.data.title));
});
});
function getValue(variable) {
return (variable instanceof ReactiveVar) ? variable.get() : variable;
}
What worked for me was simple using autorun() AND using Template.currentData() to grab the values from within autorun():
let title;
Template.templateName.rendered = function() {
this.autorun(function(){
title = Template.currentData().title;
});
function testFunction(){
console.log(title);
}
});
Template.templateName.onRendered(function(){
console.log(this.data.title);
});

Removing a window event listener in Template.onDestroyed

I have two templates that each contain a Vimeo iframe player. I'm using FlowRouter to render the templates through {{> Template.dynamic template=main}} on the main layout.
In both templates I add listeners for video events in onCreated
Template.view.onCreated( function() {
var self = this;
if (window.addEventListener) {
window.addEventListener('message', function(event) {
viewMessageReceived(event, self)}, false);
} else {
window.attachEvent('onmessage', function(event){
viewMessageReceived(event, self)}, false);
}
});
and destroy them in onDestroyed
Template.view.onDestroyed( function() {
if (window.removeEventListener) {
console.log('view remove');
window.removeEventListener('message', function(event) {
viewMessageReceived(event, self)}, false);
} else {
window.detachEvent('onmessage', function(event){
viewMessageReceived(event, self)}, false);
}
});
And here is the function being called by the anonymous event handler:
function viewMessageReceived(event, self) {
// Handle messages from the vimeo player only
if (!(/^https?:\/\/player.vimeo.com/).test(event.origin)) {
return false;
}
if (self.playerOrigin === '*') {
self.playerOrigin = event.origin;
}
var data = JSON.parse(event.data);
switch (data.event) {
case "ready":
initializePlayer(self);
break;
case "playProgress":
self.playerTime.set(data.data.seconds);
if (self.duration === '*') self.duration = data.data.duration;
break;
case "play":
self.playerStatus.set("playing");
break;
case "pause":
self.playerStatus.set("paused");
break;
}
}
When I switch to a different template, onDestroyed runs and my console.log('view remove') fires, as expected.
But then when I navigate to the page that loads the other template with a video player, a Vimeo "playProgress" message arrives that is received by the event handler in the previous video template, which was supposed to have been removed a while ago. This throws an error because the previous template has been destroyed.
Uncaught TypeError: Cannot read property 'contentWindow' of undefined
which comes from the last line in this function:
function post(template, action, value) {
console.log('view action: %s value: %s', action, value);
var data = {method: action};
if (value) data.value = value;
var message = JSON.stringify(data);
template.player[0].contentWindow.postMessage(message, template.playerOrigin);
}
Each of the video-containing templates have their own .js file, so they each have a their own post function declaration. My understanding is that defining a function that way scopes the function just to the page.
It's only one message that arrives for the wrong player. After that, they arrive for the currently loaded player.
Why does the Vimeo message event arrive or get handled after I've already destroyed the template and when I've moved to another player?
A quote from the W3Schools website regarding the removeEventListener() method:
// Attach an event handler to <div>
document.getElementById("myDIV").addEventListener("mousemove", myFunction);
// Remove the event handler from <div>
document.getElementById("myDIV").removeEventListener("mousemove", myFunction);
Note: To remove event handlers, the function specified with the
addEventListener() method must be an external function, like in the
example above (myFunction).
Anonymous functions, like "element.removeEventListener("event",
function(){ myScript });" will not work.
For this to work, you'll need to move your function definition somewhere outside of the onRendered and onDestroyed events, and just pass the function name in the add/remove event listeners.

Can't get reactive var from helper in meteor js

I'm trying to write the value of a reactive var to a meteor template:
Template.form.rendered = function() {
this.color = new ReactiveVar();
this.color.set('#333555');
};
and then I've defined a "currentColor" helper to use in the template to print the color: {{currentColor}}
Template.form.helpers({
currentColor: fuction() {
return Template.instance().color.get();
}
});
but it doesn't work;
so I've tried to add a couple of console.log:
Template.form.helpers({
currentColor: fuction() {
console.log(Template.instance());
console.log(Template.instance().color);
}
});
The strange thing is that the first console log shows a Blaze.TemplateInstance with the color property:
Blaze.TemplateInstance {...}
color: ReactiveVar
curValue: "#333555"
dep: Tracker.Dependency
equalsFunc: undefined
...
but the second log is "undefined";
Can someone help me to understand this?
You should place the ReactiveVar definition in your template.created() function, not the template.rendered() function.

meteorjs iron-router waitOn and using as data on rendered

I try to get the returned data in my Template.rendered function.
The current code is:
this.route('editCat', {
layoutTemplate : 'layoutCol2Left',
template : 'modCategoriesEdit',
path : '/mod/categories/edit/:_id',
yieldTemplates : _.extend(defaultYieldTemplates, {
'navigationBackend' : {to : 'contentLeft'}
}),
waitOn : function () {
return Meteor.subscribe('oneCat', this.params._id);
},
data : function () {
return Categories.findOne({_id : this.params._id});
}
});
In this block i wait on the subscribtion of the Collection Document and return the Document as data.
Now i can use the returned Document in my Template like this:
<template name="modCategoriesEdit">
<h1>Edit {{name}}</h1>
</template>
My problem is that i have to use the returned data in my rendered function like this:
Template.modCategoriesEdit.rendered = function () {
console.log(this.data);
}
But this returns "null".
So my question is:
How is it possible to get access to the returned data in the rendered function ?
Solution:
Just add the following to your iron-router route() method.
action : function () {
if (this.ready()) {
this.render();
}
}
Than the Template will rendered after all is loaded correctly.
There are 3 solutions if you want to wait until the waitOn data is ready before rendering:
1- Add an action hook to each route
Router.map(function()
{
this.route('myRoute',
{
action: function()
{
if (this.ready())
this.render();
}
}
}
2- Use the onBeforeAction hook globally or on every route
Sample code for the global hook:
Router.onBeforeAction(function(pause)
{
if (!this.ready())
{
pause();
this.render('myLoading');
}
});
myLoading (or whatever name) must be a template you have defined somewhere.
Don't forget the this.render line, otherwise the problem will occur when leaving the route (while the original problem occurs when loading the route).
3- Use the built-in onBeforeAction('loading') hook
Router.configure(
{
loadingTemplate: 'myLoading',
});
Router.onBeforeAction('loading');
myLoading (or whatever name) must be a template you have defined somewhere.
Using the action hook to check for this.ready() works, but it looks like the official way to do this is to call the following:
Router.onBeforeAction("loading");
Reference: https://github.com/EventedMind/iron-router/issues/679
Like #Sean said, the right solution is to use a loading template:
Router.onBeforeAction("loading");
But if you don't want it, like me, I came up with this solution:
Template.xxx.rendered = function() {
var self = this;
this.autorun(function(a) {
var data = Template.currentData(self.view);
if(!data) return;
console.log("has data! do stuff...");
console.dir(data);
//...
});
}
Template.currentData is reactive, so in the first time it is null and in the second it has your data.
Hope it helps.
-- Tested on Meteor v1.0 with Iron Router v1.0

How can I delay a reactive Meteor template from updating an HTML template variable, when a Meteor.call() updates the database instantly?

I'm developing a keno game. When the user presses the start button a Meteor.Call() executes everything for that card pick. Including updating the user balance. I have a setTimeout for the winning numbers, so that they display over a period of about 20 seconds. The problem is that when the call is made, the balance updates instantly, and then the numbers start displaying with the delay. I not familiar with how to solve this. I appreciate any help.
server-side:
Meteor.methods({
process: function(){
// generate numbers
// update user balance
}
});
client-side:
Template.keno.events({
'click #start' : function(){
Meteor.call('process',function(err,numbers){
//setTimeout on displaying numbers
// as setTimeout displays numbers, balance already updated. I need to delay
// the balance update, until all numbers are displayed.
// otherwise, the player see that they won before all numbers come out.
});
}
});
** Update **
The only help I need is to understand how to make a variable like {{balance}} unreactive, until I finish the setTimeout, and then have it update. Should I be using sessions? Should I not use a template variable and instead, insert the balance with jquery? It's just a simple solution, the difficulty is that I don't know what function / method I'm looking for that can help me turn off the reactivity for a set amount of time, and then update, after the Meteor.call() for then numbers finishes it's setTimeout.
If I understand your situation correctly, you need the template {{balance}} expression to be set at a time you decide vs. when the collection gets a result from the server. So you could use Session to set a value when you like. Below is an example:
<body>
{{> game}}
</body>
<template name="game">
<button id="process">Process</button>
<div>{{firstNumber}}</div>
<div>{{secondNumber}}</div>
<div>balance: {{balance}}</div>
</template>
if (Meteor.isClient) {
Template.game.events({
'click #process': function (e, tmpl) {
Meteor.call('process', function (err, result) {
Session.set('firstNumber', result[0]);
setTimeout(function () {
Session.set('secondNumber', result[1]);
Session.set('balance', result[0] + result[1]);
}, 2000);
});
}
});
Template.game.helpers({
firstNumber: function () { return Session.get('firstNumber'); },
secondNumber: function () { return Session.get('secondNumber'); },
balance: function () { return Session.get('balance'); }
});
}
if (Meteor.isServer) {
function randomNumber () {
return Math.floor(Math.random() * 100);
}
Meteor.methods({
process: function () {
return [randomNumber(), randomNumber()];
}
});
}
Try to wrap your Meteor.call() inside the setTimeout() itself, like:
Template.keno.events({
'click #start' : function(){
setTimeout(function(){
Meteor.call('process',function(){
//do something.
});
}, 20000);
}
});
Maybe the solution is to use reactivity on a duplicated collection :
You set your main collection on server side only.
You create another collection on the client side that will be a duplicate collection but used only for display
Then you pusblish the main collection to the client.
On the client side, you set all required observer on it that will replicate all modification on the duplicated collection. But this way you can manage animation or any other wished features. All actions on client side will do call on server-side but it won't affect immediatly the templates because the templates only use the duplicated collections.
I hope it will help you.
OK, so I threw this demo together in 5 mins, works though.
Here's the demo: http://keno-test.meteor.com/
Of course it needs LOT's more work, but the delayed thing works.
HTML:
<head>
<title>keno-test</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
<input id="callCardThing" type="button" value="Start card-thing" />
<h1>Here are the cards!</h1>
<ul>
{{#each cards}}
<li>{{value}}</li>
{{/each}}
</ul>
</template>
JS:
Cards = new Meteor.Collection('cards');
if (Meteor.isClient) {
Deps.autorun(function () {
Meteor.subscribe("cards");
});
Template.hello.events({
"click #callCardThing": function (event) {
Meteor.call("doCardThingOnServer");
}
});
Template.hello.helpers({
cards: function () {
return Cards.find({});
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
Meteor.publish("cards", function () {
return Cards.find({});
});
});
Meteor.methods({
doCardThingOnServer: function () {
// I remove all the cards every time just for the demo…
Cards.remove({});
var numberOfcards = 10;
var counter = Meteor.setInterval(function () {
Cards.insert({value: 'whatever! no: '+numberOfcards });
numberOfcards--;
if (numberOfcards < 1) Meteor.clearInterval(counter);
}, 1500);
}
});
}
Ok, so how about conditional {{balance}} rendering?
var shouldRender = false;
setTimeout(function () {
shouldRender = true;
}, 2000);
Template.template_name.shouldRender = function () {
return shouldRender;
}
{{#if shouldRender}}
{{>balance}}
{{/if}}
Have a look at the Atmosphere animation package : https://atmosphere.meteor.com/package/animation!
I've just done this package to explore one way of doing animation on database reactivity.
You have to register a cursor and the template to animate. There is a project that will show you how to do that.
I hope it will help you.

Resources