Helpers in templates - javascript

Seeing if I can insert a helper into a template. Seems like I'm doing something wrong because you can't actually click the buttons in my program.
HTML:
<head>
<title>BubblePopper</title>
</head>
<body>
{{loginButtons}}
{{>home}}
</body>
<template name ="grid">
<div id="container">
{{#each buttons}}
<button class="button" type="button"></button>
{{/each}}
</div>
</template>
<template name = "home">
<h1> Welcome to bubblePopper</h1>
<p> Current Game Status:
//this is the part I'm confused about is it ok to insert the grid helper into this
//if statement in the template?
{{#if game.active}}
Game in progress. Finish Game<br>
<center>{{>grid}}</center>
{{else}}
{{#if game.finished}}
Game done or another player left.Start a new game
{{else}}
Waiting for a new player to login. Find a new game
{{/if}}
{{/if}}
</p>
<p><strong> Current Game ID: </strong>{{game._id}}</p>
<p><strong> Current Game Player Count: </strong>{{game.players.length}}</p>
<p><strong> Current Game Active: </strong>{{game.active}}</p>
</template>
Client.JS:
if (Meteor.isClient) {
Deps.autorun(function(){
gameSubscription = Meteor.subscribe("myGames",Meteor.userId());
});
Template.home.game = function(){
return gameCollection.findOne({current: true});
};
Template.home.events({
"click #newGame" : function() {
Meteor.call('newGame')
},
"click #finishGame": function(){
var game = gameCollection.findOne({current: true});
Meteor.call('finishGame', game._id);
}
});
//was confused if I should just include buttons as a part of homes template
Template.grid.buttons = function (){
var list = [];
for(var i=1; i<=64; i++){
list.push({value: i});
}
return list;
}
Template.button.events({
'click .button': function(ev) {
$(ev.target).css('visibility', 'hidden');
console.log("pop")
}
});
}
I didn't include the server.js because it has nothing to do with any of the template helper relationship. Is this the right way to insert the grid? Also is it an error in my HTML that isn't allowing the buttons to be clicked?

Looks like you've mixed up the name of your template. If it's the buttons in the grid template you're referring to, you need to change the line Template.button.events({ to Template.grid.events({ and then you should see the function being executed when a button is clicked.

Related

Changing template on Link Click Meteor

There is a button :
<a id = "login" class="waves-effect white blue-text text-darken-2 btn"><i class="fa fa-pencil"></i> Write</a>
and there are two templates, firstpage, and secondpage.
Now, I want Meteor to change templates when I click that link, and render the second page.
How can I do this?
I used aldeed:template-extension but it doesnt work.
You can use some helpers here, and template variables.
Lets say you have this template.
<template name="example">
{{#if clicked}}
{{> firstPage}} <!-- the firstPage template will be rendered if the var is == false -->
{{else}}
{{> secondPage}} <!-- this will be render when the session is equals to true -->
{{/if}}
</template>
Now the Javascript.
First on the onCreate function declare the default values (similar so Session.setDefault)
Template.example.onCreated( function(){
var self = this;
self.vars = new ReactiveDict();
self.vars.setDefault( 'clicked' , true ); //default is true
});
Now the event to change the state of the clicked to false (Similar to Session get/set).
Template.example.events({
'click .waves-effect':function(event,template){
var instance = Template.instance();
instance.vars.set('clicked', false) //set to true.
}
})
Now we something to listen our variables changes, lets use a helper
Template.example.helpers({
clicked:function(){
var instance = Template.instance(); //http://docs.meteor.com/#/full/template_instance
return instance.vars.get('clicked') //this will return false(default) | true
}
})
NOTE Here we are using reactive-dict wich share the same syntaxis of a Session.
meteor add reactive-dict
Here is the MeteorPad Example

Is there a way to insert between Meteor templates?

I am trying to insert advertising blocks in a Meteor list template. The code will show it more easily than I can describe:
// dataList.js
Template.dataList.helpers({
dataList : function() {
return DataList.find();
});
// dataList.html
<template name="dataList">
{{#each dataList}}
<div class="col-xs-3">
{{name}} (and other data)
</div>
{{/each}}
</template>
The result I want is something like this
<div class="col-xs-3">
Jon Snow
</div>
<div class="col-xs-3"> <----This inserted programmatically
<div id="ad">
Buy a Destrier - 5 Golden Stags
</div>
</div>
<div class="col-xs-3">
Tyrion Lannister
</div>
<div class="col-xs-3">
The Hound
</div>
The effect is similar to the advertising found on Foodgawker.com. I can't work out how to insert the ads programmatically at random intervals.
This is on an infinite scroll and will need to add the Ad several times.
The previous answers are too complex ; just update your helper :
dataList : function() {
var d = _.map( DataList.find().fetch(), function(v) { return {value: v}; });
return _.union( d.slice(0,2), {
type: 'ad',
value: 'Buy a Destrier - 5 Golden Stags'
} ,d.slice(2));
});
So, dataList will have the ad inserted in third position.
You can randomly choose an _id from your list and just display the ad after that doc in the template.
Like this:
//.js
Template.dataList.created = function(){
var ids = DataList.find().map( function( doc ){ return doc._id });
this.showAdId = Random.choice( ids ); //need to add 'random' package of meteor core
};
Template.dataList.helpers({
dataList: function(){
return DataList.find({},{transform: function( doc ){
doc.showAd = doc._id === Template.instance().showAdId;
return doc;
});
}
});
//.html
<template name="dataList">
{{#each dataList}}
<div class="col-xs-3">
{{name}} (and other data)
</div>
{{#if showAd}} <!-- check new field to see whether this is the doc with an ad -->
<div class="col-xs-3">
<div id="ad">
Buy a Destrier - 5 Golden Stags
</div>
</div>
{{/if}}
{{/each}}
</template>
New Idea
// client side collection, not synchronized with server side...
DataListWithAds= new Mongo.Collection(null);
Template.DataList.destroyed= function(){
this.observer.stop();
}
Template.DataList.created= function(){
// DataList is collection anywhere
// get handler of observer to remove it later...
this.observer = DataList.find().observe({
// observe if any new document is added
// READ : http://docs.meteor.com/#/full/observe
addedAt:function(document, atIndex, before){
console.log(atIndex, document, self);
// immediately insert original document to DataListWithAds collection
DataListWithAds.insert(document);
// if some condition is true, then add ad.
if(atIndex % 5 == 1){
DataListWithAds.insert({
name:"AD", score:document.score, isAd:true
});
}
}
})
}
// use local collection to display items together with ads
Template.dataList.helpers({
dataList : function() {
return DataListWithAds.find();
});
})
Also template should be tweaked a little bit:
<template name="dataList">
{{#each dataList}}
{{#if isAd}}
ADVERTISEMENT
{{else}}
<div class="col-xs-3">
{{name}} (and other data)
</div>
{{/if}}
{{/each}}
</template>
Proof of concept can be found here:
http://meteorpad.com/pad/SoJe9dgp644HKQ7TE/Leaderboard
I think there are still things to resolve, like:
- How to make this working together with sorting ?
Old Idea
Below is an idea how you can achieve result you want.
The approach is that client side waits until your list is rendered (happens only once) and then injects an ad at position of your choice.
Assumptions:
your ad is defined in template adsTemplate
items are in div : #listContainer
Code:
Template.dataList.rendered = function(){
// position after which ads should be added
// you can generate random number
var adsPosition = 10;
// inject HTML from template **Template.adsTemplate**
// with data **adsData**
// to HTML element #listContainer
// after HTML element at position adsPosition
Blaze.renderWithData(
Template.adsTemplate,
adsData,
this.find('#listContainer'),
this.find('#listContainer .col-xs-3:nth-child('+adsPosition+')')
)
}
If you want to inject many ads, because list should be infinite , then it is good idea to use Tracker.autorun to detect new data and inject accordingly.

meteor: render a template in a specific context

I have two templates
<body>
{{>tmpl1}}
{{>tmpl2}}
....
</body>
in the tmpl1 I have a list of items, which can be clicked. When one is click, tmpl2 shown the details. How can this be achieved ?
So, just to make the idea clearer, here is how I get the list of items
Template.tmpl1.items = function () {
return Items.find({}).fetch();
};
tmpl1 displays them as follows
<template name="tmpl1">
{{#each items}}
{{title}}
{{/each}}
....
</template>
So tmpl2 template might look like this
<template name="tmpl1">
<h1>{{title}}</h1>
<p>{{content}}</p>
</template>
Any suggestions how to link the selected item in tmpl1 with tmpl2 ?
First, put your templates in a container so that you can manipulate the context. Also, put the details template in a #with context:
<template name="box">
{{> list}}
{{#with item}}
{{> details}}
{{/with}}
</template>
Now, add the event handlers for the box template. Assuming your entries in the list looks like this:
<div class="listItem" data-id="{{_id}}">{{title}}</div>
Write the handler:
Template.box.events({
'click .listItem': function(e, t) {
t.data.itemId = $(e.target).data('id');
t.data.itemDep.changed();
}
});
Finally, create the data helper and dependency for the selected item:
Template.box.created = function() {
this.data.itemDep = new Deps.Dependency();
};
Template.box.item = function() {
this.itemDep.depend();
return Items.findOne(this.itemId);
};

Meteor.publish/Subscribe issue

I need to display data to all clients using Meteor.publish/subscribe.I did one sample example in that example what i am did is 10 records are inserted at that time of server startup and these 10 records are trying to display to all clients.The Problem is The data doesn't shows the clients.I didn't have any idea because i am new to meteor JS.So Please see the below code and suggest me how to do?
HTML Code :
<head>
<title>DataApp</title>
</head>
<body>
{{> main}}
</body>
<template name="main">
{{#each messages}}
{{messages}}
{{/each}}
</template>
ANd JS Code is :
Messages = new Meteor.Collection("messages");
if (Meteor.isClient)
{
Meteor.subscribe('messages');
}
if (Meteor.isServer)
{
Meteor.startup(function ()
{
// code to run on server at startup
for(var i = 0 ; i <= 10 ; i++ )
{
console.log("EXE"+i);
Messages.insert({ name: "EXE"+i });
}
});
Meteor.publish('messages', function() {
return Messages.find();
});
}
Firstly, you are missing a template helper on your template main, so make your Meteor.isClient look like this:
if (Meteor.isClient) {
Meteor.subscribe('messages');
// this is the template helper that is referenced by {{messages}}
// in your template 'main'
Template.main.messages = function () {
return Messages.find();
};
}
Secondly, the handlebars in your html don't make sense, try this instead:
<template name="main">
<!-- inserts value of property 'name' of each object in 'messages' -->
{{#each messages}}
{{name}}
{{/each}}
</template>

jScrollPane not working with meteor.js

I'm trying to use jScrollPane with meteor.js, but it's not behaving as expected. First off, if I give the '.scroll-pane' class to a div, it will work without being initialized explicitly by me. But when I try to initialize it explicitly, it does not work. Is this some sort of meteor magic? or I am missing something obvious.
Second, if I don't initialize it, but I try to access it...it's data is empty, so I can't use the api on it. I've included some sample code below, please let me know if I am doing something wrong.
html
...
<div class="rectangle">
<div class="chat scroll-pane" id="chatWindow">
{{#each Messages}}
{{#if Compare uId UID}}
<div class="bubble me">{{Text}}</div>
{{else}}
<div class="bubble you">{{Text}}</div>
{{/if}}
{{/each}}
</div>
<input class="textinput" type="text" placeholder="Insert Message" id="textToSubmit">
<button class="btn btn-success" id="submit" autofocus="autofocus">Send
<br>
<br>
</button>
</div>
js
if (Meteor.isClient) {
...
var Message = new Meteor.Collection("Message");
Template.Message.rendered = function(){
if(!this._rendered) {
this._rendered = true;
var scroll = $(this.find("#chatWindow"));
var api = scroll.data('jsp');
console.log(api);
}
};
...
}
If you need any more info, please let me know.
Thanks
There are a couple things going on:
(1) you need to wire up the reactivity to your template to ensure that the timing of the incoming Messages is correct. This is achieved by using a Session variable to set the load, and by setting Template.scroll.Messages equal to a function that returns a collection cursor. Typically, you would not need to set a Session this way if your Template.scroll.Messages returns a query that uses a Session object (i.e., a roomId). If that were the case, you could forgo the callback on the Meteor.subscribe call and the "loaded" Session altogether.
(2) you'll want to turn off autopublish (meteor remove autopublish) and explicitly subscribe to the data so you know when your collection loads;
(3) you must declare your messages Collection outside the isClient block to ensure the server knows about it.
HTML:
<template name="scroll">
<div class="rectangle">
<div style="height:100px;overflow:auto;" id="chatWindow">
{{#each Messages}}
{{#if isMe this}}
<div class="bubble me">{{text}}</div>
{{else}}
<div class="bubble you">{{text}}</div>
{{/if}}
{{/each}}
</div>
</div>
</template>
JS:
Messages = new Meteor.Collection("messages");
if (Meteor.isClient) {
Meteor.subscribe("messages", function () {
Session.set("loaded", true);
});
Template.scroll.rendered = function () {
if (Session.get("loaded")) {
$(this.find("#chatWindow")).jScrollPane();
var api = $(this.find("#chatWindow")).data("jsp");
...
}
};
Template.scroll.helpers({
isMe: function (msg) {
return msg.userName === 'me';
}
});
Template.scroll.Messages = function () {
return Messages.find({});
};
}
if (Meteor.isServer) {
Meteor.startup(function () {
if (Messages.find().count() === 0) {
Messages.insert({ text: 'Message 1', userName: 'me' });
Messages.insert({ text: 'Message 2', userName: 'you' });
Messages.insert({ text: 'Message 3', userName: 'me' });
}
});
Meteor.publish("messages", function () { return Messages.find({}); });
}

Categories