It is said that,
A good rule of thumb is that if you're using jQuery to manipulate any
DOM elements, you're probably doing it wrong.
And:
Needing access to an element from a helper function indicates that you
are trying to use a procedural coding style rather than a
template-driven style.
I have a simple number input that I wish to translate into a more comprehensible currency outside it so that the user understands what he is doing. I wanted to do this:
<input type="number" class="raw-price">
<p>Price in USD: {{priceInUsd}}</p>
And then define a helper:
Template.myTemplate.helpers({
priceInUsd: function() {
var rawPrice = $('.raw-price').val()
//perform calculation
return calculationResult
}
})
First of all, this isn't working (I don't really know why). Second, this goes against the "rules" I posted above. How am I supposed to do it? I probably could do this the same way using an event listener instead, but that would still be the wrong approach, I assume.
Flash update!
Actually! Here's a better solution: reactive variables! If you wish to keep your rawPrice in this one template, just install the standard reactive-var package:
meteor add reactive-var
And go at it this way:
Template:
<input type="number" class="raw-price">
<p>Price in USD: {{priceInUsd}}</p>
onCreated:
Template.myTemplate.onCreated(function() {
this.rawPrice = new ReactiveVar;
this.rawPrice.set(''); // not sure what you want to preset your value to
});
Helper:
Template.myTemplate.helpers({
priceInUsd: function() {
var rawPrice = Template.instance().rawPrice.get()
//perform calculation
return calculationResult
}
})
Event:
Template.myTemplate.events({
"change .raw_price": function (evt, template) {
template.rawPrice.set($(evt.currentTarget).val());
}
});
Previous (accepted) answer
According to most examples of two-way data binding in meteor I have seen, best case would probably be to use a helper, an event and a Session variable, like so.
Template:
<input type="number" class="raw-price">
<p>Price in USD: {{priceInUsd}}</p>
Helper:
Template.myTemplate.helpers({
priceInUsd: function() {
var rawPrice = Session.get('rawPrice');
//perform calculation
return calculationResult
}
})
Event:
Template.myTemplate.events({
"change .raw_price": function (evt) {
Session.set("rawPrice", $(evt.currentTarget).val());
}
});
Sadly, you're using a session variable, but it is still better than using a collection for such a local thing, like I saw in other examples...
Related
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
Here's my callback:
Template.joinedRoomsList.rendered = function () {
// the console.log prints: 'undefined' in client console
console.log($('#joined-rooms').children().first().html());
Session.set('currentRoom', $('#joined-rooms').children().first().text());
}
Here's my template:
<template name="joinedRoomsList">
<select id="joined-rooms">
{{#each joinedRooms}}
<option>{{name}}</option>
{{/each}}
</select>
</template>
I've tried many different ways to set the Session key 'currentRoom' to the value of the first in my .
The console.log statement prints out 'undefined', in the client console, however, when I manually type into the developer console the same thing: $('#joined-rooms').children().first().html(), it gives me a value.
Essentially, I'm trying to set the Session key 'currentRoom' to the first value in the on page load/body load/etc.
You can try to fix this by calling defer. I guess that you call your methods a bit too early - before the dom had time to render.
Template.joinedRoomsList.rendered = function () {
Meteor.defer(function(){
console.log($('#joined-rooms').children().first().html());
Session.set('currentRoom', $('#joined-rooms').children().first().text());
});
}
Try Template's selector, it's more efficient cause we just find elements inside our current Template instead of the whole doom with jQuery.
Template.joinedRoomsList.rendered = function() {
console.log(this.find("#joined-rooms"));
}
Currently I'm creating an app with meteor en learning it while building. I try to incorporate Sessions instead of writing everything to the database (what I was doing). In my understanding Session is a global object which stores key-value pairs and is reactive. Therefore I thought it would a great choice to use for template rendering specifics in my simple game. My goal is small game, and the different steps will be renderend in a template for each player based on certain action they made.
I rewrote my app and wanted to use Session in this way (simplified of course).
My template:
<template name="gameRoom">
<button id='click'>click</button>
{{#if lastAction}}
{{>waiting}}
{{/if}}
</template>
Template.gameRoom.events({
lastAction: function() {
return Session.get('lastAction') === Meteor.userId();
};
})
Template.gameRoom.helpers({
'click #click' : function() {
Session.set('lastAction', Meteor.userId());
};
})
However this doesn't work they way I thought it would work. It looks like that each Session is individual for each user (what makes sense of course considering it's (sort-of) a replacement of cookies).
So my question is:
Is there a good alternative for Sessions? Do i really need to have a
alternative or is using a database ok for this? What about a local database?
Your events and helpers functions are backwards, you're missing a couple curly braces, and your event key (the button's ID) is wrong. Try this:
Template.gameRoom.helpers({
lastAction: function() {
return Session.equals('lastAction', Meteor.userId());
}
});
Template.gameRoom.events({
'click #click': function() {
Session.set('lastAction', Meteor.userId());
}
});
Edit: from what you are trying to do, it might make sense to do something like this:
Actions = new Meteor.Collection('actions');
if (Meteor.isClient) {
Template.gameRoom.events({
'click #click': function() {
Actions.insert({userId: Meteor.userId()});
}
});
Template.gameRoom.helpers({
lastAction: function() {
var lastAction = Actions.findOne() || {};
return lastAction.userId === Meteor.userId();
}
});
}
Is it possible for a parent Meteor template to access a subtemplate directly? Ideally, I'd like to use templates a widgets with an API. I was hoping for something like this:
mypage.html
<template name="myPage">
<div class="widgetContainer"></div>
<button>submit</button>
</template>
mypage.js
Template.myPage.rendered = function(){
this.myWidgetInstance = UI.render(Template.myWidget)
UI.insert(this.myWidgetInstance, $('.widgetContainer')[0]);
}
Template.myPage.events({
'click button': function(e, template){
// I don't want this logic to live inside of mywidget.js.
// I also don't want this template to know about the <input> field directly.
var val = template.data.myWidgetInstance.getMyValue();
}
});
mywidget.html
<template name="myWidget">
<input></input>
</template>
mywidget.js
Template.myWidget.getValue = function(){
return this.$('input').val();
}
The above doesn't work because myWidgetInstance.getMyValue() doesn't exist. There doesn't appear to be a way for external code to access template helper functions on an instance.
Is anyone using Meteor templates in the way I'm trying to use them above? Or is this better suited for a separate jQuery widget? If so, it'd be a shame because I still want my widget template to benefit from the features Meteor provides.
It is possible to access subtemplate helper function.
Your example will work once you apply few fixes:
fix 1 : getValue() instead of getMyValue()
Template.myPage.events({
'click button': function(e, template){
// I don't want this logic to live inside of mywidget.js.
// I also don't want this template to know about the <input> field directly.
var val = template.myWidgetInstance.getValue();
console.log(val);
}
});
fix 2 : $('input').val(); instead this.$('input').val();
Template.myWidget.getValue = function(){
return $('input').val();
}
fix 3 : <input> should have no close tag.
<template name="myWidget">
<input type="text" value="sample value">
</template>
I'm using Meteor with Iron Router, and can't seem to get typeahead (this version: https://github.com/bassjobsen/Bootstrap-3-Typeahead) to work.
Here's some code:
HomeController = RouteController.extend({
//....
after: function () {
var tags = this.getData().tags;
console.log(tags);
if(tags.length > 0) {
var tags = ['hello', 'world'];
console.log("Adding typeahead for tags to ", $('.input-search')[0]);
console.log("tags: ", tags);
$('.input-search').typeahead({
source: tags,
updater: function(item) {
Router.go('/projects/tag/' + item);
}
});
}
},
I have a header that's part of the application layout, and has an input like this:
<input type="text" class="form-control input-search" data-provide="typeahead" placeholder="Search">
The jQuery in the after: function gets the input correctly. But calling typeahead on the input doesn't seem to activate typeahead properly: when typing in the input, nothing happens.
However if I wrap the typeahead call in a setTimeout, it does work.
Of course, whenever you start wrapping things in setTimeouts, something isn't right.
Where/when is the correct place to initialise typeahead when using Iron Router?
You can initialize the typeahead in the rendered function of your template. For instance:
Template.mytemplate.rendered = function() {
$('.input-search').typeahead({
source: tags,
updater: function(item) {
Router.go('/projects/tag/' + item);
}
});
};
I figured it out.
You need to make sure any inputs used by plugins are "preserved", i.e. not re-rendered, by Meteor. The simplest way to do this is to make sure the input has an ID attribute. So changing my search input to this fixed it:
<input type="text" id="input-search" class="form-control" data-provide="typeahead" placeholder="Search">
Relevant documentation: http://docs.meteor.com/#template_preserve
As of Meteor 1.0.3.1 and iron:router#1.0.7 working solution is to install sergeyt:typeahead and init typeahead like this:
Template.MyTemplate.rendered = function () {
Meteor.typeahead.inject();
}
Init may be done only once for top-level template.
I wrote an article about this, you can read it here.
In summary, your implementation using javascript to select and alter the element is bad practice in a reactive environment. I've tried it that way and it's a huge pain.
What I found is that you can create a helper that returns a JSON string of your typeahead data and use the data-source attribute, like so
JS
Template.myHelper.helper({
typeahead : function(){
return JSON.stringify(Session.get("typeahead"));
}
});
HTML
<template name="myTemplate">
<input data-source="{{typeahead}}" data-provide="typeahead" id="blah" type="text" />
</template>