ember.js - políglota - La propiedad de errores del modelo de datos de Ember(DS.Errors) no se completa
biblia políglota complutense polyglot (3)
Estoy usando Ember Data y parece que no puedo completar la propiedad de ''errores'' del modelo con los mensajes de error de mi API REST. Casi estoy siguiendo el ejemplo en esta guía:
http://emberjs.com/api/data/classes/DS.Errors.html
Mi aplicación se ve así:
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();
}
}
});
Y mi API devuelve esto:
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."]
}
}
Después de llamar a save () en el modelo, esto es lo que veo en el modelo de usuario:
user.get(''isError'') // true
user.get(''errors.messages'') // []
Aunque el modelo está registrando correctamente la propiedad isError, parece que no puedo completar los mensajes de error. ¿Cómo puedo hacer que esto funcione? Estoy trabajando en la última versión beta de Ember Data versión 1.0.0-beta.8.2a68c63a
Tuve una experiencia larga y muy frustrante con la propiedad errors.messages de Ember Data, así que pensé en resumir todos mis hallazgos aquí en caso de que alguien más intente usar esta característica.
1) La documentación está desactualizada
Como @ kingpin2k mencionó en su respuesta, la documentación en http://emberjs.com/api/data/classes/DS.Errors.html está desactualizada. El ejemplo que proporcionan en esa página solo funciona si está utilizando DS.ActiveModelAdapter. Si está utilizando el DS.RESTAdapter predeterminado, entonces debe hacer algo como esto. Tenga en cuenta que prefiero este enfoque más simple en lugar de solo copiar la implementación ajaxError de ActiveModelAdapter:
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) Debe proporcionar una devolución de llamada rechazada
Esto es muy extraño, pero cuando llama a save () en su modelo, debe proporcionar una devolución de llamada rechazada, de lo contrario, obtendrá una excepción no capturada ''back-end rechazó la confirmación'' y JavaScript dejará de ejecutarse. No tengo idea de por qué este es el caso.
Ejemplo sin rechazar devolución de llamada. Esto dará como resultado una excepción:
user.save().then(function (model) {
// do something
});
Ejemplo con rechazo de devolución de llamada. Todo va a funcionar bien:
user.save().then(function (model) {
// do something
}, function (error) {
// must supply reject callback, otherwise Ember will throw a ''backend rejected the commit'' error.
});
3) De forma predeterminada, solo las propiedades de error que forman parte del modelo se registrarán en errors.messages. Por ejemplo, si este es tu modelo:
App.User = DS.Model.extend({
firstName: DS.attr(''string''),
lastName: DS.attr(''string'')
});
... y si esta es tu carga de error:
{
"errors": {
"firstName":"is required",
"summary":"something went wrong"
}
}
Entonces, el resumen no aparecerá en user.get (''errors.messages''). El origen de este problema se puede encontrar en el método adapterDidInvalidate de Ember Data. Utiliza this.eachAttribute y this.eachRelationship para restringir el registro de mensajes de error solo a aquellos que son parte del modelo.
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);
}
Hay una discusión sobre este tema aquí: https://github.com/emberjs/data/issues/1877
Hasta que el equipo Ember solucione esto, puede solucionar este problema creando un modelo base personalizado que anule la implementación predeterminada de adapterDidInvalidate, y todos sus otros modelos heredarán de ella:
Modelo base:
App.Model = DS.Model.extend({
adapterDidInvalidate: function (errors) {
var recordErrors = this.get(''errors'');
Ember.keys(errors).forEach(function (key) {
recordErrors.add(key, errors[key]);
});
}
});
Modelo de usuario:
App.User = App.Model.extend({
firstName: DS.attr(''string''),
lastName: DS.attr(''string'')
});
4) Si devuelve DS.InvalidError del adaptador ajaxError (el que anulamos anteriormente), su modelo quedará bloqueado en el estado ''isSaving'' y no podrá salir de él.
Este problema también es el caso si está utilizando DS.ActiveModelAdapter.
Por ejemplo:
user.deleteRecord();
user.save().then(function (model) {
// do something
}, function (error) {
});
Cuando el servidor responde con un error, el estado de ahorro del modelo es verdadero y no puedo resolver reiniciarlo sin volver a cargar la página.
Actualización: 2014-10-30 Para cualquiera que esté luchando con DS.Errors, aquí hay una excelente publicación de blog que resume bien esto: http://alexspeller.com/server-side-validations-with-ember-data-and-ds- errores /
ACTUALIZACIÓN: Ember Data 2.x
La respuesta anterior sigue siendo algo relevante y, en general, bastante útil, pero ahora está desactualizada para Ember Data 2.x (v2.5.1 al momento de escribir esto). Aquí hay algunas cosas que debe tener en cuenta cuando trabaje con versiones más nuevas de Ember Data:
-
DS.RESTAdapter
ya no tiene una funciónajaxError
en 2.x. Esto ahora lo manejaRESTAdapter.handleResponse()
. Puede anular este método si se requiere un manejo especial o formateo de errores. Código fuente RESTAdapter.handleResponse - La documentación para
DS.Errors
yDS.Model.errors
(que es una instancia de DS.Errors) es actualmente un poco engañosa. SÓLO funciona cuando los errores en la respuesta se adhieren a la especificación del objeto de error de la API JSON . Esto significa que no será del todo útil o utilizable si sus objetos de error de API siguen cualquier otro formato. Desafortunadamente, este comportamiento actualmente no se puede anular como muchas otras cosas en Ember Data, ya que este comportamiento se maneja en API privadas dentro de la clase InternalModel de Ember dentro de DS.Model. -
DS.InvalidError
solo se usará si el código de estado de la respuesta es422
por defecto. Si su API utiliza un código de estado diferente para representar los errores de las solicitudes no válidas, puede anularRESTAdapter.isInvalid()
para personalizar qué códigos de estado (u otra parte de una respuesta de error) comprobar que representan unInvalidError
. - Como alternativa, puede anular
isInvalid()
para que siempre devuelva false, de modo que Ember Data siempre creará unDS.AdapterError
más genérico enDS.AdapterError
lugar . Este error se establece enDS.Model.adapterError
y se puede aprovechar según sea necesario desde allí. -
DS.AdapterError.errors
contiene lo que se devolvió en la clave deerrors
de la respuesta de la API.
Los documentos definitivamente no existen en esta área, los errores no se completan a menos que esté utilizando el adaptador de modelo activo.
Aquí hay un ejemplo de que funciona, también echa un vistazo a Ember: error.messages no muestra errores del servidor en guardar donde digo lo mismo
http://jsbin.com/motuvaye/24/edit
Puede implementarlo fácilmente en el adaptador RESTA redefiniendo ajaxError
y copiando cómo lo hace el adaptador de modelo activo.
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;
}
}
});