I have an Ember.js ArrayController and some handlebar code that looks like this
<p>{{length}} {{pluralize length "thing"}}</p>
Then I've got a handlebar helper that looks like
Handlebars.registerHelper('pluralize', function(count, str){
debugger;
return (count > 1 ? str+"s" : str);
}
);
When the debugger breaks I observe seeing that count = 'length' not a number like I would expect.
So what gives? What's the correct way to accomplish my obvious task.
Working fiddle here. http://jsfiddle.net/MwTuw/2/
The trick is to use Ember.registerBoundHelper which passes all the relevant data as a final argument to the function.
Ember.Handlebars.registerBoundHelper('pluralize', function (count) {
var options = Array.prototype.pop.call(arguments);
var string = options.data.properties[1];
return (count > 1 ? string+"s" : string);
});
This removes the {{if controller.length}} hack that is required with the other solution and means that adding or removing additional objects will update the value accordingly.
How about this:
Ember.Handlebars.registerHelper('pluralize', function (property, options) {
var count = Ember.Handlebars.get(this, property, options);
var _options = options;
count = parseInt(count, 10); //to be sure ...
if (count > 1) {
_options = _options.concat('s');
}
return new Handlebars.SafeString(_options);
});
EDIT
Here is a working fiddle
EDIT 2
Here is your working updated fiddle
Basically the problem was that the handlebar helper was acting when the controller still had no records, I've added a if helper to the template that listen on the controller.length and fires when it changes and so invoking also the handlebar helper to parse the value.
Hope it helps
Using Ember.registerBoundHelper will make all the key-value pairs in the template available as an hash on the 2nd parameter of the helper:
Handlebars template:
{{orderShow dataOrderBy key1="value1" key2="value2" ... keyN="valueN"}}
Javascript:
Ember.Handlebars.registerBoundHelper('orderShow', function(order, options) {
if(options) {
for(var prop in options.hash) {
alert(prop + '="' + options.hash[prop] + '"')
}
}
return order;
}
This behaviour is described at the end of the following page: http://handlebarsjs.com/expressions.html
Related
here is my code I have to call another method inside the Ember controller
I was tried but this is not working I have confused in this...
please help me to what is wrong in this code?
export default Ember.Controller.extend({
....
getValue(){
var a = 7 * 2;
return a;
},
getResult(){
var result = this.getValue(); // result is this.getValue is not function
}
});
First this code does not makes sense:
getValue(){
var a = a * 2;
return a;
},
here you're using a before you declare it.
but to answer your question: your code is correct.
Here is a working example. I've just replaced var a = a * 2; by var a = 7 * 2; to make this code valid.
My assumption is that whatever is calling getResult() is not setting the context of this to the controller, and my hunch is that you'll need to put getResults() in the actions object.
I need your help about CouchDB reduce function.
I have some docs like:
{'about':'1', 'foo':'a1','bar':'qwe'}
{'about':'1', 'foo':'a1','bar':'rty'}
{'about':'1', 'foo':'a2','bar':'uio'}
{'about':'1', 'foo':'a1','bar':'iop'}
{'about':'2', 'foo':'b1','bar':'qsd'}
{'about':'2', 'foo':'b1','bar':'fgh'}
{'about':'3', 'foo':'c1','bar':'wxc'}
{'about':'3', 'foo':'c2','bar':'vbn'}
As you can seen they all have the same key, just the values are differents.
My purpse is to use a Map/Reduce and my return expectation would be:
'rows':[ 'keys':'1','value':{'1':{'foo':'a1', 'at':'rty'},
'2':{'foo':'a2', 'at':'uio'},
'3':{'foo':'a1', 'at':'iop'}}
'keys':'1','value':{'foo':'a1', 'bar','rty'}
...
'keys':'3','value':{'foo':'c2', 'bar',vbn'}
]
Here is the result of my Map function:
'rows':[ 'keys':'1','value':{'foo':'a1', 'bar','qwe'}
'keys':'1','value':{'foo':'a1', 'bar','rty'}
...
'keys':'3','value':{'foo':'c2', 'bar',vbn'}
]
But my Reduce function isn't working:
function(keys,values,rereduce){
var res= {};
var lastCheck = values[0];
for(i=0; i<values.length;++i)
{
value = values[i];
if (lastCheck.foo != value.foo)
{
res.append({'change':[i:lastCheck]});
}
lastCheck = value;
}
return res;
}
Is it possible to have what I expect or I need to use an other way ?
You should not do this in the reduce function. As the couchdb wiki explains:-
If you are building a composite return structure in your reduce, or only transforming the values field, rather than summarizing it, you might be misusing this feature.
There are two approaches that you can take instead
Transform the results at your application layer.
Use the list function.
Lists functions are simple. I will try to explain them here:
Lists like views are saved in design documents under the key lists. Like so:
"lists":{
"formatResults" : "function(head,req) {....}"
}
To call the list function you use a url like this
http://localhost:5984/your-database/_design/your-designdoc/_list/your-list-function/your-view-name
Here is an example of list function
function(head, req) {
var row = getRow();
if (!row){
return 'no ingredients'
}
var jsonOb = {};
while(row=getRow()){
//construct the json object here
}
return {"body":jsonOb,"headers":{"Content-Type" : "application/json"}};
}
The getRow function is of interest to us. It contains the result of the view. So we can query it like
row.key for key
row.value for value
All you have to do now is construct the json like you want and then send it.
By the way you can use log
to debug your functions.
I hope this helps a little.
Apparently now you need to use
provides('json', function() { ... });
As in:
Simplify Couchdb JSON response
I have a list of i18n translation strings within an meteor-i18n object that I'm iterating over. Instead of creating a Template Helper for each string manually though, which would soon become redundant and repetitive, I would like to create the Helpers dynamically, within a loop, like so:
for (var namespace in Meteor.i18nMessages) {
for (var msg in Meteor.i18nMessages[namespace]){
//Template[namespace][msg] = __(namespace + "." + msg); // <- works but is not reactive
Template[namespace][msg] = function() { // <- Doesn't work: always returns last value from object
return __(namespace + "." + msg);
}
}
}
However when I do, I lose reactivity. How would one go about solving this? I'm a fan of best-practices and elegant code.
Thanks.
You need to precompile the handlebars templates with
Meteor._def_template("templateName", function () {
return "your html"
});
This creates a template at Template.templateName which will be reactive with the helpers defined for it.
EventedMind has some screencasts that show how this works in good detail:
http://www.eventedmind.com/posts/handlebars-how-it-works
http://www.eventedmind.com/posts/handlebars-precompiling
My text editor keeps calling me "evil" for using eval but this works:
for (var namespace in Meteor.i18nMessages) {
var obj = {};
for (var msg in Meteor.i18nMessages[namespace]) {
var str = 'obj["' + msg + '"] = function() { return __("' + namespace + '.' + msg + '"); }';
console.log(str);
eval(str);
}
Template[namespace].helpers(obj);
}
At the moment I have the states in a string as follows:
var states = 'AL|AK|AR|AZ|CA|CO|CT|DC|DE|FL|GA|HI|IA|ID|IL...';
I would like to capture
mydomain.com/CA/more-stuff
Into two separate parameters. The first one with the state information, the second one with the additional. I know how to get the additional information:
app.get('/' ??? + '/:additional', ...);
How can I capture the state information?
If you define it as a parameter in the path...
app.get('/:stateabbr/:additional', ...);
You can validate it with app.param():
app.param('stateabbr', function (req, res, next, abbr) {
var stateAbbrs = /AL|AK|AR|AZ|CA|CO|CT|DC|DE|FL|GA|HI|IA|ID|IL|../i
if (stateAbbrs.test(abbr)) {
next()
} else {
next(new Error('Unrecognized State abbreviation.'));
}
});
Try this one
var states = 'AL|AK|AR|AZ|CA|CO|CT|DC|DE|FL|GA|HI|IA|ID|IL...';
app.get('/:state('+ states + ')/:additional', function(req,res,next){
res.send('state:'+req.params.state+',additional:'+req.params.additional);
})
I've tested it and it should work as You requested :)
Is there a way to retrieve the set-at-creations properties of an EmberJS object if you don't know all your keys in advance?
Via the inspector I see all the object properties which appear to be stored in the meta-object's values hash, but I can't seem to find any methods to get it back. For example object.getProperties() needs a key list, but I'm trying to create a generic object container that doesn't know what it will contain in advance, but is able to return information about itself.
I haven't used this in production code, so your mileage may vary, but reviewing the Ember source suggests two functions that might be useful to you, or at least worth reviewing the implementation:
Ember.keys: "Returns all of the keys defined on an object or hash. This is useful when inspecting objects for debugging. On browsers that support it, this uses the native Object.keys implementation." Object.keys documentation on MDN
Ember.inspect: "Convenience method to inspect an object. This method will attempt to convert the object into a useful string description." Source on Github
I believe the simple answer is: you don't find a list of props. At least I haven't been able to.
However I noticed that ember props appear to be prefixed __ember, which made me solve it like this:
for (f in App.model) {
if (App.model.hasOwnProperty(f) && f.indexOf('__ember') < 0) {
console.log(f);
}
};
And it seems to work. But I don't know whether it's 100% certain to not get any bad props.
EDIT: Adam's gist is provided from comments. https://gist.github.com/1817543
var getOwnProperties = function(model){
var props = {};
for(var prop in model){
if( model.hasOwnProperty(prop)
&& prop.indexOf('__ember') < 0
&& prop.indexOf('_super') < 0
&& Ember.typeOf(model.get(prop)) !== 'function'
){
props[prop] = model[prop];
}
}
return props;
}
Neither of these answers are reliable, unfortunately, because any keys paired with a null or undefined value will not be visible.
e.g.
MyClass = Ember.Object.extend({
name: null,
age: null,
weight: null,
height: null
});
test = MyClass.create({name: 'wmarbut'});
console.log( Ember.keys(test) );
Is only going to give you
["_super", "name"]
The solution that I came up with is:
/**
* Method to get keys out of an object into an array
* #param object obj_proto The dumb javascript object to extract keys from
* #return array an array of keys
*/
function key_array(obj_proto) {
keys = [];
for (var key in obj_proto) {
keys.push(key);
}
return keys;
}
/*
* Put the structure of the object that you want into a dumb JavaScript object
* instead of directly into an Ember.Object
*/
MyClassPrototype = {
name: null,
age: null,
weight: null,
height: null
}
/*
* Extend the Ember.Object using your dumb javascript object
*/
MyClass = Ember.Object.extend(MyClassPrototype);
/*
* Set a hidden field for the keys the object possesses
*/
MyClass.reopen({__keys: key_array(MyClassPrototype)});
Using this method, you can now access the __keys field and know which keys to iterate over. This does not, however, solve the problem of objects where the structure isn't known before hand.
I use this:
Ember.keys(Ember.meta(App.YOUR_MODEL.proto()).descs)
None of those answers worked with me. I already had a solution for Ember Data, I was just after one for Ember.Object. I found the following to work just fine. (Remove Ember.getProperties if you only want the keys, not a hash with key/value.
getPojoProperties = function (pojo) {
return Ember.getProperties(pojo, Object.keys(pojo));
},
getProxiedProperties = function (proxyObject) {
// Three levels, first the content, then the prototype, then the properties of the instance itself
var contentProperties = getPojoProperties(proxyObject.get('content')),
prototypeProperties = Ember.getProperties(proxyObject, Object.keys(proxyObject.constructor.prototype)),
objectProperties = getPojoProperties(proxyObject);
return Ember.merge(Ember.merge(contentProperties, prototypeProperties), objectProperties);
},
getEmberObjectProperties = function (emberObject) {
var prototypeProperties = Ember.getProperties(emberObject, Object.keys(emberObject.constructor.prototype)),
objectProperties = getPojoProperties(emberObject);
return Ember.merge(prototypeProperties, objectProperties);
},
getEmberDataProperties = function (emberDataObject) {
var attributes = Ember.get(emberDataObject.constructor, 'attributes'),
keys = Ember.get(attributes, 'keys.list');
return Ember.getProperties(emberDataObject, keys);
},
getProperties = function (object) {
if (object instanceof DS.Model) {
return getEmberDataProperties(object);
} else if (object instanceof Ember.ObjectProxy) {
return getProxiedProperties(object);
} else if (object instanceof Ember.Object) {
return getEmberObjectProperties(object);
} else {
return getPojoProperties(object);
}
};
In my case Ember.keys(someObject) worked, without doing someObject.toJSON().
I'm trying to do something similar, i.e. render a generic table of rows of model data to show columns for each attribute of a given model type, but let the model describe its own fields.
If you're using Ember Data, then this may help:
http://emberjs.com/api/data/classes/DS.Model.html#method_eachAttribute
You can iterate the attributes of the model type and get meta data associated with each attribute.
This worked for me (from an ArrayController):
fields: function() {
var doc = this.get('arrangedContent');
var fields = [];
var content = doc.content;
content.forEach(function(attr, value) {
var data = Ember.keys(attr._data);
data.forEach(function(v) {
if( typeof v === 'string' && $.inArray(v, fields) == -1) {
fields.push(v);
}
});
});
return fields;
}.property('arrangedContent')