Changing template on Link Click Meteor - javascript

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

Related

Dynamic form generalion and removal using meteor and Aldeed's autoform

I am using meteor and Aldeed's autoform to populate my database.
The basic functionality I want to achieve is this:
Have principal form linked to a collection as normal.
In the principal form, have a button add secondary which dynamically adds forms linked to a different collection.
The second form has a remove button which removes it.
I followed the technique outlined here and put together the following code :
<template name="PricipalForm">
{{#autoForm collection="principal" id="Principalform" type="insert" }}
<fieldset>
<legend>Principal form</legend>
{{> afQuickField name='field1'}}
{{> afQuickField name='field2'}}
<button id='add-inputs' type="button">
Add Proposal
</button>
{{#each inputs}}
{{> AddSecond}}
{{/each}}
</fieldset>
<button type="submit" class="btn btn-primary">Insert</button>
{{/autoForm}}
</template>
./Templates/PrincipalForm.html
//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
Template.PrincipalForm.onCreated(function() {
Session.set('props', []);
});
Template.Create.events({
'click #add-inputs': function () {
var inputs = Session.get('inputs');
var uniqid = Math.floor(Math.random() * 100000); /
inputs.push({uniqid: uniqid});
Session.set('inputs', inputs);
}
});
Template.Create.helpers({
inputs: function(){
return Session.get('inputs');
}
});
./Templates/PrincipalForm.js
////////////////////////////////////////////////////
///////////////////////////////////////////////////
<template name ="SecondaryFrom">
{{#autoForm collection="secondary" id="secondaryForm" type="insert" }}
<fieldset>
<legend> One instance of secondary form </legend>
{{> afQuickField name='fieldA' }}
{{> afQuickField name='fieldB'}}
</fieldset>
<button class='remove-inputs' uniqid="{{uniqid}}" type="button">Remove</button>
{{/autoForm}}
</template>
./Templates/SecondaryForm.html
//////////////////////////////////////////
//////////////////////////////////////////
Template.AddProposal.events({
'click .remove-inputs': function(event) {
var uniqid = $(event.currentTarget).attr('uniqid');
var props = Session.get('inputs');
props = _.filter(props, function(x) { return x.uniqid != uniqid; });
Session.set('inputs', inputs);
},
});
./Templates/SecondaryForm.js
This code works fine, there is only one bug that I do not understand:
I first add 3 secondary forms and put the values abc , efg , hij in fieldA of these three forms respectively.
Then I remove the second secondary form with efg and what I get is that the remaining ones are abc and efg !!
where it gets more weird is that when I check the uniqid of the removed form is the one expected (and corresponding to the previous efg).
So it seems that when I remove the form dynamically, the values that I type in persist somehow.
Can anyone please help out:
What goes wrong in what I am doing, how could I fix it?
Is there perhaps a better way to do what I am trying to do?
I also tried to check the answer here
, but the links provided were broken.
Thanks
I solved the issue by fully using aldeed:autoform and aldeed:collection2in order to generate my form rather than inserting the secondary template manually.
This can be achieved by creating the two wanted schemas as usual and then putting the secondary schema as an array onto the primary.
Then it is a question of using {{> afArrayfield}} in autoform to achieve the intended result outlined in the question.
The code would then look like this:
//Schema definition:
Primary = new Mongo.collection('Primary');
secondary = new SimpleSchema({
/* ... */
});
primary = new SimpleSchema({
/*...*/
secondaries:{
type:[secondary]
}
});
Primary.attachSchema(primary);
//On the HTML form:
{{autoform collection="Primary" id= "form_example" type="insert"}}
<fieldset>
/* any fields*/
{{afArrayField name = 'secondaries'}}
</fieldset>
{{/autoform}}

Meteor: Reusing a Template With a Chart-Div

I'm using a single template show which doesn't really have any dynamic elements (yet):
<template name="show">
<div class="row">
<div id="container-pie"></div>
</div>
</template>
I have a sidebar, which pulls from my database and creates <a> tags.
My rendered callback has the code which draws a piegraph:
Template.show.rendered = function() {
var datum = this.data.visitors;
var keys = Object.keys(datum);
newAndReturningPie(keys[0], keys[1], datum.new, datum.returning);
}
(this data is being grabbed from iron:router).
Now here's where I need help (note the sidebar template for reference:)
<template name="sidebar">
<div class="ui left demo vertical inverted labeled sidebar menu">
<a class="item" href="{{pathFor 'root'}}">
<i class="home icon"></i>Home
</a>
{{#if anyProperties}}
{{#each this}}
<a href="{{pathFor 'property.show'}}" class="item">
{{name}}
</a>
{{/each}}
{{else}}
<a class="item">
<i class="settings icon"></i>No properties
</a>
{{/if}}
</div>
</template>
I'm using the same template for every object in the #each block. The problem is that the rendered callback captures the data for the first object, and switching context to another one doesn't actually reload or refresh the page.
If I was using just some HTML with {{objectProperty}}, then I know it would dynamically update. But I'm not using any Spacebars tags, just a single div which contains the graph.
What is a simple, elegant way to have the show template re-render/reload after clicking on an <a> tag in the sidebar?
UPDATE: Thanks to #tarmes
Here's my WORKING code:
Template.show.rendered = function() {
var self = this;
controller = Router.current();
self.autorun(function(){
var params = controller.getParams();
Meteor.call('selectedProperty', params._id, function(err, res){
if (!err) {
var datum = res.visitors;
var keys = Object.keys(datum);
newAndReturningPie(keys[0], keys[1], datum.new, datum.returning);
}
});
});
}
All I did was add a Meteor method to query the DB for the object via ID.
How about something like this:
Template.show.rendered = function() {
self = this;
controller = Router.current();
self.autorun(function() {
var params = controller.getParams(); // Reactive
var datum = self.data.visitors;
var keys = Object.keys(datum);
newAndReturningPie(keys[0], keys[1], datum.new, datum.returning);
});
}
The autorun will rerun everytime the controller's parameters change (which I assume they will do if there's an _id etc. in the route), thus forcing an update.

Meteor.js mplement a singleton confirm box

I try to create a reusable confirm box, but i'm unsure how to implement this the meteor way.
I have a template for the confirm box. The text and button value should be dynamic.
<template name="confirm">
{{#if show}}
{{text}}
<button class="cancel">cancel</button>
<button class="confirm">{{action}}</button>
{{/if}}
</template>
And i have a user template with an delete button.
<template name="user">
<h1>{{name}}</h1>
<button class="delete">delete user</button>
</template>
In the app template i display a list of users and render the confirm template.
<template app="app">
{{#each user}}
{{> user}}
{{/each}}
{{> confirm}}
</tempalte>
Now when i click the delete button of a user item i want to display the confirm box.
Template.confirm.helpers({
text: function(){
return Session.get('confirmText');
},
action: function(){
return Session.get('confirmAction');
},
show: function(){
return Session.get('showConfirm');
},
});
Template.user.events({
'click .delete': function(){
Session.set('confirmAction', 'delete');
Session.set('confirmText', 'Are you sure?');
Session.set('showConfirm', true);
}
});
My confirm box displays as it should but how can i trigger the user deletion from the confirm box?
Am i even on the right track? I tried to render a confirm template inside of every user template but there should only be one active confirm box at a time.
You can certainly make it work using this pattern. The only addition you need to make is to set the user id you intend to delete in the Session, so it is accessible to your deletion method:
Template.user.events({
'click .delete': function(){
Session.set('confirmAction', 'delete');
Session.set('confirmText', 'Are you sure?');
Session.set('showConfirm', true);
/* addition - this._id refers to the id of the user in this template instance */
Session.set('userToDelete', this._id);
}
});
And then:
Template.confirm.events({
"click button.confirm": function(){
Meteor.call(
"deleteUser",
Session.get("userToDelete"),
function(error, result){
Session.set("userToDelete", null);
}
);
}
});
However a more flexible and extensible pattern would be to get and set the user you are confirming deletion of internal to the user template, using a ReactiveVar or ReactiveDict attached to the template instance. That way you are not loading up the global Session object with keys that really only involve one piece of behavior. And you could reuse your confirm template in other, unrelated contexts.
UPDATE
Here is a way to reuse a confirm button across contexts with a private reactive-var. To see if another confirmation box is open you can first check a Session property.
Session.setDefault("confirming", false);
The text and action properties in the confirm template are set from its user parent:
<template app="app">
{{#each user}}
{{> user}}
{{/each}}
</template>
<template name="user">
<h1>{{name}}</h1>
<button class="delete">delete user</button>
{{#if show}}
{{> confirm text=text action=action}}
{{/if}}
</template>
<template name="confirm">
{{text}}
<button class="cancel">cancel</button>
<button class="confirm">{{action}}</button>
</template>
And we set the helpers and events for it in the user parent as well:
Template.user.created = function(){
this.show = new ReactiveVar(false);
}
Template.user.helpers({
name: function(){
return this.name;
},
show: function(){
return Template.instance().show.get();
},
text: function(){
return "Are you sure?";
},
action: function(){
return "delete user";
}
});
Template.user.events({
"click button.delete": function(event, template){
if (Session.get("confirming")){
console.log("You are already confirming another deletion.");
return;
}
Session.set("confirming", true);
template.show.set(true);
},
"click button.confirm": function(event, template){
Meteor.call(
"deleteUser",
this._id,
function(error, result){
template.show.set(false);
Session.set("confirming", false);
}
)
}
});
Now you can give the confirm template a different context somewhere else based on its parent.

How to update data in Meteor using Reactivevar

I have a page with a form. In this form user can add multiple rows with key and values. There is a restriction that the customFields is created on the fly, not from any subscribed collection.
...html
<template name="main">
{{#each customFields}}
<div>
<input type="text" value="{{key}}"/>
<input type="text" style="width: 300px;" value="{{value}}"/>
</div>
{{/each}}
</template
.... router.js
Router.route 'products.add',
path: '/products/add/:_id'
data:
customFields:[]
....products.js
#using customFieldSet as Reactive Var from meteor package
Template.product.created = ->
#customFieldSet = new ReactiveVar([])
Template.product.rendered = ->
self = this
Tracker.autorun ->
arr = self.customFieldSet.get()
self.data.customFields = arr
Template.product.events(
'click .productForm__addField': (e)->
t = Template.instance()
m = t.customFieldSet.get()
console.log t
m.push(
key: ''
value: ''
)
t.customFieldSet.set m
....
The last event will be trigger when I click the button. And it add another row with key and value empty to the page.
Please advise me why I actually see the reactive variable customFieldSet updated, but there is nothing changed dynamically in html.
P/s: I guess customFields is not updated via Iron router.
Basically, you're doing the thing right. However, you shouldn't be assigning the new reactive data to your template's data context, but rather access it directly from your helpers:
Template.product.helpers({
customFileds: function () {
return Template.instance().customFiledsSet.get();
},
});
Now you can use {{customFields}} in your template code and it should work reactively. Just remember that {{this.customFileds}} or {{./customFileds}} will not work in this case.

Helpers in templates

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.

Categories