Unexpected text being rendered by Ember - ember.js

I have models
// models/group
export default DS.Model.extend({
parent: DS.belongsTo('parent'),
items: DS.hasMany('item', {async: true}),
quantity: Ember.computed.sum('items.#each.quantity'),
});
// models/item
export default DS.Model.extend({
...
quantity: DS.attr('number')
});
And in my template (with controller.model set to parent) I try to render
{{#each group}}
{{quantity}}
{{/each}}
and expect a list of numbers, but instead what's rendered is a list of text like <spa#model:item::ember1036:165>
I'm guessing that the async promise is only resolved after rendering, but then why does it not update?

I don't believe sum will pull properties from each item in a collection. I believe it has to be a collection of numbers.
quantities: function(){
return this.get('items').getEach('quantity');
}.property('items.#each.quantity'),
quantity: Ember.computed.sum('quantities'),

Related

How to sort Ember Data model output?

I've searched and searched, but all the examples seem either outdated, aren't in the file structure enforced by Ember CLI, etc.
Anyway, say I have a model in app/models/employee.js:
export default DS.Model.extend({
firstname: DS.attr('string'),
lastname: DS.attr('string'),
});
And a route in app/routes/employees.js:
export default Ember.Route.extend({
model: function() { return this.store.findAll('employee'); },
});
And a template in app/routes/employees.hbs:
{{#each model as |employee|}}
<tr><td>{{employee.firstname}}</td><td>{{employee.lastname}}</td></tr>
{{/each}}
What do I need to add to sort that table by firstname, for example?
I gather I'm supposed to use Ember.computed.sort(), something like:
sortedEmployee: Ember.computed.sort('employees', ['firstname'])
And then do {{#each sortedEmployee as ...}} in the template, but I'm apparently not defining sortedEmployee in the right place.
app/controllers/employees.js
export default Ember.Controller.extend({
sortProperties: ['firstname:asc'],
sortedEmployees: Ember.computed.sort('employees', 'sortProperties')
});
app/routes/employees.hbs:
{{#each sortedEmployees as |employee|}}
{{employee.firstname}}
{{/each}}
Example in JSbin: http://emberjs.jsbin.com/regowa/7/edit?html,css,js,output
You are on the right track, try the following:
sortProperties: ['firstname:asc'], // or just 'firstname', or 'firstname:desc'
sortedEmployee: Ember.computed.sort('employees', 'sortProperties')
I think it's weird to have to define an extra property on the component/controller, but that way it works.

Sorting data by timestamp on ember

I've looked at a few examples, but haven't been able to reverse-sort, so that newly generated objects are on top.
My sortable items are in components, and I don't think I'm passing sortProperties & sortAscending correctly.
lavender.js:
export default Ember.Controller.extend({
needs: ['application'],
sortProperties: ['timestamp'],
sortAscending: false
});
lavender.hbs
{{#each model.comment as |comment|}}
{{comment-thread message=comment.message user=comment.user timestamp=comment.timestamp sortProperties=sortProperties sortAscending=sortAscending}}
{{/each}}
comment.js
export default DS.Model.extend({
message: DS.attr('string'),
timestamp: DS.attr('date'),
user: DS.belongsTo('user', {async: true}),
todo: DS.belongsTo('todo', {async: true}),
});
todo.js (model for lavender.js)
export default DS.Model.extend({
title: DS.attr('string'),
isCompleted: DS.attr('boolean', {defaultValue: false}),
detail: DS.attr('string', {defaultValue: "add details.."}),
comment: DS.hasMany('comment', {async: true}),
});
There must be something I'm not seeing.. thank you!
You have to use deprecated Ember.ArrayController instead of Ember.Controller if you want your approach to work or you can choose other approach.
The best approach is to use Ember.computed macro:
export default Ember.Controller.extend({
needs: ['application'],
commentsSorting: ['timestamp:desc'],
comments: Ember.computed.sort('model.comment', 'commentsSorting')
});
Then, instead of model, iterate over comments in your template.
You can also use computed property and private(discouraged) Ember.ArrayProxy, like this:
export default Ember.Controller.extend({
needs: ['application'],
comments: Ember.computed('model', 'model.comment', function() {
return Ember.ArrayProxy.createWithMixins(Ember.SortableMixin, {
sortProperties: ['timestamp'],
sortAscending: false,
content: this.get('model.comment')
});
})
});
Then you can iterate over comments in your template:
{{#each model.comment as |comment|}}
{{comment-thread message=comment.message user=comment.user timestamp=comment.timestamp}}
{{/each}}
I don't think you need to pass sort properties to comment-thread, I don't you've misunderstood how this works. It gets sorted in controller, where are all records, not in component, where you have only 1 record per 1 component and no reference to other records.

Ember computed property to return first X number of items from list

I have a one-to-many relationship (using Ember Data). All I want to do is list the first N number of items from that relationship in an overview (index) template. I'm trying to use the Array.slice method but it doesn't seem to return anything at all.
Here's what I have right now:
models/account.js
// Account model
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
notes: DS.hasMany('note', { async: true })
});
models/note.js
// Note model
import DS from 'ember-data';
export default DS.Model.extend({
body: DS.attr('string'),
date: DS.attr('number'), // unix timestamp
account: DS.belongsTo('account', { async: true })
});
controllers/account/index.js
// account/index controller
import Ember from 'ember';
export default Ember.ObjectController.extend({
firstNotes: function() {
return this.get('notes').slice(0,2);
}.property('notes')
});
templates/account/index.hbs
{{!-- this lists all the associated `Notes` --}}
{{#each notes}}
{{date}}<br>
{{body}}
{{/each}}
{{!-- this doesn't list anything!!?? --}}
{{#each firstNotes}}
{{date}}<br>
{{body}}
{{/each}}
I figured this out just as I was about to post it so I figured I'd answer it...
All I was missing was a #each in the computed property dependency. So it works as expected with this:
firstNotes: function() {
return this.get('notes').slice(0,2);
}.property('notes.#each')
Simple.

Getting data from DS.PromiseArray in EmberJS

I'm new to emberJS and I'm having some trouble working with promises.
Here is my router:
this.resource('menus', function(){
this.resource('menu', {path: '/:menu_id'}, function(){
this.resource('submodule', {path: '/:submodule_id'});
});
});
});
I have nested routes, and the child route returns a menuss object based on a given id.
Here is my MenuRoute:
App.MenuRoute = Ember.Route.extend({
model: function(params){
return this.store.find('menuss', params.menu_id);
}
});
Here are my models:
App.Menuss = DS.Model.extend({
name: DS.attr('string'),
subModule: DS.hasMany('submodule', {async:true})
});
App.Submodule = DS.Model.extend({
name: DS.attr('string'),
content: DS.attr('string')
});
The 'subModule' attribute of Menuss model contains an array of Submodule model id's.
Inside my menu template, I'm receiving a menuss object and I want to display the SubModules each menu item has.
However, when I call {{this.subModule}}, it returns <DS.PromiseArray:ember488>. How can I get the contents from this subModule array?
I looked at some similar questions where they say use the then() method, but I can't seem to figure it out here.
In the template you'll want to iterate the property since it's an array, ember/handlebars will deal with the synchronicity of the PromiseArray.
{{#each item in subModule}}
{{item.name}}
{{/each}}

duplicated data when navigating without reloading page using ember-data

I'm using ember.js 1.0.0-pre4, ember-data revision 11.
I have the following model:
App.DbProcess = DS.Model.extend({
pid: DS.attr('number'),
backendStart: DS.attr('string'),
transactionStart: DS.attr('string'),
queryStart: DS.attr('string'),
stateChange: DS.attr('string'),
waiting: DS.attr('boolean'),
state: DS.attr('string'),
query: DS.attr('string')
})
With the following route:
App.HomeDbProcessesRoute = Ember.Route.extend({
model: function() {
return App.DbProcess.find();
}
})
I then have a template which uses {{#each controller}}{{/each}} to render all the processes retrieved. However if I navigate to other pages (without reloading the page) and returning back to the processes page, the processes will be retrieved again and the duplicates are rendered on page.
EDIT: I also tried this, but it didn't work:
DS.RESTAdapter.map('App.DbProcess', {
primaryKey: 'pid'
})
I had the same issue now and here is my little hot-fix:
{{#if id}}
<div>
{{title}}
</div>
{{/if}}
In the template I render item from store only if it has id set (only those are coming from databse). But You propably solved it already!
(using revision 12)
Turns out you can do something like this to customize the primary key globally
App.Adapter = DS.RESTAdapter.extend({
url: document.location.protocol+'//url-api.com',
serializer: DS.RESTSerializer.extend({
primaryKey: function(type) {
// If the type is `BlogPost`, this will return
// `blog_post_id`.
var typeString = (''+type).split(".")[1].underscore();
return typeString + "_id";
}
})
})