Struggling with sorting data in ember - ember.js

I have an ember route that gets the following data:
return this.store.findAll('servicerequest');
One of the fields is a date field called created_at that I am trying to sort by.
Here is my servicerequest controller:
import Controller from '#ember/controller';
import { sort } from '#ember/object/computed';
export default Controller.extend({
appName: 'Service Requests',
sortAscending: false,
sortDefinition: ['created_at:desc'],
sortedRequests: sort('servicerequest', 'sortDefinition')
});
Here is the hbs section where I am outputting the data:
{{#each sortedRequests as |servicerequest|}}
{{servicerequest-list status=servicerequest.status srdescription=servicerequest.srdescription priority=servicerequest.priority created_at=servicerequest.created_at
current_user_id=servicerequest.current_user_id servicerequestId=servicerequest.id}}
{{/each}}
I have not been able to get anything to sort by the created_at field desc. What am I doing wrong?
UPDATE:
Here is my model:
import DS from 'ember-data';
export default DS.Model.extend({
requester_name: DS.attr('string'),
srdescription: DS.attr('string'),
priority: DS.attr('string'),
status: DS.attr('string'),
created_at: DS.attr('date'),
current_user_id: DS.attr()
});
The data is coming from a rails api. The created_at field is what the systems defaults to on records that is why there is an underscore.
I am using ember-cli: 3.0.2

So I ended up getting it working by doing this:
return this.store.findAll('servicerequest').then(results =>
results.sortBy('created_at').reverse());
Not sure if it is the best way but for now it's doing what I need.

Ideally any data operation should be performed at the backend.
Incase if there are any backend limitations, you can move the sorting logic snippet into the serializer. This way, any fetch call for 'servicerequest', will come sorted and redundant sorting logic can be avoided.
Something like =>
serialize(snapshot, options) {
snapshot.sortBy('created_at').reverse();
return this._super(...arguments);
}

Related

ember data not saving foreign key, sent as null

My ember app is not sending my foreign key to the back-end.
I have a table called issues which is has a related table called categories
My model is:
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
category_id: DS.belongsTo('category'),
description: DS.attr('string')
});
My route is:
import Ember from 'ember';
export default Ember.Route.extend({
model: function(){
return this.store.findAll('issue');
},
actions: {
create: function(){
var issue = this.store.createRecord('issue');
issue.name = this.get('controller').get('newName');
issue.description = this.get('controller').get('newDescription');
issue.category_id = parseInt(this.get('controller').get('newCategory'));
//debugger;
console.log(issue);
issue.save();
},
...
other actions
...
}
}
});
the console.log from above looks like the category_id is getting set correctly:
category_id: 3
description: "foobar"
name: "test"
However my JSON payload that gets sent to the backend looks like:
{"issue":{"name":"test","description":"foobar","category_id":null}}
I tried stepping through by adding a custom serialiser in app/serializers/application.js
export default DS.RESTSerializer.extend({
...
serialize: function(snapshot,options){
console.debug('options='+options);
debugger;
var json = this._super(snapshot, options);;
return json;
}
...
});
But I got lost in all the super calling super indirection.
The snapshot.record has category_id: 3, but the json coming back from the this._super() call has category_id: null
options has includeID:true
Any clues will be much appreciated ...
Ember : 2.0.2
Ember Data : 2.0.0
Your model definition is wrong, when dealing with relationships you define them just as you would define any other attribute, there is no need to use _id.
export default DS.Model.extend({
name: DS.attr('string'),
category: DS.belongsTo('category'),
description: DS.attr('string')
});
As for the creation you should always use setters/getters when dealing with ember objects:
create: function() {
var issue = this.store.createRecord('issue', {
name: this.get('controller').get('newName'),
description: this.get('controller').get('newDescription'),
category: this.get('controller').get('newCategory') // assuming new category is a DS.Model instance of category
});
issue.save();
}
If you wish to stick to the syntax you have you would use issue.set('name', this.get('controller').get('newName')), from the looks of your code it seems you are going about this in the wrong way.
You should have a this.route('new') nested under your issues route, that way you wouldn't have to use the controller to store information.
You would simply set the model of the new route to:
model: function() {
return this.store.createRecord('issue');
}
Your template would make use of the input helpers like so:
{{input value=model.name}} and your action would just get the currentModel and call .save().

Loading a snake case field with ember data using the JSONAPI Adapter

