Ember - 多对多关系数据未更新
Ember - Many to many relationship data not being updated
我有一个扬声器模型如下:
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')
});
一个会话的模型如下:
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')
});
很明显,session 和 speaker 共享多对多关系。所以我将会话添加到扬声器,然后保存它们。两条记录都已在服务器上成功创建,但未建立 link。我已经用邮递员测试了服务器端点,它工作正常。所以,我想我在这里遗漏了一些东西。
这是控制器代码:
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);
});
}
}
});
正如@Lux 在评论中提到的,ember-data 默认情况下不会在所有情况下序列化 has-many 关系。对于扩展 DS.JSONSerializer
and not overriding shouldSerializeHasMany()
method. This is the case for DS.JSONAPIAdapter
and DS.RESTSerializer
.
的所有序列化程序都是如此
用于确定是否应序列化多关系的逻辑非常复杂且没有详细记录。所以需要 looking in source code 了解详情。
一般来说,当且仅当:
如果由序列化程序配置强制执行。要配置 Serializer 以强制序列化特定关系,attrs
option 必须包含一个键,该键的关系名称包含一个对象 { serialize: true }
.
如果未被序列化程序的 attrs
配置禁止。它与 1 相反。 attrs
选项包含一个具有关系名称的键,其中包含一个对象 { serialize: false }
.
如果是多对none关系意味着没有反向关系。
如果是多对多关系。
根据您的问题,多对多 关系未序列化。可能有很多原因导致您的问题,但我敢打赌是这个:
如果我没看错,多对多关系的两条记录都还没有保存在服务器上。我假设您不在客户端生成 ID。所以这两个记录在开始时都没有 ID。因此,无法在首次创建请求 (this.get('model.session').save()
) 时序列化该关系。您 API 对此请求的响应很好。响应中包含此记录没有任何相关信息speaker
。 ember-数据使用您的 API 返回的信息更新它的存储。此更新包括删除之前创建的关系。第二个创建请求 (this.get('model.speaker').save()
) 序列化关系但不存在,因为这是存储中的当前值。
如果是这种情况,您可以在分配关系之前简单地创建一条记录,然后保存另一条记录,这将在服务器上保留关系。
按照@jelhan 的建议,我必须先保存模型,然后再添加关系。以下代码有效:
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'));
}
}
}
});
我有一个扬声器模型如下:
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')
});
一个会话的模型如下:
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')
});
很明显,session 和 speaker 共享多对多关系。所以我将会话添加到扬声器,然后保存它们。两条记录都已在服务器上成功创建,但未建立 link。我已经用邮递员测试了服务器端点,它工作正常。所以,我想我在这里遗漏了一些东西。
这是控制器代码:
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);
});
}
}
});
正如@Lux 在评论中提到的,ember-data 默认情况下不会在所有情况下序列化 has-many 关系。对于扩展 DS.JSONSerializer
and not overriding shouldSerializeHasMany()
method. This is the case for DS.JSONAPIAdapter
and DS.RESTSerializer
.
用于确定是否应序列化多关系的逻辑非常复杂且没有详细记录。所以需要 looking in source code 了解详情。
一般来说,当且仅当:
如果由序列化程序配置强制执行。要配置 Serializer 以强制序列化特定关系,
attrs
option 必须包含一个键,该键的关系名称包含一个对象{ serialize: true }
.如果未被序列化程序的
attrs
配置禁止。它与 1 相反。attrs
选项包含一个具有关系名称的键,其中包含一个对象{ serialize: false }
.如果是多对none关系意味着没有反向关系。
如果是多对多关系。
根据您的问题,多对多 关系未序列化。可能有很多原因导致您的问题,但我敢打赌是这个:
如果我没看错,多对多关系的两条记录都还没有保存在服务器上。我假设您不在客户端生成 ID。所以这两个记录在开始时都没有 ID。因此,无法在首次创建请求 (this.get('model.session').save()
) 时序列化该关系。您 API 对此请求的响应很好。响应中包含此记录没有任何相关信息speaker
。 ember-数据使用您的 API 返回的信息更新它的存储。此更新包括删除之前创建的关系。第二个创建请求 (this.get('model.speaker').save()
) 序列化关系但不存在,因为这是存储中的当前值。
如果是这种情况,您可以在分配关系之前简单地创建一条记录,然后保存另一条记录,这将在服务器上保留关系。
按照@jelhan 的建议,我必须先保存模型,然后再添加关系。以下代码有效:
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'));
}
}
}
});