Marking a field invalid - forms

I use a custom AJAX vtype, which works and all.
However I notice the extjs docs note that
"Note: this method does not cause the Field's validate or isValid methods to return false if the value does pass validation. So simply marking a Field as invalid will not prevent submission of forms submitted with the Ext.form.action.Submit.clientValidation option set."
How does one mark a field as invalid so that form.isValid() returns false?
My vtype:
Ext.apply(Ext.form.VTypes, {
username: function(value, field){
var conn = new Ext.data.Connection();
conn.request({
url: 'test.php',
params:{email:value},
method: 'POST',
success: function(responseObject){
var resp = responseObject.responseText;
if (resp == 0) {
field.markInvalid('This email is invalid or already registered!');
} else if (resp == 1) {
field.clearInvalid();
}
},
failure: function(){
Ext.Msg.show({title: 'Error', msg: 'Connection error!',});
}
});
return true;
}
});

Well, if you look at the definition of clientValidation:
Determines whether a Form's fields are validated in a final call to
isValid prior to submission. Pass false in the Form's submit options
to prevent this. If not defined, pre-submission field validation is
performed.
This is basically saying, if its set to false then the form will be automatically validated prior to submission... at which point it will fail if you have marked any fields as invalid. If you set it to true then you need to manually check if the form is valid before submitting.
It's worth pointing out that your AJAX vtype is never going to work. When the form calls the vtype to check whether the field is valid, all it gets in response is true... so the form submits no matter what. Even if you tell it to return after the ajax request has completed, the form would submit before the vtype ajax request completes.
A better solution would be to include the call to the vtype manually in your submit button handler...then in your success of the ajax request just do
if(form.isValid()) {
form.getForm().submit();
}
This will force the submit button's click event to first validate the username field and then, and only then, will it submit the form.

Related

How to stop the user from entering the duplicate record on default save

I have a custom module where there is an email field. Now i want to stop the user if the email is already in the database.
I want to stop the user on save button and show the error. Like when a required field goes empty.
I tried to get some help but was not able to understand it.
Note: I realized after posting this that you are using suitecrm which this answer will not be applicable toward but I will leave it in case anyone using Sugar has this question.
There are a couple of ways to accomplish this so I'll do my best to walk through them in the order I would recommend. This would apply if you are using a version of Sugar post 7.0.0.
1) The first route is to manually create an email address relationship. This approach would use the out of box features which will ensure your system only keeps track of a single email address. If that would work for your needs, you can review this cookbook article and let me know if you have any questions:
https://support.sugarcrm.com/Documentation/Sugar_Developer/Sugar_Developer_Guide_9.2/Cookbook/Adding_the_Email_Field_to_a_Bean/
2) The second approach, where you are using a custom field, is to use field validation. Documentation on field validation can be found here:
https://support.sugarcrm.com/Documentation/Sugar_Developer/Sugar_Developer_Guide_9.2/Cookbook/Adding_Field_Validation_to_the_Record_View/index.html
The code example I would focus on is:
https://support.sugarcrm.com/Documentation/Sugar_Developer/Sugar_Developer_Guide_9.2/Cookbook/Adding_Field_Validation_to_the_Record_View/#Method_1_Extending_the_RecordView_and_CreateView_Controllers
For your example, I would imagine you would do something like this:
Create a language key for your error message:
./custom/Extension/application/Ext/Language/en_us.error_email_exists_message.php
<?php
$app_strings['ERROR_EMAIL_EXISTS_MESSAGE'] = 'This email already exists.';
Create a custom controller for the record creation (you may also want to do this in your record.js):
./custom/modules//clients/base/views/create/create.js
({
extendsFrom: 'RecordView',
initialize: function (options) {
this._super('initialize', [options]);
//reference your language key here
app.error.errorName2Keys['email_exists'] = 'ERROR_EMAIL_EXISTS_MESSAGE';
//add validation tasks
this.model.addValidationTask('check_email', _.bind(this._doValidateEmail, this));
},
_doValidateEmail: function(fields, errors, callback) {
var emailAddress = this.model.get('your_email_field');
//this may take some time so lets give the user an alert message
app.alert.show('email-check', {
level: 'process',
title: 'Checking for existing email address...'
});
//make an api call to a custom (or stock) endpoint of your choosing to see if the email exists
app.api.call('read', app.api.buildURL("your_custom_endpoint/"+emailAddress), {}, {
success: _.bind(function (response) {
//dismiss the alert
app.alert.dismiss('email-check');
//analyze your response here
if (response == '<email exists>') {
errors['your_email_field'] = errors['your_email_field'] || {};
errors['your_email_field'].email_exists = true;
}
callback(null, fields, errors);
}, this),
error: _.bind(function (response) {
//dismiss the alert
app.alert.dismiss('email-check');
//throw an error alert
app.alert.show('email-check-error', {
level: 'error',
messages: "There was an error!",
autoClose: false
});
callback(null, fields, errors);
})
});
},
})
Obviously, this isn't a fully working example but it should get you most of the way there. Hope this helps!