If I have a field that is in snake case coming back from the api, how should I define that field in the model? Im using the JSONAPIAdapter. It seems like fields that is one word work fine, but snake case fields come back as undefined.
This is how I have it defined in my model:
import DS from 'ember-data';
export default DS.Model.extend({
typecode_desc: DS.attr('string'),
contactnum: DS.attr('string'),
email: DS.attr('number'),
individual: DS.belongsTo('individual', {async: false})
});
And this is how the json comes back from the API:
1: {
id: "96"
type: "contact_infos"
attributes: {
typecode_desc: "E-mail address"
contactnum: "billybear#yahoo.com"
email: 1
}
}
However, in the ember inspector, typecode_desc comes back as being undefined. Is there something I need to do to tell ember that fields will come back as being snake case?
You need to define keyForRelationship in your JSON API serializer. It will look something like this:
import DS from 'ember-data';
import Ember from 'ember';
export default DS.JSONAPISerializer.extend({
keyForAttribute: function(attr) {
return Ember.String.underscore(attr);
},
keyForRelationship: function(attr) {
return Ember.String.underscore(attr);
}
});

What array name does Ember Data expect for sub directory models?

I recently started learning Ember and using Ember-CLI so I'm not quite well educated about Ember Data and what array names it expects for relationships that are in sub directories in my app.
// models/server.js
import DS from 'ember-data';
export default DS.Model.extend({
serverHistory: DS.hasMany("history/server", { async: true })
});
// models/history/server.js
import DS from 'ember-data';
export default DS.Model.extend({
server: DS.belongsTo("server", { async: true })
});
I've tried returning these names from my API
server_historys_ids
server_histories_ids
history_server_ids
history_servers_ids
But I don't see an XHR request for Server history in my application. The servers itself are fetched fine.
Update
I changed my relationship name and the API is returning history ids but I'm still not getting an history json request even though I'm trying to each in the template. The game relationship data is accessible in the template and a request is successfully made.
// models/server.js
import DS from 'ember-data';
export default DS.Model.extend({
// attr's here.. not relevant
// Relationships
game: DS.belongsTo("game", { async: true }), // works
serverHistories: DS.hasMany("history/server", { async: true }) // doesn't make a request like game does.
});
I also have an adapter/history/server.js but it's only telling what namespace to use - "api".
Update 2
I think the problem may be in the way I'm calling the data to the model.
// routes/server/view/index.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
var parentModel = this.modelFor("server.view");
return this.store.query("server", { server_address: parentModel.server_address });
// return this.store.find("server", 1);
}
});
How come when I use find with an id it updates the template data and when I use query with parameters it doesn't?
Update 3
So I got my find and query problem sorted out, here's the way I got it to work: https://stackoverflow.com/a/31831667/1814027
The relationship problem still persists. I see no serverHistory data in my Ember toolbar nor a request being made to the API for it.
I beleive serverHistory is anti-conventional name for hasMany and serverHistories should be instead.
export default DS.Model.extend({
serverHistories: DS.hasMany("history/server", { async: true })
});
Then in case of ActiveModelAdapter expected server payload is:
{"server": {"id": 1, "server_history_ids": [1,2,3]}}
It doesn't depend on the fact that serverHistory is namespaced model, it depends on relation name only.
For example for model:
// models/server.js
import DS from 'ember-data';
export default DS.Model.extend({
bars: DS.hasMany("history/server", { async: true })
});
expected payload is:
{"server": {"id": 1, "bar_ids": [1,2,3]}}
Update
Working ember-cli example: https://github.com/artych/so_ember_data_subdir
Artych's answer helped me on the right path but Ember didn't want to recognise server_history_ids so I just renamed the hasMany relation to histories and returned histories: [] from my API. Now it works.. don't know why but it works.

Decorating data from related model in Ember.js

