Ember - Many to many relationship data not being updated - javascript

I have a model for a speaker as follows:
import attr from 'ember-data/attr';
import ModelBase from 'open-event-frontend/models/base';
import { belongsTo, hasMany } from 'ember-data/relationships';
export default ModelBase.extend({
/**
* Attributes
*/
name : attr('string'),
email : attr('string'),
photoUrl : attr('string'),
thumbnailImageUrl : attr('string'),
shortBiography : attr('string'),
longBiography : attr('string'),
speakingExperience : attr('string'),
mobile : attr('string'),
location : attr('string'),
website : attr('string'),
twitter : attr('string'),
facebook : attr('string'),
github : attr('string'),
linkedin : attr('string'),
organisation : attr('string'),
isFeatured : attr('boolean', { default: false }),
position : attr('string'),
country : attr('string'),
city : attr('string'),
gender : attr('string'),
heardFrom : attr('string'),
/**
* Relationships
*/
user : belongsTo('user'),
event : belongsTo('event'),
sessions : hasMany('session')
});
And a model for a session as follows:
import attr from 'ember-data/attr';
import moment from 'moment';
import ModelBase from 'open-event-frontend/models/base';
import { belongsTo, hasMany } from 'ember-data/relationships';
import { computedDateTimeSplit } from 'open-event-frontend/utils/computed-helpers';
const detectedTimezone = moment.tz.guess();
export default ModelBase.extend({
title : attr('string'),
subtitle : attr('string'),
startsAt : attr('moment', { defaultValue: () => moment.tz(detectedTimezone).add(1, 'months').startOf('day') }),
endsAt : attr('moment', { defaultValue: () => moment.tz(detectedTimezone).add(1, 'months').hour(17).minute(0) }),
shortAbstract : attr('string'),
longAbstract : attr('string'),
language : attr('string'),
level : attr('string'),
comments : attr('string'),
state : attr('string'),
slidesUrl : attr('string'),
videoUrl : attr('string'),
audioUrl : attr('string'),
signupUrl : attr('string'),
sendEmail : attr('boolean'),
isMailSent: attr('boolean', { defaultValue: false }),
createdAt : attr('string'),
deletedAt : attr('string'),
submittedAt : attr('string', { defaultValue: () => moment() }),
lastModifiedAt : attr('string'),
sessionType : belongsTo('session-type'),
microlocation : belongsTo('microlocation'),
track : belongsTo('track'),
speakers : hasMany('speaker'),
event : belongsTo('event'), // temporary
creator : belongsTo('user'),
startAtDate : computedDateTimeSplit.bind(this)('startsAt', 'date'),
startAtTime : computedDateTimeSplit.bind(this)('startsAt', 'time'),
endsAtDate : computedDateTimeSplit.bind(this)('endsAt', 'date'),
endsAtTime : computedDateTimeSplit.bind(this)('endsAt', 'time')
});
As is clear, session and speaker share a many-to-many relationship. So I am adding the session to the speaker and then saving them. Both the records are successfully created on the server but the link is not established. I have tested the server endpoint with postman and it works fine. So, I guess I am missing something here.
This is the controller code:
import Controller from '#ember/controller';
export default Controller.extend({
actions: {
save() {
this.set('isLoading', true);
this.get('model.speaker.sessions').pushObject(this.get('model.session'));
this.get('model.session').save()
.then(() => {
this.get('model.speaker').save()
.then(() => {
this.get('notify').success(this.get('l10n').t('Your session has been saved'));
this.transitionToRoute('events.view.sessions', this.get('model.event.id'));
})
.catch(() => {
this.get('notify').error(this.get('l10n').t('Oops something went wrong. Please try again'));
});
})
.catch(() => {
this.get('notify').error(this.get('l10n').t('Oops something went wrong. Please try again'));
})
.finally(() => {
this.set('isLoading', false);
});
}
}
});

As #Lux has mentioned in comment, ember-data does not serialize has-many relationships in all cases by default. This is true for all Serializers that extend DS.JSONSerializer and not overriding shouldSerializeHasMany() method. This is the case for DS.JSONAPIAdapter and DS.RESTSerializer.
The logic used to determine if a many-relationship should be serialized is quite complex and not well documented. So looking in source code is needed for details.
In general a has-many relationship is serialized if and only if:
If enforced by Serializer configuration. To configure Serializer to enforce serializing a specific relationship, the attrs option must contain a key with relationship name holding an object { serialize: true }.
If not forbidden by attrs configuration of Serializer. It's the opposite of 1. The attrs option contains a key with relationship name holding an object { serialize: false }.
If it's a many-to-none relationship meaning there is no inverse relationship.
If it's a many-to-many relationship.
Accordingly to your question, a many-to-many relationship is not serialized. There could be many things causing your issue but I bet it's this one:
If I got you right, both records of the many-to-many relationship are not yet persisted on server. I assume you don't generate IDs client-side. So both records won't have an ID at beginning. Therefore the relationship can't be serialized on first create request (this.get('model.session').save()). Your API response well to this request. The response includes the information that this record does not have any related speaker. ember-data updates it's store with the information returned by your API. This update includes removing the relationship created before. The second create request (this.get('model.speaker').save()) serializes the relationship but as not existing since that's the current value in store.
If that's the case, you could simply create one of the records before assigning the relationship and afterwards save the other one, which will persist the relationship on server.

