How to call something after DOM changed in meteor? - meteor

Template.onRendered :
Callbacks added with this method are called once when an instance of Template.myTemplate is rendered into DOM nodes and put into the document for the first time.
Is there any way to call something after the DOM changed?
For example, a navbar(header) with login/logout button is rendered after user open the homepage, and the navbar is changed after the user logged in(logout button).
I need to do something after the navbar has been changed.

You can refactor your code by creating new templates to introduce finer grained lifecycle events on child templates.
HTML
<template name="navbar">
<div class="navbar">
{{#if currentUser}}
{{> logoutButton}}
{{else}}
{{> loginButton}}
{{/if}}
</div>
</template>
<template name="loginButton">
<button class="login">Login</button>
</template>
<template name="logoutButton">
<button class="logout">Logout</button>
</template>
JS
Template.loginButton.onRendered(function(){
// will print Login after user logged out
console.log(this.$("button").text());
});
Template.logoutButton.onRendered(function(){
// will print Logout after user logged in
console.log(this.$("button").text());
});
Alternatively, you can use an autorun inside Template.navbar.onRendered to listen to user login/logout and perform actions after DOM has been modified using Tracker.afterFlush.
HTML
<template name="navbar">
<div class="navbar">
{{#if currentUser}}
<button class="logout">Logout</button>
{{else}}
<button class="login">Login</button>
{{/if}}
</div>
</template>
JS
Template.navbar.onRendered(function(){
// declare a new reactive computation
// (rerun when the reactive data source is modified)
this.autorun(function(){
// listen to the same reactive data source as in the template helper
// => currentUser
var user=Meteor.user();
// this code will run after every other reactive computations depending
// on the same data source being modified
Tracker.afterFlush(function(){
// will print Logout after user logged in
// will print Login after user logged out
console.log(this.$("button").text());
}.bind(this));
}.bind(this));
});

Related

Meteor: template helpers firing on page back

I have a helper for a 'userLayout' template, in which I've defined a current user function. It works fine when I navigate to the template (i'm using flow router with subscriptions in the route). However, when I press 'back' in the browser, the helper fires again.
Why is the helper firing for the template I'm leaving when I press 'back'?
My code for one of the offending helpers is here - I suspect the 'context' attribute:
// Return a specific user
findUser: function(context) {
var id = context.user;
return Potential_users.find({_id: id});
}
EDIT
The view template itself looks like this:
<template name="userLayoutView">
{{#each findUser context}}
...
{{/each}}
</template>
And that view is getting called dynamically like so:
<template name="userLayout">
<div class="row">
<div class="col-xs-12">
{{#if currentUser context}}
{{> userLayoutEditCreate context=context}}
{{else}}
{{> userLayoutView context=context}}
{{/if}}
</div>
</div>
</template>

Data available ("this") from a template event handler

projects.html
{{#if projects}}
{{#each projects}}
<div class="project-item">
<div class="project-name">
{{name}}
</div>
<div class="project-settings">
<span class="rename">Rename</span>
<span class="edit">Edit</span>
<span class="delete">
<!-- Here -->
</span>
</div>
</div>
{{/each}}
{{/if}}
projects.js
Template.Projects.events({
"click .project-item .delete": function (e, template) {
e.preventDefault();
debugger
// "this" refers to the specific project
}
});
In an event handler, I noticed "this" conveniently refers to a specific object inside the template where the event is related to. For example, in this case, the delete button is inside each projects block, and the handler for the delete button has this = some project. This is convenient, but I'd like to know the scopes and rules more completely. Can someone explain in briefly and point me to the right document?
This is a data context sensitive feature. Basically, there is a lexical scope in spacebars helpers. Have a look at this: http://devblog.me/no-data-context.html
The original pull request is here: https://github.com/meteor/meteor/pull/3560

Do I need to Reload the Site in Meteor?

I have this Template:
<template name="body">
{{#if key}}
{{> mite}}
{{else}}
{{> settings}}
{{/if}}
</template>
and
<template name="settings">
<h1>The settings</h1>
<form class="form-inline">
<input id='apiKey' type='text' name='apiKey' placeholder='your API-Key'>
<button id='saveSettings' type='submit' class='btn'>save</button>
</form>
</template>
<template name="mite">
<div>
<h3>...here with key</h3>
<p>
<a id="optout" href="#">not your key?</a>
</p>
</div>
</template>
When I show the settings-form where the user can set the key needed to show the 'mite' template. Now when i 'submit' the form the page get reloaded and the 'mite' template is shown.
On the mite template I'd like to have that link 'not your key?' or something that deletes the key and then shows the settings-form again. It works with a reload... but can't I do this without all the reloading in Meteor? How can i 'call' the template part with the #if in the body template?
-- Renato
You need to bind an event handler to your form and use preventDefault() to stop it submitting. e.g
client side js
Template.settings.events({
'submit':function(event,template) {
event.preventDefault();
var apiKey = template.find('input[name=apiKey]').value;
//..rest of logic to handle submit event
Session.set("key",true);
}
});
You can then use a template helper with Session.get("showthistemplate") to decide whether to show another template or not: (this is a universal helper since you're putting it in and not a template:
Handlebars.registerHelper('key',function() {
return Session.set("key",true);
});

Content wrapped in currentUser re-rendering when user updated

I'm using Meteor and having an issue where my content is being re-rendered when I don't want it to.
I have my main content wrapped in a currentUser if statement which I feel is fairly standard.
{{#if currentUser}}
{{> content}}
{{/if}}
The problem with this is my content template is being re-rendered when I update my user object. Is there any way around this? I don't reference users anywhere inside the content template.
Thank you!
Here's a sample app to replicate my problem:
HTML
<head>
<title>Render Test</title>
</head>
<body>
{{loginButtons}}
{{> userUpdate}}
{{#if currentUser}}
{{> content}}
{{/if}}
</body>
<template name="userUpdate">
<p>
<input id="updateUser" type="button" value="Update User Value" />
User last update: <span id="lastUpdated">{{lastUpdated}}</span>
</p>
</template>
<template name="content">
<p>Render count: <span id="renderCount"></span></p>
</template>
JavaScript
if (Meteor.isClient) {
Meteor.startup(function() {
Session.set("contentRenderedCount", 0);
});
Template.content.rendered = function() {
var renderCount = Session.get("contentRenderedCount") + 1;
Session.set("contentRenderedCount", renderCount);
document.getElementById("renderCount").innerText = renderCount;
};
Template.userUpdate.events = {
"click #updateUser": function() {
Meteor.users.update({_id: Meteor.userId()}, {$set: {lastActive: new Date()}});
}
};
Template.userUpdate.lastUpdated = function() {
return Meteor.user().lastActive;
};
}
if (Meteor.isServer) {
Meteor.users.allow({
'update': function () {
return true;
}
});
}
Update:
I should've explained this example a little. After creating a user, clicking the Update User Value button, causes the render count to increment. This is because it's wrapped in a {{#if currentUser}}. If this is if is removed, you'll notice the render count remains at 1.
Also, you'll need to add the accounts-ui and accounts-password packages to your project.
Meteor will re-render any template containing reactive variables that are altered. In your case the {{currentUser}} is Meteor.user() which is an object containing the user's data. When you update the users profile, the object changes and it tells meteor to re-calculate everything reactive involving the object.
We could alter the reactivity a bit so it only reacts to changes in whether the user logs in/out and not anything within the object itself:
Meteor.autorun(function() {
Session.set("meteor_loggedin",!!Meteor.user());
});
Handlebars.registerHelper('session',function(input){
return Session.get(input);
});
Your html
{{#if session "meteor_loggedin"}}
{{> content}}
{{/if}}

meteor : setting fields post opening dialog

I'm trying to complete the parties demo
with an "edit party" feature
I understood the create Dialog opens upon setting Session showCreateDialog
{{#if showCreateDialog}}
{{> createDialog}}
{{/if}}
this shows the popin
but I want to set to fields post opening
and I don't see how to act after the opening action ?
You can set manipulate the DOM inside the Template's rendered event. But if you find yourself writing lots of glue code here ($("#someInput").val("someVal")) then watch out because you're likely on the wrong track!
Template.createDialog.rendered = function() {
// you can manipulate the DOM here
}
Remember, you can bind field values to instances, so something like the below will auto-bind your object
<template name="editDialog">
{{#with party}}
<input type="text" id="myPartyName" value="{{name}}" />
...
{{/with}}
</template>
Template.editDialog.party = function() {
return Parties.findOne(Session.get("selectedParty"));
};

Resources