Свойство Ember Data модели ошибок (DS.Errors) не заполняется
Я использую Ember Data и, похоже, не могу получить свойство модели "ошибки" для заполнения сообщениями об ошибках из моего REST API. Я в значительной степени следую примеру в этом руководстве:
http://emberjs.com/api/data/classes/DS.Errors.html
Мое приложение выглядит так:
window.App = Ember.Application.create();
App.User = DS.Model.extend({
username: DS.attr('string'),
email: DS.attr('string')
});
App.ApplicationRoute = Ember.Route.extend({
model: function () {
return this.store.createRecord('user', {
username: 'mike',
email: 'invalidEmail'
});
},
actions: {
save: function () {
this.modelFor(this.routeName).save();
}
}
});
И мой API возвращает это:
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Content-Length: 125
{
"errors": {
"username": ["Username is taken!"],
"email": ["Email is invalid."]
}
}
После того, как я вызываю save() для модели, вот что я вижу в пользовательской модели:
user.get('isError') // true
user.get('errors.messages') // []
Несмотря на то, что модель правильно регистрирует свойство isError, я не могу получить сообщения об ошибках для заполнения. Как я могу заставить это работать? Я работаю над последней бета-версией Ember Data версии 1.0.0-beta.8.2a68c63a
3 ответа
В этой области определенно отсутствуют документы, ошибки не заполняются, если вы не используете адаптер активной модели.
Вот пример того, как это работает, также проверьте Ember: error.messages не показывает ошибки сервера при сохранении, где я говорю то же самое
http://jsbin.com/motuvaye/24/edit
Вы можете довольно легко реализовать его в RESTAdapter, переопределив ajaxError
и копирование того, как адаптер активной модели делает это.
App.ApplicationAdapter = DS.RESTAdapter.extend({
ajaxError: function(jqXHR) {
var error = this._super(jqXHR);
if (jqXHR && jqXHR.status === 422) {
var response = Ember.$.parseJSON(jqXHR.responseText),
errors = {};
if (response.errors !== undefined) {
var jsonErrors = response.errors;
Ember.EnumerableUtils.forEach(Ember.keys(jsonErrors), function(key) {
errors[Ember.String.camelize(key)] = jsonErrors[key];
});
}
return new DS.InvalidError(errors);
} else {
return error;
}
}
});
У меня был долгий и очень разочаровывающий опыт работы со свойством error.messages в Ember Data, поэтому я решил обобщить все свои выводы здесь на случай, если кто-нибудь еще попытается использовать эту функцию.
1) Документация устарела
Как упомянул @kingpin2k в своем ответе, документация по адресу http://emberjs.com/api/data/classes/DS.Errors.html устарела. Пример, который они предоставляют на этой странице, работает, только если вы используете DS.ActiveModelAdapter. Если вы используете DS.RESTAdapter по умолчанию, вам нужно сделать что-то вроде этого. Обратите внимание, что я предпочитаю этот более простой подход вместо простого копирования реализации ActiveModelAdapter ajaxError:
App.ApplicationAdapter = DS.RESTAdapter.extend({
ajaxError: function (jqXHR) {
this._super(jqXHR);
var response = Ember.$.parseJSON(jqXHR.responseText);
if (response.errors)
return new DS.InvalidError(response.errors);
else
return new DS.InvalidError({ summary: 'Error connecting to the server.' });
}
});
2) Вы должны предоставить обратный звонок
Это очень странно, но когда вы вызываете save() для вашей модели, вам необходимо предоставить обратный обратный вызов, в противном случае вы получите необработанное исключение "backend отклонил коммит", и JavaScript прекратит выполнение. Я понятия не имею, почему это так.
Пример без отклонения обратного вызова. Это приведет к исключению:
user.save().then(function (model) {
// do something
});
Пример с отклоненным обратным вызовом. Все будет хорошо работать
user.save().then(function (model) {
// do something
}, function (error) {
// must supply reject callback, otherwise Ember will throw a 'backend rejected the commit' error.
});
3) По умолчанию в свойствах error.messages будут зарегистрированы только те свойства ошибок, которые являются частью модели. Например, если это ваша модель:
App.User = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string')
});
... и если это ваша ошибка полезной нагрузки:
{
"errors": {
"firstName":"is required",
"summary":"something went wrong"
}
}
Тогда сводка не появится в user.get("errors.messages"). Источник этой проблемы можно найти в методе adapterDidInvalidate Ember Data. Он использует this.eachAttribute и this.eachRelationship, чтобы ограничить регистрацию сообщений об ошибках только теми, которые являются частью модели.
adapterDidInvalidate: function(errors) {
var recordErrors = get(this, 'errors');
function addError(name) {
if (errors[name]) {
recordErrors.add(name, errors[name]);
}
}
this.eachAttribute(addError);
this.eachRelationship(addError);
}
Здесь обсуждается эта проблема: https://github.com/emberjs/data/issues/1877
Пока команда Ember не исправит это, вы можете обойти эту проблему, создав настраиваемую базовую модель, которая переопределяет реализацию adapterDidInvalidate по умолчанию, и все остальные ваши модели наследуются от нее:
Базовая модель:
App.Model = DS.Model.extend({
adapterDidInvalidate: function (errors) {
var recordErrors = this.get('errors');
Ember.keys(errors).forEach(function (key) {
recordErrors.add(key, errors[key]);
});
}
});
Модель пользователя:
App.User = App.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string')
});
4) Если вы вернете DS.InvalidError из ajaxError адаптера (тот, который мы переопределили выше), то ваша модель застрянет в состоянии "isSaving", и вы не сможете выйти из него.
Эта проблема также имеет место, если вы используете DS.ActiveModelAdapter.
Например:
user.deleteRecord();
user.save().then(function (model) {
// do something
}, function (error) {
});
Когда сервер отвечает с ошибкой, состояние isSaving модели имеет значение true, и я не могу понять, сбросить ли это без перезагрузки страницы.
Обновление: 2014-10-30 Для тех, кто борется с DS.Errors, вот отличная запись в блоге, которая суммирует это хорошо: http://alexspeller.com/server-side-validations-with-ember-data-and-ds-errors/
ОБНОВЛЕНИЕ: Ember Data 2.x
Приведенный выше ответ все еще в некоторой степени актуален и в целом довольно полезен, но в настоящее время устарел для Ember Data 2.x(v2.5.1 на момент написания этой статьи). Вот несколько вещей, на которые следует обратить внимание при работе с более новыми версиями Ember Data:
DS.RESTAdapter
больше не имеетajaxError
функция в 2.x. Теперь это обрабатываетсяRESTAdapter.handleResponse()
, Вы можете переопределить этот метод, если требуется какая-либо специальная обработка или форматирование ошибок. RESTAdapter.handleResponse исходный код- Документация для
DS.Errors
а такжеDS.Model.errors
(который является экземпляром DS.Errors) в настоящее время немного вводит в заблуждение. Он работает ТОЛЬКО тогда, когда ошибки в ответе соответствуют спецификации объекта ошибки JSON API. Это означает, что это не будет полезным или пригодным для использования, если ваши объекты ошибок API будут следовать любому другому формату. К сожалению, это поведение в настоящее время нельзя переопределить так же хорошо, как и многие другие в Ember Data, так как это поведение обрабатывается в частных API внутри класса Ember InternalModel в DS.Model. DS.InvalidError
будет использоваться только если код состояния ответа422
по умолчанию. Если ваш API использует другой код состояния для представления ошибок для недействительных запросов, вы можете переопределитьRESTAdapter.isInvalid()
настроить коды состояния (или другую часть ответа об ошибке), чтобы проверить, что они представляютInvalidError
,- В качестве альтернативы вы можете переопределить
isInvalid()
всегда возвращать false, чтобы Ember Data всегда создавал более общийDS.AdapterError
вместо. Эта ошибка затем устанавливаетсяDS.Model.adapterError
и может быть использован по мере необходимости оттуда. DS.AdapterError.errors
содержать все, что было возвращено наerrors
ключ ответа API.