Sorting data by timestamp on ember - ember.js

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.

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.

Add second model to a route

My application is for swimming lessons. I need to add swimmers to a class. The relationship is has many in both directions. A Lesson can have many Swimmers and Swimmers can have many Lessons.
From the lesson route, I would like to select a swimmer in a drop down, from a list of all swimmers and have an action add that swimmer's ID to the lesson's "swimmers" array.
I can't get the swimmers to show up in the drop down field because I don't think I am loading the second model correctly.
I would also be open to suggestions of how to add a specific swimmer to a specific class. It's important to see all available swimmers
I am new to both ember and programming so please keep this in mind when making suggestions. Thank you!
App.Router.map(function() {
this.resource('lessons', { path: '/lessons' }, function() {
this.resource('lesson', { path: '/:lesson_id' })
this.route('new', { path: '/new' })
});
this.resource('swimmers', { path: '/swimmers' }, function() {
this.resource('swimmer', { path: '/:swimmer_id' })
this.route('new', { path: '/new' })
});
});
App.Lesson = DS.Model.extend({
type: DS.attr(),
name: DS.attr(),
/*level: DS.attr(), sometimes there are hybrid levels, likely leave it up to */
instructor:DS.belongsTo('instructor', {async: true}),
startDate: DS.attr(),
endDate: DS.attr(),
capacity: DS.attr('number'),
swimmers: DS.hasMany('swimmer',{async: true}),
});
App.Swimmer = DS.Model.extend({
nameFirst: DS.attr(),
nameLast: DS.attr(),
level: DS.attr(),
birthdate: DS.attr(),
gender: DS.attr(),
note:DS.attr(),
lessons: DS.hasMany('lesson', {async: true}),
});
App.LessonRoute = Ember.Route.extend({
model: function(params) {
return Ember.RSVP.hash({
lesson: this.store.find('lesson', params.lesson_id),
swimmers: this.store.findAll('swimmer')
})
},
setupController: function(controller, model) {
controller.set('model', model.lesson);
controller.set('swimmer', model.swimmer);
},
});
Drop down I am trying to use
<div class="form-group">
<label for="lesson_swimmers" class="col-sm-2 control- label">Swimmers</label>
<div class="col-sm-9">
{{view Ember.Select content=swimmers optionLabelPath="content.fullName" class="form-control" id="lesson_swimmers"}}
</div>
</div>
There are two answers to this question. The right one for you will depend on how you are providing the Model.
If you are arriving via transferTo or linkTo you will need to use a different approach than if you are arriving directly via URL.
#Kingpin2k answers the question and provides examples here,
Kingpin2k's elegant solution.

Unexpected text being rendered by Ember

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'),

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.

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";
}
})
})