Can't get data from a computed property with ember-model - ember.js

I am making an app with ember.js and ember-model
I have a model named Plugin defined as follows:
Eme.Plugin = Ember.Model.extend
id: Ember.attr()
name: Ember.attr()
description: Ember.attr()
downloads: Ember.attr()
tags: Ember.attr()
Eme.Plugin.url = "/api/v1/plugins"
Eme.Plugin.adapter = Ember.RESTAdapter.create()
Eme.Plugin.collectionKey = 'plugins'
I want show the most downloaded in index.hbs ( i use ember-rails)
And i fetch data in IndexRoute 's setupController hook:
Eme.IndexRoute = Em.Route.extend
setupController: (controller, model)->
console.log Eme.Plugin.findAll().toArray()
controller.set 'plugins', Eme.Plugin.findAll()
Output :
[nextObject: function, firstObject: undefined, lastObject: undefined, contains: function, getEach: function…]
But in chrome console i execute Eme.Plugin.findAll().toArray(), i got the results as follows:
[{
__ember1377710636537: "ember404"
__ember1377710636537_meta: Meta
_dirtyAttributes: Array[0]
_reference: Object
_super: undefined
get _data: function() {}
isLoaded: true
isNew: false
set _data: function(value) {}
__proto__: Object
}, {
...
}, {
...
}]
In my IndexController have a computed property:
Eme.IndexController = Em.Controller.extend
mostDownloads:(->
# console.log #get('plugins').slice(0, 3)
#get('plugins').slice(0, 3)
).property('plugins')
and i iterate the mostDownloads but there is nothing to show, however when i output {{plugins.length}}, i can't get the count of all my data
Who can give a hand to me?

Plugins looks like an array and would need to use the .#each iterator like so:
Eme.IndexController = Em.Controller.extend({
// Code
}).property('plugins.#each')
Here is documentation on #each http://emberjs.com/guides/object-model/computed-properties-and-aggregate-data/
Regarding your array length, I've never had much luck using .length, for length I usually do
plugins.get('length')
Hope that helps!

I propose two changes to make you app working.
First
I assume since it's called plugins (plural) the call to .findAll() returns an array of plugins for this to work you should change your controller type to be an ArrayController. Then because you are using # aka. this in your computed property you should use the fat arrow => to have the right reference to this, so the resulting IndexController should look like:
Eme.IndexController = Em.ArrayController.extend
mostDownloads:(=>
# console.log #get('content').slice(0, 3)
#get('content').slice(0, 3)
).property('content.[]')
Notice also that we observe content.[] this will trigger whenever the content array changes, items added or removed etc. you could also use content.#each but this is better suited for when you need to observe changes to the properties of a plugin record in the array, e.g. content.#each.name.
Second
Now change also how you set the plugins collection on your controller, you should rather set the controller's content property since this is what it is for:
Eme.IndexRoute = Em.Route.extend
setupController: (controller, model)->
# console.log Eme.Plugin.findAll().toArray()
controller.set 'content', Eme.Plugin.findAll()
This line console.log Eme.Plugin.findAll().toArray() will not work the way you expect because when you call it it will give you a promise back and not the array that is still underway (async...).
And a last change, to print out the plugins length, use the afterModel hook of your IndexRoute, since this is the right time when the model promise has being resolved (the async operation has given back control to your app).
Eme.IndexRoute = Em.Route.extend
...
afterModel: (plugins, transition) ->
console.log plugins.get 'length'
...
Hope it helps.

Related

Ember bind controller property to query in another controller

