Refer to the following code:
Template.EditProfile.helpers({
'welcomeMessage': function() {
if (Session.get("welcomeMessage")) {
var welcomeMessage = Session.get("welcomeMessage");
delete Session.keys['welcomeMessage']
return welcomeMessage;
} else {
return false;
}
}
})
In html,
{{#if welcomeMessage}}
<div class="change-explain red-msg">{{ welcomeMessage }}</div>
{{else}}
<div class="change-explain">People will see your username on your comments.</div>
{{/if}}
This always renders nothing. If I take that outside if statements, it works fine. What am I missing here about reactive style?
In Meteor the Session variable itself is reactive, meaning that when you change the value of the "welcomeMessage" session variable, your "welcomeMessage" helper will run again and not display anything. To give it a test, comment out the delete Session.keys['welcomeMessage'] and while your app is running, set the session manually in the console by typing something like Session.set('welcomeMessage', "test") and then Session.set('welcomeMessage')
If you wanted to set the class based off that session variable you could add a helper like:
'getClass': function() {
var className = "";
if (Session.get("welcomeMessage"))
className = 'red-msg';
return className;
}
and a message helper like:
'getMessage': function() {
var messsage = "People will see your username on your comments.";
if (Session.get("welcomeMessage"))
messsage = Session.get("welcomeMessage");
return messsage;
}
and then your html would look like:
<div class="change-explain {{getClass}}">{{getMessage}}</div>
Related
i want to refresh/reload a part of my template after a variable change so that if the variable is true it shows a content A or else it will show content B. I'm sure this is a quite simple question but i'm having troubles on finding the solution.
Something like this:
Template.x.created = function() {
this.variable = false;
}
Template.x.helpers({
'getValue': function(){
return this.variable;
}
});
Template:
<template name="x">
{{#if getValue}}
<content A>
{{else}}
<content B>
{{/if}}
</template>
You need to create a reactive data source to get the template helper to re-run when the variable changes, as a normal variable won't let the helper know when it changes value. The simplest solution is to use ReactiveVar:
Template.x.onCreated(function() {
this.variable = new ReactiveVar(false);
});
Template.x.helpers({
'getValue': function() {
// Note that 'this' inside a template helper may not refer to the template instance
return Template.instance().variable.get();
}
});
If you need to access the value somewhere outside this template, you can use Session as an alternative reactive data source.
#Waiski answer is a good one, but I want to share a simple Template helper I build because a lot of Templates need this:
Using registerHelper you can build a global helper like so:
Template.registerHelper('get', function (key) {
let obj = Template.instance()[key]
return (obj && obj.get) ? obj.get() : obj
})
Use it in every template:
Template.x.onCreated(function() {
this.foo = new ReactiveVar(true)
this.bar = new ReactiveVar('abc')
})
Html:
{{#let foo=(get 'foo')}}
{{#if get 'bar'}}
Bar is true. Foo: {{foo}}
{{/if}}
{{/let}}
When I try to use this.findAll on a template where the selector is in a sub-template, findAll returns nothing.
Here's the HTML:
<template name="products">
{{#each productList}}
{{> product }}
{{/each}}
</template>
<template name="product">
<div class="box">{{name}}</div>
</template>
Here's the JS:
Template.products.helpers({
productList: function() {
var all = Products.find({}).fetch();
return all;
}
});
Template.products.rendered = function(){
var boxes = this.findAll('.box');
console.log(boxes.length);
}
Output of boxes.length is 0. Any ideas how I could get the "box" elements?
According to the docs for findAll:
Only elements inside the template and its sub-templates can match parts of the selector.
So it should work for sub-templates. I tried this with a fixed array of products and it worked, which implies that you are just seeing a delay between the call to rendered and the products being fetched. For example if you do:
Template.products.events({
'click .box': function (e, t) {
var boxes = t.findAll('.box');
console.log(boxes.length);
}
});
Then if you click on one of the boxes, you should see the correct number logged to the console. In short, I think the test may just be invalid. If you are using iron-router, you could try adding a waitOn for the products - that may ensure they arrive before the rendered call.
Here's what I did to run a script after all products have been loaded.
I've added last_product property in all the products.
Template.products.helpers({
productList: function() {
var all = Products.find({}).fetch();
var total = all.length;
var ctr = 0;
all.forEach(function(doc){
doc.last_product = false;
ctr++;
if(ctr == total)
{
doc.last_product = true;
}
return doc;
});
return all;
}
});
Then instead of "Template.products", I used "Template.product" to detect if the last product is rendered. When the last product is rendered, run the script.
Template.product.rendered = function(){
if(this.data.last_product){
var boxes = $('.pbox');
console.log(boxes.length);
}
}
boxes.length now has the correct length.
Thanks to David for the idea!
Here's the correct answer. I've added this to my iron-router route:
action : function () {
if (this.ready()) {
this.render();
}
}
Found the answer from https://stackoverflow.com/a/23576039/130237 while I was trying to solve a different problem.
I want to include a template (or use a helper, I don't care) that could be clicked to edit in place. This view MUST be reusable, and so can't rely on the Session variable or any other variables that aren't contained by the view instance.
In the display mode it would look like this:
<div class="editable">{{content}}</div>
which would change to the edit mode when you clicked on it, which would look like this:
<input type="text" value="{{content}}" />
and you could revert back to display mode (persisting it's changes appropriately) by either hitting enter or pressing a button.
It seems meteor doesn't make this incredibly easy, since my first attempts with html:
<template name="editable">
{{#if editing}}
<input type="text" value={{this}} />
{{else}}
<div class="edit-thing">{{this}}</div>
{{/if}}
</template>
// In the appropriate display template.
{{> editable stuff}}
and js:
Template.user.stuff = "yo yo yo";
Template.editable.events({
'click .edit-thing': function(e) {
this.isEditing = true;
}
});
Template.editable.helpers({
editing: function() {
return !!this.isEditing;
}
});
have had problems with not being reactive, which using the Deps library didn't solve. (This version just wouldn't change when you clicked it, since this.isEditing isn't reactive and doesn't trigger a change in the editing helper.)
Ask for more information if you like! Thanks!
That's a typical use case for Deps, did you remember to use both depend and changed? The js code may look like this:
Template.editable.created = function() {
this.data.isEditing = false;
this.data.isEditingDep = new Deps.Dependency();
};
Template.editable.events({
'... whatever to start edit mode ...': function(e, t) {
t.data.isEditing = true;
t.data.isEditingDep.changed();
},
'... whatever to close edit mode ...': function(e, t) {
t.data.isEditing = false;
t.data.isEditingDep.changed();
},
});
Template.editable.editing = function() {
this.isEditingDep.depend();
return this.isEditing;
};
Ok so I'm not sure why I can't render the code. First if I console.log users.content I get the content I want but I'm some how not able to pass it to a textarea so that it show's it...
Users = new Meteor.Collection("users");
if(Meteor.is_client){
Template.inputUser.code = function(){
var el = Users.find({name:"oscar"});
el.forEach(function(users){
console.log(users.content);
})
}
}
And then on my html template I have
<body>{{> inputUser}}</body>
<template name="inputUser">
<textarea>{{content}}</textarea>
</template>
And I would have a record on the db suck as so
if(Meteor.is_server)
Users.insert({name:"oscar",content:"hello world"})
Thanks for your help guys.
Firstly your method Template.inputUser.code should return something, you should also note that it wouldn't be called with that template either as it needs a {{code}} call in it rather than {{content}}
The second point is database contents are not always available if you have disabled the autopublish package, if so check out using publish(in the server code) and subscribe(in the client code): http://docs.meteor.com/#meteor_subscribe you can use this to check when the client has all the data to display. Something like:
Meteor.subscribe('allusers', function() {
Template.inputUser.code = function(){
var user = Users.findOne({name:"oscar"});
return user.content;
}
});
...
Meteor.publish('allusers', function() {
return Users.find();
});
Ok so I've got my template in its own file named myApp.html. My template code is as follows
<template name="initialInsertion">
<div class="greeting">Hello there, {{first}} {{last}}!</div>
</template>
Now I want to insert this template into the DOM upon clicking a button. I've got my button rendered in the DOM and I have a click event tied to it as follows
Template.chooseWhatToDo.events = {
'click .zaButton':function(){
Meteor.ui.render(function () {
$("body").append(Template.initialInsertion({first: "Alyssa", last: "Hacker"}));
})
}
}
Now obviously the $("body").append part is wrong but returning Template.initialInsertion... doesn't insert that template into the DOM. I've tried putting a partia {{> initialInsertion}}but that just errors out because I dont have first and last set yet... any clues?
Thanks guys
In meteor 1.x
'click .zaButton':function(){
Blaze.renderWithData(Template.someTemplate, {my: "data"}, $("#parrent-node")[0])
}
In meteor 0.8.3
'click .zaButton':function(){
var t = UI.renderWithData(Template.someTemplate, {my: "data"})
UI.insert(t, $(".some-parrent-to-append"))
}
Is first and last going into a Meteor.Collection eventually?
If not, the simplest way I know is to put the data into the session:
Template.chooseWhatToDo.events = {
'click .zaButton' : function () {
Session.set('first', 'Alyssa');
Session.set('last', 'Hacker');
}
}
Then you would define:
Template.initialInsertion.first = function () {
return Session.get('first');
}
Template.initialInsertion.last = function () {
return Session.get('last');
}
Template.initialInsertion.has_name = function () {
return Template.initialInsertion.first() && Template.initialInsertion.last();
}
Finally, adjust your .html template like this:
<template name="initialInsertion">
{{#if has_name}}
<div class="greeting">Hello there, {{first}} {{last}}!</div>
{{/if}}
</template>
This is the exact opposite solution to your question, but it seems like the "Meteor way". (Basically, don't worry about manipulating the DOM yourself, just embrace the sessions, collections and template system.) BTW, I'm still new with Meteor, so if this is not the "Meteor way", someone please let me know :-)
I think you may want to use Meteor.render within your append statement. Also, note that if you are passing data into your Template, then you must wrap Template.initialInsertion in an anonymous function, since that's what Meteor.render expects. I'm doing something similar that seems to be working:
Template.chooseWhatToDo.events = {
'click .zaButton':function(){
$("body").append(Meteor.render(function() {
return Template.initialInsertion({first: "Alyssa", last: "Hacker"})
}));
}
}
Hope this helps!
Many answer here are going to have problems with the new Blaze engine. Here is a pattern that works in Meteor 0.8.0 with Blaze.
//HTML
<body>
{{>mainTemplate}}
</body>
//JS Client Initially
var current = Template.initialTemplate;
var currentDep = new Deps.Dependency;
Template.mainTemplate = function()
{
currentDep.depend();
return current;
};
function setTemplate( newTemplate )
{
current = newTemplate;
currentDep.changed();
};
//Later
setTemplate( Template.someOtherTemplate );
More info in this seccion of Meteor docs