As suggested by #jelhan, I had to save on the models first and then add the relationship. The following code worked:
import Controller from '#ember/controller';
export default Controller.extend({
actions: {
async save() {
try {
this.set('isLoading', true);
await this.get('model.speaker').save();
this.get('model.speaker.sessions').pushObject(this.get('model.session'));
await this.get('model.session').save();
await this.get('model.speaker').save();
this.get('notify').success(this.get('l10n').t('Your session has been saved'));
this.transitionToRoute('events.view.sessions', this.get('model.event.id'));
} catch (e) {
this.get('notify').error(this.get('l10n').t('Oops something went wrong. Please try again'));
}
}
}
});

Related

Ember not showing foreign key field data

I have four models
//models/exam.js
name: attr('string'),
owner: belongsTo('user'),
//models/question.js
content: attr('string'),
exam: belongsTo('exam')
//models/answer.js
owner: belongsTo('user'),
question: belongsTo('question'),
answer: attr('string'),
remarks: attr('string'),
exam: belongsTo('exam')
//models/user.js
owner : attr('string'),
email : attr('string'),
password : attr('string'),
I load the models into a route. Then, when I run the following template code,
{{#each model.answers as |ans|}}
<p>{{ans.question.content}}</p>
{{/each}}
//route.js
import Route from '#ember/routing/route';
import { hash } from 'rsvp';
export default Route.extend({
model: function(params){
return hash({
student: this.store.findRecord('student',params.id),
answers: this.store.query('answer',{
owner: params.id
}),
});
}
});
it shows the output as follows
<frontend#model:question::ember276:5>
<frontend#model:question::ember281:6>
<frontend#model:question::ember286:4>
why is it showing such an code, why not showning the original content?
I think you encountered a very special and rare case. The content has a special meaning for ember relationships. That's internal stuff and an end-user should not deal with it. But that's the reason, why you get
<frontend#model:question::ember276:5>
for
{{ans.question.content}} {{!-- .content doesn't return the content attribute --}}
I would work around it by changing the attribute's name at server and ember-model. If I server's attribute-name is not mutable, I would customize the ember-serializer. I.e.:
//app/serializers/person.js (ember g serializer question)
import DS from 'ember-data';
export default DS.JSONAPISerializer.extend({
attrs: {
questionContent: 'content' //map server's attribute content to ember-model's questionContent
}
});

Error while processing route: home No model was found for 'App.undefined'

DEBUG: Ember : 1.7.1
DEBUG: Ember Data : 1.0.0-beta.12
DEBUG: Handlebars : 1.1.2
DEBUG: jQuery : 1.10.2
Having an issue with what I believe is the belongsTo attribute on my user model. (This happens on my other belongsTo attributes within my application as well). I have a Django backend which returns a response when I comment out the network: attribute.
{
email: "test#test.com",
first_name: "Test",
global_code: "daht64q691zy4k887ch",
global_id: "GBID-USER-dat64q6917zy4k887ch",
institution_gbid: "GBID-GINS-567j53ey0lojsu2kys",
institution_name: "Some University",
last_name: "Testing",
network: { },
view_policy: {
capability: "system:view",
description: "Anyone can view a user",
hold: true,
id: "daht64q691y4k887ch:system:view",
values: ""
}
}
Code for the User Model:
App.User = DS.Model.extend({
first_name: DS.attr('string'),
last_name: DS.attr('string'),
global_id: DS.attr('string'),
network: DS.belongsTo('basicgrouping')
}):
Code for Basic Grouping model:
App.Basicgrouping = DS.Model.extend({
global_id: DS.attr('string'),
name: DS.attr('string'),
gbid_code: function(){
return getGBIDCode(this.get('global_id'));
}.property('global_id')
});
Debugging ember-data I placed a console.log() within the following code:
relationshipsByName: Ember.computed(function() {
var map = Map.create();
this.eachComputedProperty(function(name, meta) {
console.log(name, meta);
if (meta.isRelationship) {
meta.key = name;
var relationship = relationshipFromMeta(this.store, meta);
relationship.type = typeForRelationshipMeta(this.store, meta);
map.set(name, relationship);
}
});
This seems to show that the type of the object that it belongs to is not being found (Basicgrouping) as it's returning App.undefined.
My theory is it may have something to do when parsing the server response and maybe the payload response. This also happens in other belongTo relationships in my code.
It turns out that there was a file that was overriding some of the DS. methods and causing an empty type to be sent. I was in the process of removing use of the shim but didn't know that it was being used.
Thanks the Bmacs from the ember community for the help debugging the issue.

Ember You must include an 'id' for customer in an object passed to 'push'

I am using ember 2.7.0.I got the following data from resp API,
{
"accounts": [
{
"id": "57a3660793d4ba3a5b78a780",
"status": null,
"crid": "Someone",
"deleted": false,
"accountInfo": {
"iccid": "accountId",
"iccidValue": "accountValue"
},
"subscriptionInfo": null
},
{
"id": "57a3660793d4ba3a5b78a780",
"status": null,
"crid": "Someone",
"deleted": false,
"accountInfo": {
"iccid": "accountId",
"iccidValue": "accountValue"
},
"subscriptionInfo": null
}
],
"user": {
"id": "288607702394",
"isdn": "491622897075",
"pcc": null
}
}
Created Model for Customer which contain details of all the accounts,users etc:
Customer.js (Model)
export default DS.Model.extend({
device:DS.hasMany('device'),
user:DS.belongsTo('user')
});
accounts.js(Model)
status: DS.attr(),
id: DS.attr(),
status: DS.attr(),
crid: DS.attr(),
deleted: DS.attr(),
accountInfo: DS.attr(),
subscriptionInfo: DS.attr()
User.js (Model)
export default DS.Model.extend({
isdn: DS.attr(),
pcc: DS.attr()
});
Created serializer for all the above 3-Models
Customer.js
export default ApplicationSerializer.extend({
normalizeResponse(store, primaryModelClass, payload, id, requestType){
console.log("Entered normalizeResponse",payload );
return this._super(...arguments)
}
});
Device.js
export default ApplicationSerializer.extend({
primaryKey: 'id'
});
user.js
export default ApplicationSerializer.extend({
primaryKey: 'id'
});
Issue is while running the application i am getting the following issue in chrome console : Assertion Failed: You must include an 'id' for customer in an object passed to 'push'
I do not know how to assign 'id' for customer model.Actually i would like to assign user model id as customer model is that possible? How can achieve it in customer model serializer?In the normalizeResponse of customer serializer i am able to see my response.
Note : I cannot modify the rest response.So option is left with modification of ember data model.
The way i am calling the REST API GET in my controller is
var customer = this.store.findRecord('customer', this.get('customerID')).then((customer) => {
console.log("customer::",customer);
},(resp,status) => {
console.log("resp:status:",resp,status);
});
It always entered in to the error response.
Your help should be appreciable.
I am not sure where you are storing your data and what type of API you are using but in my experiences (using a PHP or Loopback api) I have set the id variable able to be null (not required). On the database side I have used mySQL and I set the id field to auto increment. Ember already expects the ID and does not allow you to edit or create the id

How to describe hasMany relation using links in Ember.js?

I created model Consultation:
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr('string'),
records: DS.hasMany('record', { async: true }),
currentUser: DS.belongsTo('user'),
remoteUser: DS.belongsTo('user')
});
And also I created model Record:
import DS from 'ember-data';
export default DS.Model.extend({
record_text: DS.attr('string'),
record_poster_id: DS.attr('string'),
record_consultation_id : DS.attr('number'),
consultation: DS.belongsTo('consultation'),
isMine: DS.attr('boolean')
});
At first all consultations load during opening page. And I don't want to load all records of each consultation during opening page. To do this I added async: true but all records loaded simultaneously sending many requests like /consultations/:id/records. After that consultation and records still non-joined. I have next json response for consultation:
{
"consultations":[
{
"id":140721,
"title":"ExpertId: 41217, clientId: 0",
"links":{
"records":"records"
},
"currentUser":41217,
"remoteUser":159984
},
......
]
}
And for records:
{
"records":[
{
"record_id":681952,
"record_consultation_id":140721,
"record_poster_id":0,
"record_text":"1",
},
........
]
}
I think I need to override default serializer. I tried to create serializer for Record:
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
primaryKey: 'record_id',
keyForRelationship: function(key, kind) {
return 'record_consultation_id';
}
});
But it still doesn't work.
Please advise how to join models using links?
UPDATE:
Template
{{#each item in model.records}}
<div class="table message">
{{item.record_text}}
</div>
{{/each}}
I am doing Async (lazy loading) of data using RESTAdaptor and Ember-Data too.
For my links area, i put in the full request URL as follows:
"links":{
"records":"/consultations/140721/records"
},
And using firebug to look at when the request gets sent off, its only when I request for the async content that the AJAX gets fired off.
model.get('records');
Can you provide your Controllers & Template code so I can see how your accessing the Records?

Returned object's Id is ZERO ( creating a record in database )

When I tried to print out the User using console.log(user); I discovered that the id was zero. However, in the database(mysql), the user id is showing the correct value, which is the uuid.
Following is my User model
module.exports = {
tableName : 'user',
autoPK : false,
attributes: {
id: {
type : 'string',
unique : true,
primaryKey : true,
uuidv4 : true,
columnName: 'id',
defaultsTo : uuid.v4()
},
// { ..... other fields}
}
Following is my create function :
User.create(userObj).done(function(err, user){
console.log(user);
.... // other codes
}
Ah, it looks like you uncovered a bug in the latest beta version of sails-mysql. It has now been fixed and published, so if you delete your node_modules/sails-mysql folder and npm install sails-mysql#beta you should be all set. Thanks!

Categories

Resources