I'm having a problem with outputting the value of selected checkbox's with Meteor.js. The checkbox value is outputting as [object Object] in the browser. Could someone please help me out.
HTML
<head>
<title>project</title>
</head>
<body>
{{>addStatusForm}}
</body>
<template name="addStatusForm">
<form class="addStatus">
{{#each category}}
<input type="checkbox" name="categoryCheckbox" class="boxCheck" value={{categoryDesc}}>{{categoryDesc}}<br>
{{/each}}
<input type="text" name="status">
<input type="submit" value="Add status">
</form>
{{#each status}}
<p>{{statusDesc}} {{category}}</p>
{{/each}}
</template>
Helpers
Template.addStatusForm.helpers({
status: function () {
return Status.find();
},
category: function(){
return Category.find();
}
});
Events
Status = new Mongo.Collection('status');
Category = new Mongo.Collection('category');
Template.addStatusForm.events({
'submit .addStatus': function (event) {
event.preventDefault();
var statusInput = event.target.status.value;
var categorySelected = $('.boxCheck:checked').val();
//var categorySelected = event.target.categoryCheckbox.value; tried this
//var categorySelectedString = JSON.stringify(categorySelected); tried this also
//console.log(categorySelected); just testing console output
//console.log(statusInput); just testing console output
Status.insert({
statusDesc : statusInput,
category : categorySelected
});
}
Problem with this is not how it is stored (console.log(typeof categorySelected ) shows that it is a string and not an object) it is that you are defining the 'category' field twice within this template when displaying the data, once from a helper and once as a field inside of the collection object. It gives the helper priority over the collection data so you get the object being returned from return Category.find(); which is an object and hence the [object Object] output
Two quick solutions here:
A) Change the name of the category helper to categories or something else
B) (Possibly more meteoric) move the code inside your each into a status template giving it a bit of isolation so it can't see the parent templates category field
{{#each status}}
<p>{{statusDesc}} {{category}}</p>
{{/each}}
/*...BECOMES...*/
{{#each status}}
{{>statusTemplate}}
{{/each}}
<template name="statusTemplate">
<p>{{statusDesc}} {{category}}</p>
</template>
Related
I'm working on learning Ember and am trying to do some small ideas with it. Currently, I am trying to receive text field input to filter a list and return the matching results. I have all of this working, you know, the 'hard' stuff. However, the part that isn't working is Handlebars reading the 'title' property of my array that I am returning. It's just blank.
Here is my template:
<script data-template-name="application" type="text/x-handlebars">
{{input type="text" value=searchString placeholder="Search..."}}
{{filterMovies}}
<ul>
{{#each searchResults}}
<li>{{title}}</li>
{{/each}}
</ul>
</script>
And now my controller:
App.ApplicationController = Ember.Controller.extend({
filterMovies: function() {
var self = this,
searchString = self.get('searchString'),
searchResults = [],
filterArrLength = null,
theFullMovieList,
theFilteredMovieList = [];
if (!searchString) {
return;
}
var url = 'http://www.json-generator.com/api/json/get/clVKyWQSnC';
Ember.$.getJSON(url).then(function(data) {
theFullMovieList = data;
theFullMovieList.filter(function(movie) {
if (movie.title.toLowerCase().startsWith(searchString)) {
theFilteredMovieList.push(movie);
}
});
console.log(theFilteredMovieList);
self.set('searchResults', theFilteredMovieList);
});
}.property('searchString')
});
I have tried printing using {{this}}, {{this.title}}, {{searchResults.title}}, and {{title}} with no luck. However, logging the array shows the correct values.
Any ideas? View On CodePen
Your each syntax is invalid. You have to use new syntax:
<ul>
{{#each searchResults as |movie|}}
<li>{{movie.title}}</li>
{{/each}}
</ul>
See working demo on CodePen.
To elaborate on the title, what I am trying to achieve is the following.
I am building an interactive table component in Ember. Here is the stripped template:
<table>
<thead>
<tr>
{{#each header in headers}}
<th>{{header}}</th>
{{/each}}
</tr>
</thead>
<tbody>
{{#each row in rows}}
<tr>
{{#each header in headers}}
<td>{{input value=row.[header]}}</td> <!-- can I bind to row.header here somehow? -->
{{/each}}
</tr>
{{/each}}
</tbody>
</table>
I want each input field for a specific row and specific column, to be bound to that row's row object, specifically to a property named the way the header column is named.
Essentially, I want to use the value of the header variable to bind a property in the row object called by that value (If current header has value 'title' then I want to bind to row.title)
Here is an example of how I initialize these objects:
var headers = ['title','description'];
var rows = [],
row = {};
for(var i = 0; i < headers.length; i++) {
row[headers[i]] = ''; // this line does similar thing to what I am trying to achieve
}
rows.push(row);
/* This gives
rows = [
{
title: '',
description: ''
}
]
*/
After researching, I found this in the Handlebars documentation that says I can access properties like this:
{{#each articles.[10].[#comments]}}
...
{{/each}}
Which is, according to the docs, pretty much the same as:
articles[10]['#comments']
However, using:
rows.[header]
doesn't work for me because it tries to literally access the 'header' property of the rows object (i.e. rows.header) and not the value contained in the header variable.
You can always extend the textfield component and at least get the value you are looking for.
App.DynamicInputComponent = Ember.TextField.extend({
row: null,
col: null,
value: function(){
var row = this.get('row');
var col = this.get('col');
return row[col];
}.property('row', 'col')
});
Then, in your template you can do:
<table>
{{#each item in model}}
<tr>
{{#each col in columns}}
<td> {{ dynamic-input type='text' row=item col=col }} </td>
{{/each}}
</tr>
{{/each}}
</table>
(Partially) working solution here
This functionality can now easily be achived in Ember (since 1.13) using the new and awesome inline helpers mut and get:
The way to achieve this is in two basic steps:
Use get to dynamically lookup a property from r named as whatever the value of h is at that each iteration. For example, if h = 'title', then this would return r['title'].
Use mut to specify that this extracted value is mutable by our input component (specifically its value property).
This is how the whole each looks:
{{#each rows as |r|}}
<tr>
{{#each headers as |h|}}
<td>
<input onkeyup={{action (mut (get r h)) value="target.value" }}>
</td>
{{/each}}
</tr>
{{/each}}
Detailed example on Ember Twiddle
I am trying to filter a JSON response by using 'filterProperty' in Emberjs. But I am getting this error, Uncaught Error: Nothing handled the event 'last'
Here's my App.js
App = Ember.Application.create({});
App.IndexRoute = Ember.Route.extend({
renderTemplate : function(controller) {
this.render('MyApp', {
controller : controller
});
},
model : function() {
return App.MyTemplateModel.find();
}
});
App.IndexController = Ember.ArrayController.extend({
last : (function() {
this.get('content').filterProperty('last_name', 'Solow');
}).property('content.#each.type')
});
App.MyTemplateModel = Ember.Model.extend({
id : Ember.attr(),
last_name : Ember.attr(),
first_name : Ember.attr(),
suffix : Ember.attr(),
expiration : Ember.attr()
});
App.SiteController = Ember.ObjectController.extend({
});
App.MyTemplateModel.url = "http://ankur1.local/index.php/api/example/users/";
App.MyTemplateModel.adapter = Ember.RESTAdapter.create();
var existing = App.MyTemplateModel.find();
App.MyTemplateModel.camelizeKeys = true;
Here's my HTML page,
<script type="text/x-handlebars" data-template-name="MyApp">
{{#each item in content }}
<tr><td>
{{id}} <p> {{item.first_name}} {{item.expiration}}</p>
</td></tr>
{{/each}}
<button {{action last}}>filter</button>
</script>
<script type="text/x-handlebars">
<h1>Application Template</h1>
{{outlet}}
</script>
</body>
What I might be doing wrong in my App.js or should I use any other property to filter the JSON response?
You declared the property last on your IndexController as Computed Property, but if you want to use the {{action}} helper, this is not allowed. It has be a plain function. This is why Ember does not find a suitable event anywhere and complains about it.
App.IndexController = Ember.ArrayController.extend({
// for initial filling of this property, will be overridden by last action
filteredContent : Ember.computed.oneWay("content"),
last : function() {
var filtered = this.get('content').filterProperty('last_name', 'Solow');
this.set("filteredContent", filtered);
}
});
<script type="text/x-handlebars" data-template-name="MyApp">
{{#each item in filteredContent }}
<tr><td>
{{id}} <p> {{item.first_name}} {{item.expiration}}</p>
</td></tr>
{{/each}}
<button {{action last}}>filter</button>
</script>
So i did basically two things:
I changed the computed property to a plain function.
The template is iterating over filteredContent instead of content. (Note the initialization i had to do on your Controller.)
Sou the basic mechanism is to have an additional property on your Controller, which holds the filtered content. You have to expand on this, because your usecase is a bit more complex for sure. :-)
I have the following event in my client file:
Template.categories.events({
...
'keyup #add-category': function (e,t){
if (e.which === 13)
{
var catVal = String(e.target.value || "");
if (catVal)
{
lists.insert({Category:catVal,owner:this.userId});
Session.set('adding_category', false);
}
}
},
...
});
And this is the relevant template part:
<template name="categories">
<div id="categories" class="btn-group">
{{#if new_cat}}
<div class="category">
<input type="text" id="add-category" value="" />
</div>
{{else}}
<div class="category btn btn-inverse" id="btnNewCat">+</div>
{{/if}}
{{#each lists}}
<div class="category btn {{list_status}}" id="{{_id}}">
{{Category}}
</div>
{{/each}}
</div>
</template>
So when a new Category is inserted, the owner should be set.. But it doesn't.
Here's the entry in MongoDB:
> db.lists.find()
{ "Category" : "test-admin", "_id" : "EsybjC3SLnNzCBx2t" }
Any idea what I'm doing wrong? (actually I'm following the "Getting Started with Meteor" book lending library example
EDIT it seems that:
console.log(this.userId);
undefined
Swap this line:
lists.insert({Category:catVal,owner:this.userId});
to this one:
lists.insert({Category:catVal,owner:Meteor.userId()});
this is probably not what you expect it to be inside that event. You should debug to confirm that that is the case. You are probably just getting undefined for this.userId. I would recommend assigning this to a variable (call it "self" or "that") outside of this event handler function but inside the scope where this will be what you actually want it to be. You can then reference that variable inside the event handler.
It should look like this:
function thatRegistersEvents() {
var self = this;
// ...
registerSomeEvent(function () {
return self.someThisProperty;
});
}
This is the correct behavior. If you read the official Meteor documentation for references to this.UserId, it is only available in Meteor.publish() and Meteor.methods(). this.UserId is not available in Meteor.template(), which you have done in the code sample above, so one must use Meteor.userId() in templates.
I've got a handlebar template that loads a partial for a sub-element.
I would need to access a variable from the parent context in the calling template, from within the partial. .. doesn't seem to resolve to anything inside the partial.
Simplified code goes like this:
the template
{{#each items}}
{{> item-template}}
{{/each}}
the partial
value is {{value}}
(obviously the real code is more complicated but it's the same principle, within the partial .. appears to be undefined.)
To show it's undefined, I've used a very simple helper whatis like this:
Handlebars.registerHelper('whatis', function(param) {
console.log(param);
});
and updated the above code to this:
updated template
{{#each items}}
{{whatis ..}} <-- Console shows the correct parent context
{{> item-template}}
{{/each}}
updated partial
{{whatis ..}} <-- Console shows "undefined"
value is {{value}}
Is there a way to go around that issue? Am I missing something?
EDIT: There's an open issue relating to this question on handlebars' github project
Just in case anyone stumbles across this question. This functionality exists now in Handlebars.
Do this:
{{#each items}}
{{! Will pass the current item in items to your partial }}
{{> item-template this}}
{{/each}}
Working fiddle (inspired by handlebars pull request #385 by AndrewHenderson)
http://jsfiddle.net/QV9em/4/
Handlebars.registerHelper('include', function(options) {
var context = {},
mergeContext = function(obj) {
for(var k in obj)context[k]=obj[k];
};
mergeContext(this);
mergeContext(options.hash);
return options.fn(context);
});
Here's how you'd setup the parent template:
{{#each items}}
{{#include parent=..}}
{{> item-template}}
{{/include}}
{{/each}}
And the partial:
value is {{parent}}
As of 2.0.0 partials now supports passing in values.
{{#each items}}
{{> item-template some_parent_var=../some_parent_var}}
{{/each}}
Took me awhile to find this, hope it's useful for someone else too!
The easiest way to pass the parent context to the partial is to do the loop inside the partial. This way the parent context is passed by default and when you do the loop inside the partial the {{../variable}} convention can access the parent context.
example fiddle here.
The Data
{
color: "#000"
items: [
{ title: "title one" },
{ title: "title two" },
]
}
The Template
<div class="mainTemplate">
Parent Color: {{color}}
{{> partial}}
</div>
The Partial
<div>
{{#each items}}
<div style="color:{{../color}}">
{{title}}
</div>
{{/each}}
</div>
You can use some of the proposed solutions on the comments from the link to github:
https://github.com/wycats/handlebars.js/issues/182#issuecomment-4206666
https://github.com/wycats/handlebars.js/issues/182#issuecomment-4445747
They create helpers to pass the info to the partial.
I created an each Helper function that includes the parent key/values within the subcontext under the key parentContext.
http://jsfiddle.net/AndrewHenderson/kQZpu/1/
Note: Underscore is a dependency.
Handlebars.registerHelper('eachIncludeParent', function ( context, options ) {
var fn = options.fn,
inverse = options.inverse,
ret = "",
_context = [];
$.each(context, function (index, object) {
var _object = $.extend({}, object);
_context.push(_object);
});
if ( _context && _context.length > 0 ) {
for ( var i = 0, j = _context.length; i < j; i++ ) {
_context[i]["parentContext"] = options.hash.parent;
ret = ret + fn(_context[i]);
}
} else {
ret = inverse(this);
}
return ret;
});
To be used as follows:
{{#eachIncludeParent context parent=this}}
{{> yourPartial}}
{{/eachIncludeParent}}
Access parent context values in your partial using {{parentContext.value}}
I needed dynamic form attributes for something like this...
{{#each model.questions}}
<h3>{{text}}</h3>
{{#each answers}}
{{formbuilder ../type id ../id text}}
{{/each}}
{{/each}}
and a helper like so...
Handlebars.registerHelper('formbuilder', function(type, id, qnum, text, options)
{
var q_type = options.contexts[0][type],
a_id = options.contexts[1].id,
q_number = options.contexts[0][qnum],
a_text = options.contexts[1].text;
return new Handlebars.SafeString(
'<input type=' + q_type + ' id=' + a_id + ' name=' + q_number + '>' + a_text + '</input><br/>'
);
});
Which produces...
<input type="checkbox" id="1" name="surveyQ0">First question</input>
My model is a big blob of arrays and objects mixed together. What's noteworthy is that using '../' like so '../type', passes in the parent model as the context, and without it, such as with 'id', it passes in the current model as the context.
To get specifically the parent of the partial (where you may be several partials deep) then follow the other answers like SeanWM.
If you know that the parent is the main template then you can use #root which resolves to the top-most context no matter how deep you are.
e.g. {{#root.rootObject.rootProperty}}
It is a pity that ../../.. does not go up past a partial.