Ember can't get data from hasMany relationship - ember.js

I try to build Rails+Ember app using Ember data.
Model:
export default DS.Model.extend({
name: DS.attr('string'),
content: DS.attr('string'),
image_before: DS.attr('string'),
created_at: DS.attr('date'),
updated_at: DS.attr('date'),
status: DS.attr('number'),
comments: DS.hasMany('comment', {async: true}),
user: DS.belongsTo('user'),
});
and get json like this (json is generated with active_model_serializers)
{"work":{
"id":3,
"user_id":1,
"content":"My first photo",
"image_before": "image_before/original/AnViWyuup5I.png",
"created_at":"2015-08-11T16:57:24.153Z",
"updated_at":"2015-11-13T11:39:44.076Z",
"status":1,
"comment_ids":[13]
},
"comments": [{
"id": 13,
"text": "good!",
"created_at": "2015-09-28T10:34:16.461Z",
"user_id": 2
}]
}
Template is:
<div class="work__img-wrapper">
<img class="work__img" src="{{model.image_before}}">
</div>
<div class="work__content">{{model.content}}</div>
<div class="work__comments-list">
<strong>Comments( {{model.comments.length}} ):</strong>
{{#each model.comments as |comment|}}
<div class="work__comment">
<div class="work__comment-text">{{comment.text}}</div>
</div>
{{/each}}
</div>
At page, I get all information about work and Comments( 0 ), but Ember Inspector at Chrome shows comment.
How i can print comments too?

Your model definition should read:
comments: DS.hasMany('comment', {async: false}),
because the related data is being side-loaded with the response, not being fetched asynchronously.
If this is an Ember 2.0 application, the default async setting is true. From the Ember Blog (http://emberjs.com/blog/2015/06/18/ember-data-1-13-released.html, emphasis mine):
In Ember Data 2.0 relationships will be asynchronous by default. Sync
relationships will still be supported but you will need to manually
opt into them by setting { async: false } on your relationships. Ember
Data 1.13 will log a deprecation warning you if you have any
relationships where the async property is not explicitly set.
Additionally you can use ember-watson to help identify cases in your
codebase where you have relationships without an explicit async
property.

Problem solved. By default, DS.hasMany associated IDs are not added to the objects that are serialized (http://emberjs.com/api/data/classes/DS.EmbeddedRecordsMixin.html).
Should add
export default DS.ActiveModelSerializer
.extend(DS.EmbeddedRecordsMixin)
.extend({
attrs: {
comments: {serialize: 'ids', deserialize: 'ids'}
}
});

Related

EmberJS filter hasMany data, apply to belongsTo relationship

I have this teams model:
export default DS.Model.extend({
"name": attr('string'),
"players": DS.hasMany('player',{
async: true
})
});
along with this player model:
export default DS.Model.extend({
"name": attr('string'),
"status": attr('string'),
"team": DS.belongsTo('team',{
async: true
})
});
If I want to list out all the teams, easy enough. And if I want to list out all the teams, and within each team, list out all the players... also simple enough.
{{#each teams as |team index|}}
<strong>{{team.name}}</strong></br>
<em>Players</em>
<ul>
{{#each team.players as |player|}}
<li>{{player.name}} - {{player.status}}</li>
{{/each}}
</ul>
{{/each}}
My QUESTION... let us say certain players are injured. If I'm looping through the teams, how would I go about ONLY displaying Teams that have injured players. If at least one player is injured, the TEAM will display, if not... the team will not display?
You can try filtering using isAny method for players
export default Ember.Route.extend({
model() {
return this.store.findAll('team').then((teams)=>{
return teams.get('players').then((players) => {
return players.isAny('status','injured');
})
})
}
})
Please take a look at this twiddle

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 app won't load data from has_many relationships using ActiveModelAdapter

This app works fine when the store is FixtureAdapter, but will not load has_many relationships when the store is ActiveModelAdapter.
The Route is like this:
App.IndexRoute = Ember.Route.extend({
model: function(){
return this.store.find('post', 1 )
}
});
The models are like this:
App.Post = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
comments: DS.hasMany('comments', {embedded: 'always'})
});
App.Comment = DS.Model.extend({
text: DS.attr('string'),
post: DS.belongsTo('post')
});
The handlebars template calls for:
{{name}}
{{#each comments}}
{{text}}
{{/each}}
The name appears, the comments do not.
In the ember developer console, the comments are not getting loaded at all.
The server seems to be serving the serialized content correctly (as far as I can tell). It looks like this:
{"post":{"id":1,"name":"Title","description":"Lorem Ipsum","comments":[{"id":1, "text": "commentary here"}]}}
Does anyone know why this isn't working and how I could fix it? I've been struggling with this for hours now and cannot figure out why it's not working. Thanks very much for any insight you can provide.
I'm using this as part of a Rails project, and these are the relevant gems:
gem 'ember-rails'
gem 'ember-source', '1.3.0'
gem 'ember-data-source', '~> 1.0.0.beta.6'
gem "active_model_serializers"
You need to specify a custom adapter for each over-riden model and mixin the Embedded Records Mixin:
App.PostSerializer = DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
comments: {embedded: 'always'}
}
});
Please see the comments in the Ember-Data source code for more info.

Ember.Select works sometimes but assertion fails

In my ember.js app, I have a 'user' model that has a "hasMany" relationship to 'group'. A user can be a member of zero or more groups. To allow the user to select the groups, I am using the built-in Ember.Select view.
If I load my users via the route /users, I can see the user and the groups to which that user is assigned. If I go to the edit route (/users/1/edit), I use Ember.Select to show the universe of all groups, along with the selection of that user's "selected" groups. Unfortunately, when I transition via the /users route, none of the groups are selected. If I refresh the page on the edit route, I see the groups correctly selected as I expect.
Another thing to note is that I don't see any errors when transitioning from /users to /users/1/edit (with no selected groups). However, when I refresh directly from the /users/1/edit route, the selection works correctly, but I see the following in the console (I am including a bit of the stack):
Assertion failed: The content property of DS.PromiseArray should be set before modifying it ember.js:394
(anonymous function) ember.js:394
Ember.assert ember.js:53
Ember.ArrayProxy.Ember.Object.extend._replace ember.js:16284
Ember.ArrayProxy.Ember.Object.extend.replace ember.js:16291
Ember.EnumerableUtils.replace ember.js:1829
Ember.Select.Ember.View.extend._changeMultiple ember.js:27933
Ember.Select.Ember.View.extend._change ember.js:27859
Ember.Select.Ember.View.extend._triggerChange ember.js:27902
sendEvent ember.js:2334
Any pointers would be helpful!
user_model.js:
Usermanagement.User = DS.Model.extend({
authenticateExternally: DS.attr(),
email: DS.attr(),
enabled: DS.attr(),
firstName: DS.attr(),
lastName: DS.attr(),
password: DS.attr(),
systemExternalAuthenticationEnabled: DS.attr(),
selectedGroups: DS.hasMany('group', {
async: true
}),
username: DS.attr(),
meta: DS.attr(),
fullName: function() {
return '%# %#'.fmt(this.get('firstName'), this.get('lastName'));
}.property('firstName', 'lastName'),
});
user_edit_template.hbs: (snippet)
<div class="field form-group">
<div class="fieldLabel">Groups</div>
{{view Ember.Select
multiple="true"
class="form-control"
selectionBinding="selectedGroups"
contentBinding="controllers.groups.allGroups"
optionLabelPath="content.name"
optionValuePath="content.id"}}
</div>
groups_controller.js:
Usermanagement.GroupsController = Ember.ArrayController.extend({
allGroups: function() {
return this.store.find('group');
}.property()
});
EDIT: Forgot to mention, Ember v1.0.0, Ember-data v1.0.0-beta3
The error is complaining about the select mucking with your model (user.selectedGroups) before it's finished loading.
The reason none are selected is probably because they are probably different objects. You might iterate over each item in the selected items and the allGroups options and check out the ember guid on it, if they are different items then that's why it's not showing them as selected.
Just out of curiosity, can you try setting the controller in the application route's setupController?
App.ApplicationRoute = Em.Route.extend({
setupController: function(controller, model){
this._super(controller, model);
this.controllerFor('groups').set('model', this.store.find('group'));
}
});
{{view Ember.Select
multiple="true"
class="form-control"
selectionBinding="selectedGroups"
contentBinding="controllers.groups.model" //instead of allGroups
optionLabelPath="content.name"
optionValuePath="content.id"}}

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