Ember Data 1.0.0: how to retrieve validation errors (422 response) - ember.js

I am converting from Ember data 0.13 to 1.0.0 Beta 1. In 0.13, I was using the becameError and becameInvalid states to know whether there was a problem when saving a record.
In 1.0.0 there is no longer a transaction and you need to use the save promise to handle errors. See below:
save: function() {
this.get('model').save().then(function () {
alert("Record saved");
}, function () {
alert("Problem");
});
},
In the above, I want to make a distinction between validation errors and all the rest (just as it was before in 0.13 with becameError and becameInvalid).
Is there a way to access the error object and how to read the validation errors included in the json response ? Before this was via this.get('content.errors') ...
Hoep somebody can help
Marc

Three steps:
Return errors in a proper format. If it Rails application, then:
\# Rails controller, update function
format.json { render json: {errors: #post.errors.messages}, status: :unprocessable_entity }
Set errors in promise
// app.js
save: function() {
self = this;
this.get('model').save().then(function () {
alert("Record saved");
}, function (response) {
self.set('errors', response.responseJSON.errors);
});
}
Display errors in a handlebar template
<\!-- index.html -->
{{input type="text" value=title}}<span class="alert-error">{{errors.title}}</span>

Not sure if this helps you substitute the bacameInvalid and becameError since states are now being removed, but you could try this as a catchall workaround:
Ember.RSVP.configure('onerror', function(error) {
console.log(error.message);
console.log(error.stack);
});
Hope it helps.

Related

Using Ember.set to change a value, getting error that I have to use Ember.set

In my application, I have to update a record (eventToUpdate) with the data from another object (updatedEvent). To do this, I use the following code:
editEvent (updatedEvent, eventToUpdate) {
eventToUpdate.set('name', updatedEvent.name);
eventToUpdate.set('matching', updatedEvent.matching);
eventToUpdate.set('dcfEvent', updatedEvent.dcfEvent);
eventToUpdate.save().then(() => {
toastr.success('Event updated');
}).catch((error) => {
toastr.error('There occured an error while trying to update the event');
console.log(error);
});
},
When I try to update the event, I get the following error:
Assertion Failed: You must use Ember.set() to set the `name` property (of [object Object]) to `DCF tests`."
I have also tried setting the values with Ember.set, like this:
Ember.set(eventToUpdate, 'name', updatedEvent.name);
But that gives the same result..
I use Ember.js 1.13
It seems that eventToUpdate is not an Ember Object and someone is watching this property. So use Ember.set to set values:
editEvent (updatedEvent, eventToUpdate) {
Ember.set(eventToUpdate, 'name', updatedEvent.name);
Ember.set(eventToUpdate, 'matching', updatedEvent.matching);
Ember.set(eventToUpdate, 'dcfEvent', updatedEvent.dcfEvent);
eventToUpdate.save().then(() => {
toastr.success('Event updated');
}).catch((error) => {
toastr.error('There occured an error while trying to update the event');
console.log(error);
});
},
The problem lied in the structure of my data. I was trying to edit sub events, which are part of a root event. The problem was solved by first loading the single subevent from the backend and then editing that single subevent. It looks somewhat like this:
var event = this.store.findRecord('event', subevent.id);
event.set('name', updatedEvent.name);
event.set('matching', updatedEvent.matching);
event.save().then(() => {
toastr.success('element successfully updated');
}).catch((error) => {
console.log(error);
});

How to display error message in ember js 2.0

I would like to display an error message when the server responses with record not found.
The model in the route handler:
model: function(userLoginToken) {
var userLoginToken= this.store.createRecord('userLoginToken');
return userLoginToken;
},
The action:
actions: {
sendOTP: function(userLoginToken) {
var thisObject = this;
var model=this.currentModel;
this.store.findRecord('user-login-token', userLoginToken.get('mobileNumber')).then(function(response) {
//thisObject.get('controller').set('model', response);
},
function(error) {
//thisObject.get('controller').set('model', error);
//alert("model======== "+model.get('errors'));
});
},
The template is not displaying any error message.
The template:
{{#each model.errors.messages as |message|}}
<div class="errors">
{{message}}
</div>
{{/each}}
Unfortunately, the error message doesn't appear.
Ember depends on an DS.error object, in order to get errors from your models the response has to fulfill the requirements. In order to get Ember to recognize an valid error, in Ember 2.x the error code MUST be 422 and has to follow jsonapi http://jsonapi.org/format/#errors-processing
If you want to catch the errors from the backend response you have to use the catch method:
this.store.findRecord('user-login-token', userLoginToken.get('mobileNumber'))
.then(success => {
// Do whatever you need when the response success
})
.catch(failure => {
// Do whatever you need when the response fails
})
},
For catching the errors automatically as you are doing in your template, your backend needs to response in the right way. I would suggest you to read the answer for this SO question.

Ember data dependent keys undefined

A lot of the other posts on this topic are 2+ years old, so here goes a potentially simple question.
I am using Ember data relationships to have a 'bizinfo' record belong to a 'user' record. Seems simple, but I am having the worst time of it.
In app/models/bizinfo.js I have the line:
'ownedBy': DS.belongsTo('user')
And in my route, where I validate and then save the model, I have the following code:
user_id: Ember.computed(function(){
return `${this.get('session.data.authenticated.user_id')}`;
}),
user: Ember.computed(function(){
return this.store.findRecord('user', this.get('user_id'));
}),
model(params){
return this.store.createRecord('bizinfo', {'ownedBy': this.get('user')});
},
at this point if I go into the Ember inspector to look at the 'bizinfo' data object, I see the following under the belongsTo tab:
ownedBy : <(subclass of Ember.ObjectProxy):ember1053>
Here is the code from my submit action:
submit() {
let model = this.currentModel;
console.log(model.ownedBy);
console.log(`what does the model look like?`);
console.log(model.toJSON());
model.validate().then(({model, validations}) => {
if (validations.get('isValid')) {
this.setProperties({
showAlert: false,
isRegistered: true,
showCode: false
});
let success = (response) => {
console.log(`Server responded with ${response.toJSON()}`);
};
let failure = (response) => {
console.log(`Server responded with ${response}`);
};
model.save().then(success, failure);
} else {
this.set('showAlert', true);
}
this.set('didValidate', true);
}, (errors) => {
console.log(`errors from failed validation: ${errors}`);
});
},
So here is the result of the first console.log statement:
ComputedProperty {isDescriptor: true, _dependentKeys: undefined, _suspended: undefined, _meta: Object, _volatile: falseā€¦}
And when I look at the model.toJSON() log, I see
ownedBy: null
Can anyone see what's going wrong here? Is it the create record statement? I have tried a lot of different permutations (such as submitting just the id as the 'user' parameter.
findRecord will return a promise. A simple way to get around the issue is
model(params){
return this.store.findRecord('user', this.get('user_id')) .
then(ownedBy => this.store.createRecord('bizinfo', {ownedBy});
}
This will wait for the findRecord to resolve, then return a new record with the resolved value as the ownedBy property.

Ember-data 1.0 Beta 2 Issue with transition after save for the first time

I am creating a sample application using ember 1.0 and ember-data 1.0 Beta 2.0. with RESTAdapter to connect to backend server.
When I try to save a record, it always invoke failure handler at the first submission. But the record actually gets saved at backend without fail. From the server the response for submission contains the created entity set with id.
When I try to debug the code in developer tools, it actually goes through the code for route transition, but then it returns back to Add view before completing the transition. It seems to be some callbacks from jQuery global event handlers are causing the problem.
Here is the code I am using
App.AddResourceRoute = App.ResourceManagerRoute.extend(
{
model: function () {
return this.store.createRecord('Resource');
},
actions: {
save: function () {
this.modelFor('AddResource').save().then(function (resource) {
App.Router.router.transitionToRoute('Resources');
}, function (reason) {
alert('Failure reason:' + reason);
});
}
}
});
Please help me to find out what is wrong with my code.
Thanks in advance
I think that you are receiving an error from App.Router.router.transitionToRoute('Resources'); invocation, try to update to the following:
App.AddResourceRoute = App.ResourceManagerRoute.extend(
{
model: function () {
return this.store.createRecord('Resource');
},
actions: {
save: function () {
var route = this;
this.modelFor('AddResource').save().then(function (resource) {
route.transitionTo('Resources');
}, function (reason) {
alert('Failure reason:' + reason);
});
}
}
});
You should use transitionTo(someRoute) inside of a route, or transitionToRoute(someRoute) when inside of a controller

How should errors be handled when using the Ember.js Data RESTAdapter?

ember-data.js: https://github.com/emberjs/data/tree/0396411e39df96c8506de3182c81414c1d0eb981
In short, when there is an error, I want to display error messages in the view, and then the user can 1) cancel, which will rollback the transaction 2) correct the input errors and successfully commit the transaction, passing the validations on the server.
Below is a code snippet from the source. It doesn't include an error callback.
updateRecord: function(store, type, record) {
var id = get(record, 'id');
var root = this.rootForType(type);
var data = {};
data[root] = this.toJSON(record);
this.ajax(this.buildURL(root, id), "PUT", {
data: data,
context: this,
success: function(json) {
this.didUpdateRecord(store, type, record, json);
}
});
},
Overall, what is the flow of receiving an error from the server and updating the view? It seems that an error callback should put the model in an isError state, and then the view can display the appropriate messages. Also, the transaction should stay dirty. That way, the transaction can use rollback.
It seems that using store.recordWasInvalid is going in the right direction, though.
This weekend I was trying to figure the same thing out. Going off what Luke said, I took a closer look at the ember-data source for the latest commit (Dec 11).
TLDR; to handle ember-data update/create errors, simply define becameError() and becameInvalid(errors) on your DS.Model instance. The cascade triggered by the RESTadapter's AJAX error callback will eventually call these functions you define.
Example:
App.Post = DS.Model.extend
title: DS.attr "string"
body: DS.attr "string"
becameError: ->
# handle error case here
alert 'there was an error!'
becameInvalid: (errors) ->
# record was invalid
alert "Record was invalid because: #{errors}"
Here's the full walk through the source:
In the REST adapter, the AJAX callback error function is given here:
this.ajax(this.buildURL(root, id), "PUT", {
data: data,
context: this,
success: function(json) {
Ember.run(this, function(){
this.didUpdateRecord(store, type, record, json);
});
},
error: function(xhr) {
this.didError(store, type, record, xhr);
}
});
didError is defined here and it in turn calls the store's recordWasInvalid or recordWasError depending on the response:
didError: function(store, type, record, xhr) {
if (xhr.status === 422) {
var data = JSON.parse(xhr.responseText);
store.recordWasInvalid(record, data['errors']);
} else {
store.recordWasError(record);
}
},
In turn, store.recordWasInvalid and store.recordWasError (defined here) call the record (a DS.Model)'s handlers. In the invalid case, it passes along error messages from the adapter as an argument.
recordWasInvalid: function(record, errors) {
record.adapterDidInvalidate(errors);
},
recordWasError: function(record) {
record.adapterDidError();
},
DS.Model.adapterDidInvalidate and adapterDidError (defined here) simply send('becameInvalid', errors) or send('becameError') which finally leads us to the handlers here:
didLoad: Ember.K,
didUpdate: Ember.K,
didCreate: Ember.K,
didDelete: Ember.K,
becameInvalid: Ember.K,
becameError: Ember.K,
(Ember.K is just a dummy function for returning this. See here)
So, the conclusion is, you simply need to define functions for becameInvalid and becameError on your model to handle these cases.
Hope this helps someone else; the docs certainly don't reflect this right now.
DS.RESTAdapter just got a bit more error handling in this commit but we are still not yet at a point where we have a great recommendation for error handling.
If you are ambitious/crazy enough to put apps in production today with ember-data (as I have been!), it is best to make sure that the likelihood of failures in your API is extremely low. i.e. validate your data client-side.
Hopefully, we can update this question with a much better answer in the coming months.
I just ran into such a situation, not sure if this is already explained anywhere.
I am using:
Em.VERSION : 1.0.0
DS.VERSION : "1.0.0-beta.6"
Ember Validations (dockyard) : Version: 1.0.0.beta.1
Ember I18n
The model was initially mixedin with Validation mixin.
App.Order = DS.Model.extend(Ember.Validations.Mixin, {
.....
someAttribute : DS.attr('string'),
/* Client side input validation with ember-validations */
validations : {
someAttribute : {
presence : {
message : Ember.I18n.t('translations.someAttributeInputError')
}
}
}
});
In the template, corresponding handlebars is added. (note that ember validations will automatically add errors to model.errors.<attribute> in case of input validations, I will be using same trade-off in server validations as well)
<p>{{t 'translations.myString'}}<br>
{{view Ember.TextField valueBinding="attributeName"}}
{{#if model.errors.attributeName.length}}<small class="error">{{model.errors.attributeName}}</small>{{/if}}
</p
Now, we will be saving the Order
App.get('order').save().then(function () {
//move to next state?
}, function(xhr){
var errors = xhr.responseJSON.errors;
for(var error in errors){ //this loop is for I18n
errors[error] = Ember.I18n.t(errors[error]);
}
controller.get('model').set('errors', errors); //this will overwrite current errors if any
});
Now if there is some validation error thrown from server, the returned packet being used is
{"errors":{"attributeName1":"translations.attributeNameEror",
"another":"translations.anotherError"}}
status : 422
It is important to use status 422
So this way, your attribute(s) can be validated client side and again on server side.
Disclaimer : I am not sure if this is the best way!
Since there's currently no good solution in stock Ember-Data, I made my own solution by adding an apiErrors property to DS.Model and then in my RestAdapter subclass (I already needed my own) I added error callbacks to the Ajax calls for createRecord and updateRecord that save the errors and put the model in the "invalid" state, which is supposed to mean client-side or server-side validations failed.
Here's the code snippets:
This can go in application.js or some other top-level file:
DS.Model.reopen({
// Added for better error handling on create/update
apiErrors: null
});
This goes in the error callbacks for createRecord and updateRecord in a RestAdapter subclass:
error: function(xhr, textStatus, err) {
console.log(xhr.responseText);
errors = null;
try {
errors = JSON.parse(xhr.responseText).errors;
} catch(e){} //ignore parse error
if(errors) {
record.set('apiErrors',errors);
}
record.send('becameInvalid');
}