How to prevent validate function call while calling model.save in backbone JS - validation

I have a backbone view where I call model.save to create/updated date submitted in the form. Before calling the save I explicitly call model.isValid(true) to validate the form fields then I process the form data to make it ready for API expected format (by adding or modifying additional fields) and then make call to mode.save function which is again triggering validate function where the validations are getting failed due to the modified data. As I have already called the isValid function explicitly, I want to prevent the call again during save. How can I do it in backbone. Here is sample code.
var data = Backbone.Syphon.serialize($(e.currentTarget).closest('form.my_form')[0]));
this.model.set(data);
if(this.model.isValid(true)) {
data['metas'] = this.context.metaData;
data['metas'][0]['locale'] = this.parentObj.model.get('locale');
data['metas'][0]['name'] = data['name'];
delete data['name'];
}
var tempDynAttrs = [];
if(data['dynamicAttributes']){
$.each(data['dynamicAttributes'], function(index,obj) {
if(obj['attributeValue'] !== null && obj['attributeValue'] !== undefined ) {
tempDynAttrs.push({
attributeName: obj['attributeName'],
attributeValue: [obj['attributeValue']],
locale: data['defaultLocale'],
status: 'active'
});
}
});
}
data['dynamicAttributes'] = tempDynAttrs;
this.model.save(data, {
url: this.model.url(),
patch: true,
success : function(model, response) {
$('#headerMessage').html('Data is updated successfully');
},
error : function(model, response) {
$('#headerMessage').html('Error updating data');
}
});
} else {
$('#formPanel').animate({
scrollTop: $('.has-error').first().offset().top-50
}, 100);
return false;
}

Try passing {validate:false} in the save options, like
book.save({author: "Teddy"}, {validate:false});
According to change log of version 0.9.10:
Model validation is now only enforced by default in Model#save and no longer enforced by default upon construction or in Model#set, unless the {validate:true} option is passed.
So passing {validate:false} should do the trick.

Related

ember.js `afterModel` event handler changes not reflected in Template

I have a route in which I use Ajax (not Ember Data) to pull a record, a costcentre, off the server. The record is intended to populate a template for subsequent edits.
Before the fetch, in beforeModel, an empty costcentre is created using createRecord. After the model processing is complete, in afterModel, the returned data is used to populate the costcentre object in the Data Store.
The fetch of the data is successful and in the debugger the update of the locally stored DS object can be seen to have worked but the changes are not seen in the template.
How can I get the template to populate with the data returned from the server ?
In the route I have this :
beforeModel: function(transition) {
this.set('ccToEdit', this.store.createRecord('costcentre'));
},
model(params) {
return getCCByCCIdent( this.urlbase,
this.currentMOP.currentMOP,
ENV.APP.MATClientCode,
params.cceIdentifier_to_edit);
},
afterModel(ccs, transition) {
//I'm testing this with an API end point that returns a
//list but there will only ever be one item in the list
this.ccToEdit.setProperties(ccs[0]);
},
The getCCByCCIdent looks like this :
export const getCCByCCIdent = function(urlbase, currentMOP, clientCode, targetCostCentreIdent) {
return new Promise(function (resolve, reject) {
if (targetCostCentreIdent.length == 0)
{
resolve([])
}
else
{
var theUrl = `${urlbase}/costcentres/${currentMOP}/${clientCode}/${targetCostCentreIdent}`;
$.ajax({
type: 'GET',
url: theUrl,
success: function (response) {
resolve(response);
},
error: function (request, textStatus, error) {
reject(error);
}
});
}
})
}
The simplest way to do this would be to do a then() on the promise being returned from your Ajax call, set appropriate values after that and then return your model:
model(params) {
return getCCByCCIdent(
this.urlbase,
this.currentMOP.currentMOP,
ENV.
params.cceIdentifier_to_edit
).then(ccs => {
let costCentre = this.store.createRecord('costcentre');
costCentre.setProperties(ccs[0]);
return costCentre;
});
},

Sails.js and Waterline: dynamic validation by DB request

