I am new to Meteor so obviously I do not know the Meteor best practice. I am trying to understand the context this in a template's helpers and events functions. What I wanted was to tie the text of a div to an input field.
Coffeescript
Template.test.helpers
text: "initial text"
Template.test.events
"keypress #input": (e) ->
this.text = e.target.value
return
But I've learned that this does not point to test. What is the right way to access helpers from events?
Also I tried Tempalte.test.text = e.target.value though the value is changing, the DOM is not being updated. Aren't helpers supposed to be reactive?
You can't set helpers that way. If you want it to be reactive the best way is using a Session variable. I don't write coffescript but here's how you can do it with javascript:
Template.test.text = function(){
return Session.get("myTextVar");
};
Template.test.events({
"keypress #input":function(e,t){
Session.set("myTexVar",e.target.value);
}
});
You could also create your own reactive variables but that's too hard for that you need.
Related
I'm looking through the Meteor simple tutorial and the way that 'this' binding in the different Template objects works seems inconsistent to me in my unknowledgeable state.
Template.body.events({
"submit .new-task": function(event) {
console.log(this); // Logs an empty object
}
})
Template.task.events({
"click .toggle-checked": function() {
console.log(this); // logs a task
}
});
I can see that task is an xml template defined in the view, which is a visual representation of the items returned by a function in the Template.body.helpers object.
I guess that the task objects are bound the html representation of each object (though I can't see how as there doesn't seem to be any identifying property within the li elements??)
Anyhow. When I click the task, this is the task. But when I submit the form, I was expecting this to be the body. Why is it not?
I was expecting Meteor to handle Template.body and Template.task in a similar way
In Meteor this referes to the data context. You define it with helpers or with the route controller ( IronRouter or FlowRouter)
Example:
{{#with myData}}
<h1>{{title}}</h1>
{{/with}}
js
Template.yourTemplate.helpers({
myData : function(){
return {
title : "My title"
}
}
})
You need to use the "event" argument
Template.task.events({
"click .toggle-checked": function( event , instance ) {
console.log( event );
}
});
The instance argument is also very useful. You have access to a jQuery selector like: instance.$() and it will only search for elements on your template and also child templates.
Personally I use the instance a lot. My Favorite pattern is:
Template.task.onCreated(function(){
this.vars = new ReactiveDict();
this.data = "some data";
});
Later if you want to access vars or data:
Events - You get this on the arguments
Helpers - var instance = Template.instance();
With instance you avoid storing states in the global namespace, like Session, and your code is a lot easier to maintain and understand. I hope this helps you to understand how template works in Blaze.
Following from this question it appears I have to go around the houses with Meteor just to flag a checkbox as checked - but I've been Googling for an hour now and I can't find any examples of how to actually do it.
I've created the base of a helper:
Template.Settings.helpers({
isChecked: function () {
var id = this.id;
console.log(id);
}
});
And here's the HTML:
<input type="checkbox" name="imaname" id="imanid" {{isChecked}} />
However I'm clearly misunderstanding the context of this as literally whatever I try returns undefined (this.target etc. all nada). No docs I can find elaborate on how to actually create the isChecked helper that I'm supposed to use, so I'm now lost and frustrated.
Where do I go from here?
Edit: OK so I have it working, however I'd still like to know what the context of this is (maybe a duh moment, I guess the context is the helper itself… how do I access the element that triggers the helper?) - so I can make it a little more dynamic:
Template.Settings.helpers({
isChecked: function (value) {
return (value == 'on' ? 'checked' : false);
}
});
Along with:
<input type="checkbox" checked={{ isChecked currentUser.profile.services.bananaExports }} />
I'd still like to know what the context of this is
In a Blaze template helper this refers to the data context where the helper was called, which is the data context of the template unless you've set the context to be something else. To learn about data contexts see Discover Meteor's Guide on the topic.
how do I access the element that triggers the helper
Blaze doesn't have any way of telling you where in the DOM a helper was called, it only has access to the data context and any arguments you pass to the helper.
If you really have to access the DOM node you need to use Template.instance().find('some query') or Template.instance().$('some query') for a JQuery object.
An alternative way of marking a checkbox as checked is to define it like this
<input type="checkbox" name="imaname" id="imanid" checked={{isChecked}} />
And then setting isChecked to true or false in your helper.
If I understand your question, here's my approach, instead of using helper, you better use blaze template event handler and jquery like below:
Template.Setting.event({
'click input'(event){
if($('#imanid').is(":checked") == true){
return true;
}else{
return false;
}
}
});
hope this helps
I've come across this situation several times now and I realise I'm not really confident about the 'meteor/right' way to handle it.
Suppose I have a form with several parts - each represented by a template - and within each part there are more templates representing eg. datepickers etc.
<template name='myForm'>
{{>partOne}}
{{>partTwo}}
<button class='submit'>Submit</button>
</template>
<template name='partOne'>
{{>widget}}
{{>widget}}
</template>
<template name='widget'>
<input class='datepicker' />
</template>
I want to keep track of my form as the user fills it out - on the level of the 'myForm' template - but all the events are happening at the level of 'widget'.
One solution I keep seeing (e.g. in this SO answer) is to just put everything in the global Session variable. Like so
Template.widget.events({
'click .select' : function(event, template){
var name = template.data.name;
Session.set(name, $(event.currentTarget).val());
}
});
And then in myForm I should do something like this
Template.myForm.rendered = function(){
Tracker.autorun(function(){
var name = Session.get('name');
// do something
});
}
But as my forms are getting more complicated, I find this is really turning into a mess on the myForm template level, all while filling up my Session variable with data that isn't really application-global.
I'd be really grateful for any ideas on how others deal with this ! Keeping templates and widgets modular while still being able to follow and react to their triggered events from parent templates...
You're not alone in feeling like something just isn't right. This is one of the reasons there's a lot of talk about a Blaze 2. Here's what I do:
Create an app global namespace (e.g. G = {}). I usually use the first letter of the app name & do this in lib/config/_namespace.js
Put your collections in G.Collections or G.C,
Put your shared functions in G.Fx, etc...
Put your template vars in G.T.
Then, save that variable to G.T.varName. In doing so, you can use it in rendered as well as events and helpers. As a perk, it's super easy to find all your "globals" because they're all in the G object. Additionally, you can now 'use strict' again.
Then, to keep it clean:
Template.parentTemplate.destroyed = function() {
G.T = {};
};
So if you need reactivity, just make a ReactiveDict:
Template.parentTemplate.created = function() {
G.T.RD = new ReactiveDict();
};
You can use a file-level ReactiveVar or ReactiveDict, instead of the Session object.
In my meteor app I need to load an array of items corresponding to the item clicked.As I'm new to meteor, I'm held up here.Here is my code.
Template.templatename.events({
'click .showdiv' : function()
{
Template.templatename.vname = function () {
return Db.find();
}
}
Can I set the variable vname dynamically by this code ? This is not working for me.
I think you're misunderstanding the notion of reactivity. A reactive data source will cause any functions which depend on it (including helpers) to rerun when its value is changed, which seems to be the behavior you're looking for here. Instead, you're rewriting the helper function itself every time an item is clicked, which kind of defeats the object of Meteor's reactive data model. Session variables could help:
Template.templatename.events({
'click .showdiv' : function() {
Session.set('vname', Db.find());
}
});
Template.templatename.vname = function () {
return Session.get('vname');
}
If you use an {{#each vname}} block in the templatename template, it will automatically update with the results of the Db.find() query when a .showdiv is clicked. If all you want to do is show the result of that query regardless of whether a click has been registered it would be as simple as:
Template.templatename.vname = function () {
return Db.find();
}
Note that it's still not clear exactly what data you're trying to populate here since the query will return a cursor (which is fine, but you need to loop through it using {{#each ...}} - use findOne if you only want one item), and its contents aren't going to depend on anything intrinsic to the click event (like which .showdiv you clicked). In the former example it will however fail to show anything until the first click (after which you would have to reset with Session.set('vname', null) to stop it showing anything again).
I have a Meteor template that includes the following:
{{#with selected_recipe}}
{{>recipe}}
{{/with}}
In my code (Coffeescript), I want to call a function by name from my event map (Backbone-style):
Template.recipe.events = {
'click #btn-edit-recipe': 'editRecipe'
}
editRecipe = (event) ->
console.log # #should log the selected_recipe object
#edit recipe
However, this fails. When I click on my button in the recipe template, I get Uncaught TypeError: Object editRecipe has no method 'call' (liveui.js:651) I learned event maps from Backbone, and maybe Meteor is different. I can get it to work with:
Template.recipe.events = {
'click #btn-edit-recipe': -> editRecipe.call(#, event)
}
Is this the right way to do this? Or am I making some simple error? I've always liked using event maps this way because it summarizes the behaviors of the rendered template in just a few lines. Anonymous functions can spread the list out, making it harder to read, and of course they are not reusable.
What you are doing (later one, where event definition points to a function) is right.
Event map with value as function name (string) is pattern specific to backbone. Meteor doesn't support it.
I've always liked using event maps this way because it summarizes the
behaviors of the rendered template in just a few lines.
But you can acheive similar functionality by doing something like this:
Template.recipe.doLogin = function(){};
Template.recipe.requestData = function(){};
// OR Another way
_.extend(Template.recipe, {
"openFile":function(){},
"editRecipe":function(){}
});
// now Events
Template.recipe.events {
'click #btn-edit-recipe': Template.recipe['editRecipe'],
'click #btn-create-recipe': Template.recipe['createRecipe']
}
Personally, I don't like event-maps. cause its a mapping, which developer has to maintain manually.
Edit: Working code # https://gist.github.com/3010818