Angular2 return data from validation service after Http call

I have build a validation service for my registration form and one of the static methods is checking if the entered email is available by calling my API the following:
static emailAvailable(control){
let injector = ReflectiveInjector.resolveAndCreate([HTTP_PROVIDERS]);
let http = injector.get(Http);
let valid = "E-mail is available";
http.post('https://secretapi.com/email', JSON.stringify({ email: control.value }))
.map((res: Response) => res.json())
.subscribe(function(result){
if(result.success){
valid = result.success; //The console.log on the line below is correct, the one at the bottom of the script never changes.
console.log(valid);
return null; //Doesn't do anything?
}else{
valid = result.error; //The console.log on the line below is correct, the one at the bottom of the script never changes.
console.log(valid);
return { 'invalidEmailAddress': true }; //Doesn't do anything, just like the return above
}
});
console.log(valid); //Output always "E-mail is available"
}
It should return "null" to the form validator when the email is available. The last console.log at the bottom should output the message that it recieves in the subscribe call. This doesn't happen and I'm not sure why. For some reason everything that happens within the subscribe call is contained there and never reaches the validator. What should I change? I have no idea and been searching the web for hours now.
You have to return Observable or Promise from your validator:
return http.post('https://secretapi.com/email', ...
console.log(...) doesn't make any sense here, since it will be executed after the Observable has been created as an object, but not after the ajax call has bee made.
If you want to output something after a response has been received, you have to move it inside subscribe
So in the end this website had the right answer. Also important to notice with the Angular2 Form validator to put the Async validators in the third (3) parameter and not together in an array in the second (2) parameter. That took me about 3 hours to figure out.
function checkEmail(control: Control){
let injector = ReflectiveInjector.resolveAndCreate([HTTP_PROVIDERS]);
let http = injector.get(Http);
return new Observable((obs: any) => {
control
.valueChanges
.debounceTime(400)
.flatMap(value => http.post('https://secretapi.com/email', JSON.stringify({ email: control.value })))
.map((res: Response) => res.json())
.subscribe(
data => {
if(data.success){
obs.next(null);
obs.complete();
} else {
obs.next({ 'invalidEmailAddress': true });
obs.complete();
}
}
);
});
}
The validator should look something like this, with the first validators checking on required and if it's actually an email address and the last doing an async call to the server to see if it's not already in use:
this.registerForm = this.formBuilder.group({
'email': ['', [Validators.required, ValidationService.emailValidator], ValidationService.emailAvailable],
});

If form validation fails

I want to redirect people to a certain page if the form validations fails. However, I can’t quite figure out how.
If i redirect people at the REDIRECT HERE comment below, it also redirects when it loads up the form and causes a endless loop.
public function create() {
$this->form_validation->set_rules('email_adress', 'E-mail', 'required|valid_email|is_unique[users.email_adress]');
if ($this->form_validation->run() !== FALSE) {
// PASSED
}
else {
// REDIRECT HERE
}
$this->load->view('user_register_view');
}
How can I achieve this?
You would redirect like so:
redirect('insert_URI_here');
Or if you are passing it to another method in the same controller you could just do :
$this->method_name();
However, if you just drop that in your else statement, user_register_view will not load because $this->form_validation->run() will return false either on validation error or non submission of the form.
What you will have to do is add another check to look for validation errors. If validation has failed and there are no validation errors, then your form hasn't been submitted.
So you could do something like this:
public function create() {
$this->form_validation->set_rules('email_adress', 'E-mail', 'required|valid_email|is_unique[users.email_adress]');
if(($this->form_validation->run() == FALSE) && ($this->form_validation->error_string() == ''));
//form not submitted yet
$this->load->view('user_register_view');
else if ($this->form_validation->run()) {
// PASSED
} else {
//Validation errors
redirect('insert_URI_here');
}
}
You will have to play around with it. I think $this->form_validation->error_string() should return an empty string if not submitted, but it might be a null value or a false (sorry can't remember off the top of my head).

What's the best way to handle a REST API's 'create' response in Backbone.js

I'm using backbone.js to interact with a REST API that, when posting to it to create a new resource, responds with a status of 201, a 'Location' header pointing to the resource's URI, but an empty body.
When I create a new model at the moment, its successful, but the local representation of the model only contains the properties I explicitly set, not any of the properties that would be set on the server (created_date, etc.)
From what I understand, Backbone would update its representation of the model with data in the body, if there were any. But, since there isn't, it doesn't.
So, clearly, I need to use the location in the Location header to update the model, but what's the best way to do this.
My current mindset is that I would have to parse the url from the header, split out the id, set the id for the model, then tell the model to fetch().
This seems really messy. Is there a cleaner way to do it?
I have some influence over the API. Is the best solution to try to get the API author to return the new model as the body of the response (keeping the 201 and the location header as well)?
Thanks!
Sounds like you will have to do a little customization.
Perhaps override the parse method and url method of your model class inherited from
Backbone.Model.
The inherited functions are:
url : function() {
var base = getUrl(this.collection);
if (this.isNew()) return base;
return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + this.id;
},
parse : function(resp) {
return resp;
},
and you could try something like:
parse: function(resp, xhr) {
this._url = xhr.getResponseHeader('location')
return resp
}
url: function() {
return this._url
}
Yes, backbone.js really wants the result of a save (be it PUT or POST) to be a parseable body which can be used to update the model. If, as you say, you have influence over the API, you should see if you can arrange for the content body to contain the resource attributes.
As you point out, its makes little sense to make a second over-the-wire call to fully materialize the model.
It may be that a status code of 200 is more appropriate. Purists may believe that a 201 status code implies only a location is returned and not the entity. Clearly, that doesn't make sense in this case.
With Backbone 0.9.9, I couldn't get the accepted answer to work. The signature of the parse function seems to have changed in an older version, and the xhr object is no longer available in the function signature.
This is an example of what I did, to make it work with Backbone v0.9.9 and jQuery 1.8.3 (using a Deferred Object/Promise), relying on the jqXHR object returned by Backbone.Model.save() :
window.CompanyView = Backbone.View.extend({
// ... omitted other functions...
// Invoked on a form submit
createCompany: function(event) {
event.preventDefault();
// Store a reference to the model for use in the promise
var model = this.model;
// Backbone.Model.save returns a jqXHR object
var xhr = model.save();
xhr.done(function(resp, status, xhr) {
if (!model.get("id") && status == "success" && xhr.status == 201) {
var location = xhr.getResponseHeader("location");
if (location) {
// The REST API sends back a Location header of format http://foo/rest/companys/id
// Split and obtain the last fragment
var fragments = location.split("/");
var id = fragments[fragments.length - 1];
// Set the id attribute of the Backbone model. This also updates the id property
model.set("id", id);
app.navigate('companys/' + model.id, {trigger: true});
}
}
});
}
});
I did not use the success callback that could be specified in the options hash provided to the Backbone.Model.save function, since that callback is invoked before the XHR response is received. That is, it is pointless to store a reference to the jqXHR object and use it in the success callback, since the jqXHR would not contain any response headers (yet) when the callback is invoked.
Another other to solve this would be to write a custom Backbone.sync implementation, but I didn't prefer this approach.

How do I clear one set of errors on a two form page in zend

I have a one page website that has two seperate forms that need to be posted back to the same page, the problem being, that if I submit one form, the error checking is done on both, so displays error messages for both. What I need is that if form one is submit, only form ones' error messages appear, and not form twos. Is this possible in zend?
It isn't a problem for zend to do - but it is a problem for you to solve!
If you give your form a hidden field, or if you have a field ID unique to one form, you should be able to check which form has been submitted in your controller, then you tell zend which form you want it to check. Something like the following should do the job, it will check for a field with the ID unique_form_one_field which obviously should only be on form one, this could be a hidden field for example:
// Get the forms:
$form1 = $this->_helper->formLoader('form_one');
$form2 = $this->_helper->formLoader('form_two');
// Check if there is a POST:
if (!$request->isPost())
{
// It isn't show the forms:
$this->view->form_one = $form1;
$this->view->form_two = $form2;
}
else
{
// It is, get the POST data:
$post_data = $request->getPost();
// Check if form one has been submitted:
if (isset($post_data['unique_form_one_field']))
{
// Check if form one is valid:
if (!$form1->isValid($post_data))
{
// Its got an error, display the forms again, form one will now be populated with the data and also the error messages:
$this->view->form_one = $form1;
$this->view->form_two = $form2;
}
else
{
// Form one was valid - do what you want to process it:
}
}
else
{
// Check if form two is valid:
if (!$form2->isValid($post_data))
{
// Its got an error, display the forms again, form two will now be populated with the data and also the error messages:
$this->view->form_one = $form1;
$this->view->form_two = $form2;
}
else
{
// Form two was valid - do what you want to process it:
}
}
}