So I have a requirement that there should be a panel displaying a users unsubmitted widgets. Since it has to be in every view, I made into a component that accepts a binding to a count of unsubmitted widgets:
# components/unsubmitted-widgets.emblem
.well.text-center
a href="#" My Widgets (#{unsubmittedWidgetsCount})
button.small-button type="button" click="submitWidgets"
strong Submit Widgets
I was thinking that the query for the widgets API would go into the application controller, which all other controllers can bind to
App.ApplicationController = Ember.Controller.extend
unsubmittedWidgets: (->
#store.find('unsubmittedWidget', user: #get('currentUser'))
).property()
App.HomeController = Ember.Controller.extend
needs: ["application"]
unsubmittedWidgetCount: (->
#get('controllers.application.unsubmittedWidgets').toArray().length
).property('controllers.application.unsubmittedWidgets')
So this fires off the request and I get a result. However the view doesn't get updated automatically. The view shows My Widgets() on whatever screen I'm on, and when I transition to another route where the view is present, I get the real value, but when I go back to the original page it's still not displaying everything.
How would I actually go about appropriately binding the value on the page to the length of the returned record set?
When you create a property that has a collection as dependency, you must use #each, like so:
App.HeroesController = Ember.Controller.extend({
identities: [
Em.Object.create({ mask: 'Captain America', name: 'Steve Rogers', isAvenger: true }),
Em.Object.create({ mask: 'Iron Man', name: 'Tony Stark', isAvenger: true }),
Em.Object.create({ mask: 'Batman', name: 'Bruce Wayne', isAvenger: false }),
],
justiceLeague: function() {
var identities = this.get('identities');
return identities.filterBy('isAvenger', false).get('length');
}.property('identities.#each.isAvenger'),
avengers: function() {
var identities = this.get('identities');
return identities.filterBy('isAvenger', true).get('length');
}.property('identities.#each.isAvenger')
});
The #each will run your computed property code to get the count of items that match whenever one or more objects in the identities array gets the isAvenger property updated. For this example, it should show a count of two characters, considering one out of the 3 items has the filtered property set to false. The other list, watches the exact same path, but the "formula" is different, outputting the count of 1 character for the example above.

How to get model properties

I have model:
App.Item = DS.Model.extend({
itemId: DS.attr('string'),
itemName: DS.attr('string'),
itemType: DS.attr('string'),
});
I successfully create some items from JSON. I can put them to page by {{#each items}}{{ itemName}}{{/each}}. But I don't know, how to get itemName in javascript.
I tried this:
var item = App.Item.find(1);
console.log(item.itemName);
--> undefined
I can't find anything useful from emberjs and ember-data docs. Can anyone help me?
Thanks
I tried this:
var item = App.Item.find(1);
console.log(item.itemName);
--> undefined
This is normal because the call to .find(1); is asyncronous and returns a promise and not the item you are expecting.
Therefore you should try:
App.Item.find(1).then(function(result) {
console.log(record.get('itemName'));
});
It also depends from where you are doing App.Item.find() if it's from inside a route you should wait until the afterModel hook is called to access your items:
App.FooRoute = Ember.Route.extend({
model: function() {
return App.Item.find(1);
},
afterModel: function(record) {
console.log(record.get('itemName'));
}
});
Also be aware that if you where calling find() without parameter then you will receive a RecordArray which you need to loop over to get access to your items. Also worth mentioning is that in ember you should always use .get() and .set() instead of the vanilla dot-notation otherwise you hijack the binding mecanism resulting in no updates in your view etc.
Note, if you are using the latest ember.js release (1.0.0) then the call to .find() should be made somewhat different. But that's not clear from your question.
Hope it helps.

Ember - correct way to get the content value in one controller

Apologies for my english,
I am starting to learn ember.js and now i cant read the content property in one controller.
I have one route defined as:
App.FilmsIndexRoute = Em.Route.extend
model: ->
App.Film.find()
setupController: (controller, model) ->
controller.set('content', model)
and the controller to this route:
App.FilmsIndexController = Ember.ArrayController.extend
init: ->
console.log "entra en films-index"
console.log #get('content')
numPeliculas:(->
#get('length')
).property('length')
In the view, numPeliculas works perfectly, i get the length of the model resource´s array, but the console doesn´t return the value of content.
how is the correct way to get the content value in one controller ?
Thanks in advance
When the controller is initialized, it knows nothing about its content. The content property is set in setupController after the controller has been initialized.
#get('content') is a perfectly fine way to get content inside controller, you just have to do it after you've set the content property.

When my computed property is updated the linkTo attr does not get update in ember RC1

Here is the linkTo helper in my handlebars template
{{#linkTo 'person.page' nextPage target="controller"}}Next{{/linkTo}}
Here is my controller
PersonApp.PersonController = Ember.ArrayController.extend(Ember.PaginationMixin, {
itemsPerPage: 2
});
Here is the computed property in the mixin
nextPage: function() {
var nextPage = this.get('currentPage') + 1;
var availablePages = this.get('availablePages');
if (nextPage <= availablePages) {
return Ember.Object.create({id: nextPage});
}else{
return Ember.Object.create({id: this.get('currentPage')});
}
}.property('currentPage', 'availablePages'),
when I console log just before each return statement I can see the page id is correct ... yet my html isn't updated. Anything simple that I'm doing wrong in the above?
Also I do see a print each time I change the page (so the computed properties I depend on are being fired)
Here is a full blown jsfiddle showing that after you click next the first time ... it still points at /#/page/2 instead of /#/page/3
http://jsfiddle.net/RhTyx/2/
Thank you in advance
First off: It would be nice, if you would not link a fiddle where the most important code is not part of the fiddle (the FilterSortSliceMixin). Therefore one cannot test anything, despite the fact the fiddle was really huge and contained lots of unnecessary code.
Regarding your problem:
I think this cannot work because the dependent properties you specified do not do anything. Your {{#linkTo}} helper sends the user into the the PersonPageRoute. The code of this route is:
PersonApp.PersonPageRoute = Ember.Route.extend({
model: function(params) {
return PersonApp.Person.find(params.page_id);
},
setupController: function(controller, model) {
this.controllerFor('person').set('selectedPage', model.get('id'));
}
});
So you are getting the personcontroller and set the property selectedPage. But this property is not specified in your dependent keys. So therefore i would suggest this:
//maybe even remove currentPage??
}.property('currentPage', 'availablePages' , 'selectedPage'),
So i guess you got confused with your naming. I guess, you should either have the property 'selectedPage' or 'currentPage', right?
Update: This is definitely a bug. Heres an excerpt from the LinkView class, which is used with the linkTo helper:
var LinkView = Ember.View.extend({
attributeBindings: ['href', 'title'],
href: Ember.computed(function() {
var router = this.get('router');
return router.generate.apply(router, args(this, router));
})
});
As you see it does not specify any dependent keys. Maybe you can reopen/patch this class and add the dependent key. Don't know which one this would have to be, but maybe context?

How to change DS.Model url in ember-data?

I am new(to ember) and trying to build a search centric Ember App w/ Ember-data also. I wanted to change the url on the fly(based on search string) and the data should change automatically(on the fly). How to do it?
This is my not working code:
Emapp.Data = DS.Model.extend({
first_name: DS.attr('string')
}).reopenClass({
url: Emapp.MyURL.get('url')
});
Emapp.MyURL = Em.Object.create({
urlParam: 'John',
url: function()
{
return 'emb/data.php?id=%#'.fmt(this.get('urlParam'));
}.property('urlParam')
});
When I execute. emapp.MyURL.set('urlParam', 'Adams'). I can inspect and see the url changed to 'Adams'. But data is not fetched again.
Edit: emapp -> Emapp (pointed out by rudi-angela)
As you have made the 'url' property a computed property, Ember takes care of updating this value when the urlParam changes. That is all you have instructed Ember to do here (and apparently it is doing it properly).
But I reckon what you want here is any change in the 'urlParam' property to trigger a fetch action. In that case a solution would be to create a separate object that observes the urlParam and will take action when the 'urlParam' value changes. Something along these lines:
emapp.watcher = Ember.Object.create({
valueBinding: "emapp.MyURL.urlParam",
observer: function() {
console.log("urlParam has changed:");
// perform your fetch here
}.observes("value"),
});
Note: I thought there was a requirement for the namespace to be capitalised (rather Emapp instead of emapp).