Is it possible to get all attrs defined in an Ember component? E.g. if someone uses it like this
{{my-datepicker one='option' another='option' ... }}
in the component code can I grab or iterate over all the options that were passed in?
I believe this is going to be much simpler with block params + other forthcoming improvements, but is there a hacky way to do this right now?
If there is a will - there is gotta to be a way. You asked for hacky :)
App.XHackComponent = Ember.Component.extend({
didInsertElement: function(){
var source = this._keywords.view.source;
var exceptions = ["helperName", "templateData", "container", "elementId",
"currentState", "classNames", "classNameBindings", "controller",
"toString", "buffer", "element"];
for (var key in source) {
if(key.charAt(0) === "_") continue;
if (source.hasOwnProperty(key) && !exceptions.contains(key)) {
console.log("Key: " + key + ", Value: " + source[key]);
}
}
}
});
Working example here
Related
The overarching requirement I am trying to implement is to show comments (made on a list, item by item basis).
I added the feature on the authoring side by enabling versioning on the list and adding a text field with the option "Append Changes to Existing Text" set to true.
This indeed allows me to comment on items and displays them chronologically, but on the authoring side only.
The issue is that the UI part will be done on another site collection and I can't find a straightforward way to get all comments there.
So far, every resource I have found points to
<SharePoint:AppendOnlyHistory runat="server" FieldName="YourCommentsFieldName" ControlMode="Display"/>
The thing is, I can't (don't know how to) use this inside a display template.
So far, I am getting all my data using the REST API, via
var siteUrl=_spPageContextInfo.webAbsoluteUrl.replace("publishing","authoring");
$.ajax({
url: siteUrl + "/_api/web/lists/getbytitle('" + listname + "')/items(" + id + ")",
type: 'GET',
async:false,
headers: {"accept": "application/json;odata=verbose",},
dataType: 'JSON',
success: function(json) {
console.log(json);
//var obj = $.parseJSON(JSON.stringify(json.d.results));
//alert(obj);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("error :"+XMLHttpRequest.responseText);
}
});
What this gives me is the latest comment only. I need a simple way to get a hold of the entire thread.
I ended up using javascript object model to get them like so:
function GetComments(listname, itemId) {
var siteUrl = _spPageContextInfo.webAbsoluteUrl.replace("publishing", "authoring");
if ($(".comments-history").length) {
$().SPServices({
operation: "GetVersionCollection",
async: false,
webURL: siteUrl,
strlistID: listname,
strlistItemID: itemId,
strFieldName: "Comments",
completefunc: function (xData, Status) {
$(xData.responseText).find("Version").each(function (data, i) {
var xmlComment = $(this)[0].outerHTML;
var arr = xmlComment.split(/comments|modified|editor/g);
var comment = arr[1].trim().substring(2, arr[1].length-2);
var dateSt = Date.parse((arr[2].substring(1, arr[2].length)).replace('/"', ''));
var user = getUsername(arr[3]);
var st = "<div class='comment-item'><div class='comment-user'>" + user + "(" + FormatDate(dateSt) + ")</div>";
st += "<div class='comment-text'>" + comment + "</div></div>";
$(".comments-history").append(st);
});
}
});
}
}
the parsing could be better, but this is just an initial working idea
I'm trying to so this, but it doesnt work
App.FacetCheckboxView = Ember.Checkbox.extend({
facetName: null,
facetValue: null,
checked: function () {
var s = this.get('facetValue');
var selected = this.get('controller.' + this.get('facetName'));
return selected.contains(s);
}.property('controller.'+ this.get('facetName') +'.#each')
})
If I use a static string instead in the property method argument it does work. Is there an alternative to accomplish the same?
Sure, you can define a computed property on init
App.FacetCheckboxView = Ember.Checkbox.extend({
init: function(){
this._super();
Ember.defineProperty(this, 'checked', Ember.computed(function() {
var facetValue = this.get('facetValue'),
options = this.get('controller.' + this.get('facetName'));
return options.contains(s);
}).property("controller." + this.get('facetName') + ".[]", 'facetValue'));
},
facetName: null,
facetValue: null
});
I made an example, I'm not completely sure if this is how you're using the view, but it should get you on the right road.
http://emberjs.jsbin.com/vopewayo/1/edit
Personally it seems like it'd be easier to just not define the value as a string, but as the collection, but I'm not sure of your exact use case.
According to this question, it was possible to do something like this with Handlebars rc1:
{{#each links}}
<li>{{#index}} - {{url}}</li>
{{/each}}
{{#index}} would basically give you the iteration index, which is really useful when creating tables.
When I try this with Ember.js rc3, I get an unexpected token error. Does this not work anymore? Did it ever work? Is there another way to get the iteration index?
It looks like it was possible. Can't get it to work with HBS RC3. Probably, is deprecated.
Here's a "hand written" HBS helper.
This can help you gettin the index with {{index}} and side by side you can know if the iteration in on first or last object of the Array with {{first}} and {{last}} respectively.
Ember.Handlebars.registerHelper("foreach", function(path, options) {
var ctx;
var helperName = 'foreach';
if (arguments.length === 4) {
Ember.assert("If you pass more than one argument to the foreach helper, it must be in the form #foreach foo in bar", arguments[1] === "in");
var keywordName = arguments[0];
options = arguments[3];
path = arguments[2];
helperName += ' ' + keywordName + ' in ' + path;
if (path === '') {
path = "this";
}
options.hash.keyword = keywordName;
} else if (arguments.length === 1) {
options = path;
path = 'this';
} else {
helperName += ' ' + path;
}
options.hash.dataSourceBinding = path;
// Set up emptyView as a metamorph with no tag
//options.hash.emptyViewClass = Ember._MetamorphView;
// can't rely on this default behavior when use strict
ctx = this || window;
var len = options.contexts[0][path].length;
options.helperName = options.helperName || helperName;
options.contexts[0][path].map(function(item, index) {
item.index = index;
item.first = index === 0;
item.last = index === len - 1;
})
if (options.data.insideGroup && !options.hash.groupedRows && !options.hash.itemViewClass) {
new GroupedEach(ctx, path, options).render();
} else {
return Ember.Handlebars.helpers.collection.call(ctx, Ember.Handlebars.EachView, options);
}
});
and this can be tested like
{{#foreach array}}
{{log index first last}}
{{/foreach}}
i had the same problem recently i finish by writing a bound helper and passing them objects via Binding for example item here is an ember DS.Store object and content is a 'content' of the controller. hope it
Ember.Handlebars.registerBoundHelper 'isPair', (content, options)->
item = options.hash.item
content_name = options.hash.content || 'content'
if #get(content_name).indexOf(item) % 2 == 0 then 'is-pair' else 'is-unpair'
and in you view you call it
{{isPair content itemBinding='order'}}
i don't know if it is what you looking for but it might give you some ideas how to use it in your project.
btw. Ember overwrites #each helper that's why there it no #index i suppose
Are there any helpers for making templates aware of when to use plural words?
In the example below, how do you make the template output "2 dogs have..."?
The code:
Ember.View.create({dog_count: 2})
The template:
{{dog_count}} (dog has)/(dogs have) gone for a walk.
I know this is old, but I needed it today, so here goes.
Ember.Handlebars.registerBoundHelper('pluralize', function(number, opts) {
var single = opts.hash['s'];
Ember.assert('pluralize requires a singular string (s)', single);
var plural = opts.hash['p'] || single + 's';
return (number == 1) ? single : plural;
});
Usage:
{{questions.length}} {{pluralize questions.length s="Question"}}
or
{{dog_count}} {{pluralize dog_count s="dog has" p="dogs have"}} gone for a walk.
The plural (p=) option is only necessary when you don't want the standard +s behavior.
There is a I18n library for Ember: zendesk/ember-i18n.
There is a handlebars helper t which handles the internationalization by looking up string from Em.I18n.translations:
Em.I18n.translations = {
'dog.walk.one': '1 dog has gone for a walk.',
'dog.walk.other': '{{count}} dogs have gone for a walk.'
};
And you can then use the string in your Handlebars template via:
{{t dog.walk countBinding="dogCount"}}
The code above is untested and just taken from the documentation in the README.
Another JS I18n library I found is Alex Sexton's messageformat.js.
It depends on the complexity of you app, but you can also use a computed property for that, see http://jsfiddle.net/pangratz666/pzg4c/:
Handlebars:
<script type="text/x-handlebars" data-template-name="dog" >
{{dogCountString}}
</script>
JavaScript:
Ember.View.create({
templateName: 'dog',
dogCountString: function() {
var dogCount = this.get('dogCount');
var dogCountStr = (dogCount === 1) ? 'dog has' : 'dogs have';
return '%# %# gone for a walk.'.fmt(dogCount, dogCountStr);
}.property('dogCount')
}).append();
If you use Ember Data you can use Ember.Inflector.
var inflector = new Ember.Inflector(Ember.Inflector.defaultRules);
inflector.pluralize('person') //=> 'people'
You can register a new helper with:
Handlebars.registerHelper('pluralize', function(number, single) {
if (number === 1) { return single; }
else {
var inflector = new Ember.Inflector(Ember.Inflector.defaultRules);
return inflector.pluralize(single);
}
});
More details at http://emberjs.com/api/data/classes/Ember.Inflector.html
It looks like you got an answer from wycats himself, but I didn't see it mentioned in this thread, so here it is:
Handlebars.registerHelper('pluralize', function(number, single, plural) {
if (number === 1) { return single; }
else { return plural; }
});
I recently found this library http://slexaxton.github.com/Jed/ which seems to be a nice tool for JS i18n. I guess you can pretty easily create your own implementation by registering a handlebars helper using this library.
I do not know of any Ember specific functions that will do this for you. However, generally when you pluralize a word, the single version only shows up when the count is one.
See this for an example: http://jsfiddle.net/6VN56/
function pluralize(count, single, plural) {
return count + " " + (count == 1 ? single : plural);
}
pluralize(1, 'dog', 'dogs') // 1 dog
pluralize(10, 'dog', 'dogs') // 10 dogs
pluralize(0, 'dog', 'dogs') // 0 dogs
I'm just diving in to Ember. I'm looking for a way to pass a plain array of vanilla objects into a collection/controller and have them type cast to the correct model.
Here's the simple collection view:
{{#collection id="prods" contentBinding="Vix.prodsController" tagName="ul"}}
{{content.title}}
{{/collection}}
Here's the model:
Vix.Prod = Ember.Object.extend({
id: null,
title: null
});
And the controller:
Vix.prodsController = Ember.ArrayController.create({
content: []
});
Then let's get some JSON-formatted data from the server. In this example I'll just hard-code it:
var prods = [{id:"yermom1", title:"yermom 1"}, {id:"yermom2", title:"yermom 2"}]
Vix.prodsController.set('content', prods);
So far so good. I get my simple list of li elements displaying the titles as I'd expect. But when I want to update the title of one of the objects, using:
Vix.prodsController.objectAt(0).set('title', 'new title')
It complains because the object has no set method-- it has not been properly cast to my Vix.Prod Ember Object.
Using this alternative:
Vix.prodsController.pushObjects(prods);
Produces the same result. It's only if I explicitly create new model instances that I get the get/set goodness:
var prods = [Vix.Prod.create({id:"yermom1", title:"yermom 1"}), {Vix.Prod.create(id:"yermom2", title:"yermom 2"})]
Is there a way to automatically type cast those vanilla objects to my Vix.Prod Ember Object? If not, am I the only one that really wants something like that? In Backbone one can set the model property on a collection. I suppose I can create a setter on my Controller to do something similar- just wondering if there is something built-in that I'm missing. Thanks!
No magic. I'd suggest do a loop wrapping the model.
var prods = [{id:"yermom1", title:"yermom 1"}, {id:"yermom2", title:"yermom 2"}];
for (var i = 0; i < prods.length; i++) {
prods[i] = Vix.Prod.create(prods[i]);
}
If I use ember as much as I hope to, I'm going to want a shortcut. So here's what I've done for now. I created a base Collection class that I use to create my Collections/Controllers:
Vix.Collection = Ember.ArrayController.extend({
model: null,
pushObject: function(obj) {
if (this.get('model') && obj.__proto__.constructor !== this.get('model')) {
obj = this.get('model').create(obj);
}
return this._super(obj);
},
pushObjects: function(objs) {
if (this.get('model')) {
objs = this._typecastArray(objs)
}
return this._super(objs);
},
set: function(prop, val) {
if (prop === 'content' && this.get('model')) {
val = this._typecastArray(val);
}
return this._super(prop, val);
},
_typecastArray: function(objs) {
var typecasted = [];
objs.forEach(function(obj){
if (obj.__proto__.constructor !== this.get('model')) {
obj = this.get('model').create(obj);
}
typecasted.push(obj);
}, this);
return typecasted;
}
})
Now when I call pushObject, pushObjects, or .set('collection', data), if the collection instance has a defined model property and the objects being added to the collection aren't already of that type, they'll be cast. Been working good so far, but I welcome any feedback.
You should have a look at ember-data: https://github.com/emberjs/data
It seems to fit your needs...
As of today, it's not yet production ready (as stated in the readme), but is quickly converging toward maturity, thanks to an active development.