I use Sails 11.1 and Waterline 2.11.2 with a MongoDB database.
I would like to validate data inserted in my "Article" model using a in validator for 1 attribute.
Before, I was doing the job with lifecycle callbacks (beforeCreate and beforeUpdate especially), but it makes double code.
Here you have the model, truncated with just the attribute in question :
module.exports =
{
schema: true,
autoCreatedAt: false,
autoUpdatedAt: false,
attributes:
{
theme:
{
model: 'Theme',
required: true
}
}
}
I know how to define it statically:
in: ['something', 'something other']
I know how to call constants I defined in my constants.js file :
defaultsTo: function ()
{
return String(sails.config.constants.articleDefaultTheme);
}
But I would like to get all themes in my DB, to have a dynamic in validation. So, I wrote this :
theme:
{
model: 'Theme',
required: true,
in: function ()
{
Theme.find()
.exec(function (err, themes)
{
if (err)
{
return next({ error: 'DB error' });
}
else if (themes.length === 0)
{
return next({ error: 'themes not found' });
}
else
{
var theme_ids = [];
themes.forEach(function (theme, i)
{
theme_ids[i] = theme.theme_id;
});
return theme_ids;
}
});
}
}
But it's not working, I have always the "1 attribute is invalid" error. If I write them statically, or if I check in the beforeCreate method with another DB request, it works normally.
If I sails.log() the returned variable, all the themes ids are here.
I tried to JSON.stringify() the returned variable, and also to JSON.parse(JSON.stringify()) it. I also tried to convert the theme.theme_id as a string with the String() function, but nothing else...
What am I doing wrong? Or is it a bug?
You can also check my question here : Waterline GitHub issues
Models's configuration at your attributes scope at in field of course will throw an error, because it should not use a function, especially your function is not return anything, also if you force it to return something, it will return Promise that Theme.find()... did.
Try use different approach. There are exist Model Lifecycle Callbacks. You can use something like beforeCreate, or beforeValidate to manually checking your dynamic Theme, if it's not valid, return an error.
Or if it's achievable using standard DB relation, just use simple DB relation instead.

Knockout computed observable boolean that uses an ajax call not returning boolean on sucess

I have a knockout pureComputed observable that is supposed to return if a account number is valid.
First it checks if the field is empty. (returns false)
Then it checks to see if the account number has changed from what was loaded. (returns true)
Finally it will run the ajax call to get the account info.
If successful it will set the ErrorMessage from the object and the Customer info. Then it is supposed to return true or false based on the ErrorMessage.
If unsuccessful it will set the ErrorMessage and return false.
self.isAccountValid = ko.computed(function () {
if (!self.account()) {//If not complete mark as InValid
self.accountError("Account Number Incomplete.")
return false;
} else if (vm.account == self.account()) {
self.accountError("");
return true;
} else {
var deferred = $.Deferred();
return $.ajax({
url: '/Order/NumberValidation',
data: { account: self.account() }
}).success(function (data) {
self.accountError(data.ErrorMessage);
//Set customer info properties
self.setCustomerInfo(data);
//Set success or warn icon based on error message field
var isValid = !data.ErrorMessage;
deferred.resolve(isValid);
return deferred;
}).error(function (data) {
//Set error message for invalid account
self.accountError("Server error. Please try again in a few minutes.");
//Set warning icon
return false;
})
}
}).extend({ async: ko.observable() });
They extend is a method I found to allow the computed to wait on the ajax call:
ko.extenders.async = function (nonObservableComputed, observableResult) {
nonObservableComputed.subscribe(function (result) {
jQuery.when(result).then(observableResult);
});
return observableResult;
}
This is setting the other data correctly on success, but the return of !data.ErrorMessage which evaluates to false if looking at it through debug is not what is set for the value of isAccountValid. Instead it is being set to the whole data object even though I am trying to return just the boolean.
myViewModel.isAccountValid()
Object {FirstName: null, LastName: null, StreetAddress: null, City: null, State: null…}
City: null
ErrorMessage: "Sequence contains no matching element"
FirstName: null
LastName: null
PhoneNumber: 0
State: null
StreetAddress: null
ZipCode: 0
One problem is that you're using a pureComputed for a function with side effects.
You also have an extra c in acccountError in your success section.
The extender is pretty horrible. It becomes trivial if you pass it the computed you want to use instead of its type, and don't need the added inProgress member.
ko.extenders.async = function (nonObservableComputed, observableResult) {
nonObservableComputed.subscribe(function (result) {
jQuery.when(result).then(observableResult);
});
return observableResult;
}
Demo: http://jsfiddle.net/sp160tan/1/
Update
Pretty sure your problem is that you're returning the ajax result rather than the deferred. Also, the success and error callbacks don't need to return anything; their return values are not used. This could be a source of confusion.
} else {
var deferred = $.Deferred();
$.ajax({
url: '/Order/NumberValidation',
data: { account: self.account() }
}).success(function (data) {
self.accountError(data.ErrorMessage);
//Set customer info properties
self.setCustomerInfo(data);
//Set success or warn icon based on error message field
var isValid = !data.ErrorMessage;
deferred.resolve(isValid);
}).error(function (data) {
//Set error message for invalid account
self.accountError("Server error. Please try again in a few minutes.");
//Set warning icon
deferred.resolve(false);
})
return deferred;
}