I have a Property model and a Pricing Summary model, which relate to each other and are shown below:
App.Property = DS.Model.extend({
totalRoomCount: DS.attr(),
name: DS.attr(),
address: DS.attr(),
city: DS.attr(),
state: DS.attr(),
zip: DS.attr(),
pricingSummaries: DS.hasMany('pricingSummary', {async: true})
});
App.PricingSummary = DS.Model.extend({
startDate: DS.attr(),
endDate: DS.attr(),
days: DS.hasMany('day', {async: true}),
property: DS.belongsTo('property', {async: true})
});
Inside of my Property route I set the model to a Property, and then in the template, I want to output a list of the PricingSummary's that are related to that Property, as follows:
{{#each pricingSummary in pricingSummaries}}
{{render 'summaryRow' pricingSummary}}
{{/each}}
This works, and I'm able to output the attributes of each particular PricingSummary inside of the summaryRow template, like its startDate and endDate, for example. But what I REALLY want to do here is modify/format the startDate and output this formatted version. Basically I think I want a controller at this point, but I don't know how to tie a controller to the specific Pricing Summary model being output.
How do I do this? And furthermore, you can see that a PricingSummary also has a relationship to my Day model, so I'm going to want to do this again, another level deep.
Please help!
There are several ways to accomplish this, and all of them are relatively simple.
In relation to actually decorating a model, the easiest method would be to create a computed property on the model itself. Some people don't like this because they believe the models should be skinny and decorators should be in controllers/components, but it's all up to your preference. You could accomplish it this way:
App.YourModel = DS.Model.extend({
date: attr('date'),
formattedDate: function() {
var date = this.get('date');
return date ? this.get('date').toDateString() : null ; // Use your own format :-)
}.property('date')
});
Alternatively, I like to use a getter/setter pattern so you can use two-way bindings and it will marshal the value to a date on set, or to a string on get. In the following example, I'm using moment.js to parse/format:
App.YourModel = DS.Model.extend({
date: attr('date'),
dateMarshal: function(key, value) {
if (arguments.length > 1) {
var parsed = moment(value);
this.set('date', parsed.isValid() ? parsed.toDate() : null);
}
return this.get('date') ? moment(this.get('date')).format('MM/DD/YYYY') : null ;
}.property('date'),
});
Another option would be to provide an itemController property to the {{#each}} helper, but that's effectively the same as using render without having to use a custom view.
If you're using more properties and perhaps some actions on the pricing summary row (to delete it, for instance), my preference would be to use a component:
{{#each pricingSummary in pricingSummaries}}
{{pricing-summary-item content=pricingSummary}}
{{/each}}
And your component:
App.PricingSummaryItem = Ember.Component.extend({
content: null,
dateFormatted: function() {
var formattedDate = this.get('content.date');
// Format your date
return formattedDate;
}.property('content.date')
actions: {
'delete': function() {
this.get('content').deleteRecord();
},
markRead: function() {
this.set('content.isRead', true);
this.get('content').save();
}
}
});
Finally, to address JUST the date issue and not decoration, I would make a bound helper. Again, this example uses moment.js (and I'm using ember-cli as well, so pardon the ES6 syntax):
import Ember from 'ember';
export function formatDate(input, options) {
input = moment(input);
if (options.hashContexts.fromNow) {
return input.fromNow();
} else {
return input.format(options.hash.format || 'LL');
}
}
export default Ember.Handlebars.makeBoundHelper(formatDate);
Then you just use {{format-date yourDateProperty}} in your template.

Ember Data loading hasMany relation doesn't work in Ember App Kit (EAK)

I'm trying to set up a hasMany relationship between two models and a hasOne (belongsTo in the current version of Ember Data) between the hasMany and hasOne.
I'm working with Ember Data and have a made a RESTful API that works according to Ember's conventions. All the classes can be queried individually.
Bookmark = hasMany -> Termbinding
Termbinding = belongsTo -> Term
Term = belongsTo -> Termbinding
So the goal is to fetch a Bookmark and get the Terms that are attached to it through the Termbinding. I would already be pretty happy to get the Bookmark to Termbinding relation working. I went through all questions posted on here, sadly enough that didn't work.
Router.js
var Router = Ember.Router.extend();
Router.map(function() {
this.resource('bookmarks', { path:'bookmarks'});
this.resource('bookmark', { path:'bookmarks/:bookmark_id' });
this.resource('termbindings', { path:'termbindings' });
this.resource('termbinding', { path:'termbindings/:termbinding_id' });
});
export default Router;
Bookmark.js
var Bookmark = DS.Model.extend({
url: DS.attr('string'),
description: DS.attr('string'),
visits: DS.attr('number'),
termbinding: DS.hasMany('termbinding')
});
export default Bookmark;
Termbinding.js
var Termbinding = DS.Model.extend({
bookmarkId: DS.attr('number'),
termId: DS.attr('number'),
termOrder: DS.attr('number'),
bookmarks: DS.belongsTo('bookmark')
});
export default Termbinding;
I hope someone can help me because this is preventing me from using Ember for my bookmark application. Thanks in advance.
It might be wise to explicitly specify your inverses, i.e.
var Termbinding = DS.Model.extend({
bookmarkId: DS.attr('number'),
termId: DS.attr('number'),
termOrder: DS.attr('number'),
bookmarks: DS.belongsTo('bookmark', { inverse: 'termbinding' })
});
export default Termbinding;
var Bookmark = DS.Model.extend({
url: DS.attr('string'),
description: DS.attr('string'),
visits: DS.attr('number'),
termbinding: DS.hasMany('termbinding', { inverse: 'bookmarks' })
});
export default Bookmark;
Ember Data will try to map inverses for you, however, it is not without faults. It could possibly be that your pluralization of 'bookmarks' on a DS.belongsTo relationship is throwing off its automatic inverse mapping. Typically for belongsTo you would use the singular, 'bookmark'. Conversely, your hasMany would be termbindings: DS.hasMany('termbinding')
Also, if you could show where you're invoking the models that would be greatly appreciated. Typically I find that creating a JSbin at emberjs.jsbin.com helps me isolate the problem and also provides a collaborative space to debug and experiment.