How to handle server side validation in Ember-Data 1.0.0

I'm using Ember 1.2.0 and the latest Ember Data Beta and wonder, how to handle server side errors (from API calls).
This question is quite similar, but it doesn't work.
At first, the becameInvalid method doesn't triggered. I'm using ember-validations (do I have to?)
My API sends an 422 status code and responses like that:
{"errors":{"name":["has already been taken"],"initial":["has already been taken"]}}
model.js
Docket.Customer = DS.Model.extend( Ember.Validations, {
name: DS.attr('string'),
initial: DS.attr('string'),
description: DS.attr('string'),
validations: {
name: {
presence: true
}
},
becameError: function() {
alert('there was an error!');
},
becameInvalid: function(errors) {
alert("Record was invalid because: " + errors);
}
});
controller.js
Docket.OrganizationCustomersController = Ember.ArrayController.extend({
actions: {
save: function () {
var customer = this.store.createRecord('customer');
customer.set('name', this.get('name'));
customer.set('initial', this.get('initial'));
customer.set('description', this.get('description'));
customer.save().then(function() {
console.log('jeah')
}, function() {
console.log('nooo')
});
}
}
});
The becameError method gets fired, but the becameInvalid method doesn't.
The second problem: even if the error is triggered, Ember.js adds the new record to the DOM. How can I prevent this behaviour?
Your errors json is ok, I think you are using the DS.RESTAdapter, and it doesn't implement the becameInvalid based in json with errors.
Just DS.ActiveModelAdapter have implemented in the moment, so I recommend you to change your adapter configuration to:
Docket.ApplicationAdapter = DS.ActiveModelAdapter;
In order to keep DS.RestAdapter, you can override its ajaxError method with the one from ActiveModelAdapter.
As for today the code, slightly adapted because some dependencies are needed, would be :
App.ApplicationAdapter = DS.RESTAdapter.extend({
// ... your own customizations,
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;
}
}
});
Obviously you have a chance here to adapt to your backend specifics: HTTP code (422 is not a standard one) and format.
Source :
http://discuss.emberjs.com/t/how-to-handle-failure-to-save-on-server/3789

Breeze client-side custom validation with server-side data

I created a custom validator that check if a username is used on a DB.
The whole process of validation works. What is not working is result.
function createExistingUsernameValidator() {
var name = 'existingUsernameValidator';
var ctx = { messageTemplate: 'Questa partita I.V.A. o codice fiscale sono già stati inseriti.', displayName: "Partita IVA o Codice Fiscale" };
var val = new Validator(name, valFunction, ctx);
return val;
function valFunction(value, context) {
var result = ko.observable(true);
require('services/datacontext').getIsUserByUsername(value, result)
.then(function () {
debugger;
return !result();
});
}
}
The promise works: I know because it hits the debbugger line and the retunrnig value is correct.
But the validator always evaluate as false because I'm not returning anything when the validator is called. In other words: it won't wait for the promise.
Is it my bad javascript or something else?
Any help is welcome.
Thank you!
Edited after answer
I've come to a solution that involves Knockout Validation (very useful script).
function createIsExistingUserKoValidation() {
ko.validation.rules['existingUsername'] = {
async: true,
validator: function (val, params, callback) {
if (val) {
var result = ko.observable();
require('services/datacontext').getIsUserByUsername(val, result)
.then(function () {
callback(!result());
});
}
},
message: ' Existing username.'
};
ko.validation.registerExtenders();
}
In the entity creation:
var createDitta = function () {
var ditta = manager.createEntity(entityNames.ditta,
{
id: newGuid(),
legaleRappresentante: createPersona(),
isAttiva: true
});
ditta.pivaCodFiscale.extend({ existingUsername: { message: ' Existing username.', params: true } });
ditta.pivaCodFiscale.isValidating(false);
return ditta;
};
ditta.pivaCodFiscale.isValidating(false); this is needed because isValidating is initialized with true.
The problem is that your valFunction as written will ALWAYS return 'undefined'. ( which is 'falsy'.
The 'return !result()' expression is NOT the return value of 'valFunction', it is simply the result of an anonymous function that executes AFTER valFunction has already returned. This is the async nature of promises.
What you are trying is to write an 'asynchronous' validation which is NOT supported out of the box with Breeze, but the idea IS a good one.
I think that you might be able to accomplish what you want by having your async callback actually 'set' a value on the entity and have that set operation itself trigger a seperate 'synchronous' validation.
This IS a good idea for Breeze to support more naturally so please feel free to add a feature request to the Breeze User Voice for something like "asynchonous validation". We use this to gauge the communities interest in the various proposed features/extensions to Breeze.

Resources