How to trigger Form Validators in Angular2 - forms

In angular2 I want to trigger Validators for some controls when a another control is changed. Is there some way that I can just tell the form to re-validate? Better still, can I request validation of specific fields?
Example:
Given Checkbox X and input P.
Input P has a validator that behaves differently based on the model value of X.
When X is checked/unchecked I need to invoke the validator on P. The Validator on P will look at the model to determine the state of X and will validate P accordingly.
Here's some code:
constructor(builder: FormBuilder) {
this.formData = { num: '', checkbox: false };
this.formGp = builder.group({
numberFld: [this.formData.num, myValidators.numericRange],
checkboxFld: [this.formData.checkbox],
});
}
this.formGp.controls['checkboxFld'].valueChanges.observer({
next: (value) => {
// I want to be able to do something like the following line:
this.formGp.controls['numberFld'].validator(this.formGp.controls['numberFld']);
}
});
Anybody have a solution? Thanks!

I don't know if you are still looking for an answer, so here is my suggestions:
Have a look at this: Angular 2 - AbstractControl
I think what you could do is following:
this.formGp.controls['checkboxFld'].valueChanges.observer({
next: (value) => {
this.formGp.controls['numberFld'].updateValueAndValidity();
}
});
This should trigger and run the validators. Furthermore the state gets updated as well. Now you should be able to consult the checkbox value within your validator logic.
Validaton-Guide
FormControl Documentation

with my ControlGroup I do this because I have errors divs checking if touched
for (var i in this.form.controls) {
this.form.controls[i].markAsTouched();
}
(this.form is my ControlGroup)

With the help of this blog
blog link
I have came across a solution with the combine of Nightking answer
Object.keys(this.orderForm.controls).forEach(field => {
const control = this.orderForm.get(field);
control.updateValueAndValidity();
});
this.orderForm is the form group

This did the trick for me
this.myForm.markAllAsTouched();

There are more elegant ways of modeling this behavior - for example, putting your state into a ReplaySubject and observing that, and then using async validators observing the state - but the pseudo-coded approach below should work. You simply observe the value changes in the checkbox, update the model as appropriate, then force a re-validation of the numberFld with the updateValueAndValidity cal.
constructor(builder: FormBuilder) {
this.formData = { num: '', checkbox: false };
const numberFld = builder.control(this.formData.num, myValidators.numericRange);
const checkbox = builder.control(this.formData.checkbox);
checkbox.valueChanges.map(mapToBoolean).subscribe((bool) => {
this.formData.checked = bool;
numberFld.updateValueAndValidity(); //triggers numberFld validation
});
this.formGp = builder.group({
numberFld: numberFld,
checkboxFld: checkbox
});
}

You can trigger validation in this way:
this.myform.get('myfield').updateValueAndValidity();

static minMaxRange(min: number, max: number): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
if (Validators.min(min)(control)) { // if min not valid
return Validators.min(min)(control);
} else {
return Validators.max(max)(control);
}
};
}

Here is another similar way that also uses markAsDirty and updateValueAndValidity, particularly good if you use angular material where markAsTouched is not enough.
export function forceValidation(form: AbstractControl) {
if (form instanceof FormGroup || form instanceof FormArray) {
for (const inner in form.controls) {
const control = form.get(inner);
control && forceValidation(control);
}
} else {
form.markAsDirty();
form.markAsTouched();
form.updateValueAndValidity();
}
}

Related

Clear input upon onChange event

I'd like to clear the input field after a successful selection (in single selection mode) - I'm aware that this isn't a normal use-case so I'm not surprised I couldn't find a way to do it in the docs. Is there a workaround I could use?
The typeahead instance exposes a public clear method, which you can use in the onChange handler to reset the component after a successful selection:
const MyTypeahead = () => {
const typeaheadRef = useRef(null);
return (
<Typeahead
...
onChange={s => {
if (s.length) {
typeaheadRef.current.clear();
}
}}
options={[ ... ]}
ref={typeaheadRef}
/>
);
};
Working example: https://codesandbox.io/s/rbt-clear-on-change-59kgq

React Native - How to validate Textinput correclty?

How to validate Textinput correclty? I want to validate my form correctly with custom form validation and after validation display errors in Text component, but how? Please, guys show me example!
install react-native-snackbar to show error messages.
import React, { Component } from 'react';
import { View, Text, TextInput } from 'react-native';
import Snackbar from 'react-native-snackbar';
export default class LoginPasswordScreen extends Component {
constructor(props) {
super(props);
this.state = {
password: ''
}
}
validate = () => {
//include your validation inside if condition
if (this.state.password == "") {
() => {
setTimeout(() => {
Snackbar.show({
title: 'Invalid Credintials',
backgroundColor: red,
})
}, 1000);
}
}
else {
Keyboard.dismiss();
// navigate to next screen
}
}
render() {
return (
<View>
<TextInput
returnKeyType="go"
secureTextEntry
autoCapitalize="none"
autoCorrect={false}
autoFocus={true}
onChangeText={(password) => this.setState({ password })}
/>
<TouchableOpacity>
<Text onPress={this.validate}>Next</Text>
</TouchableOpacity>
</View>
);
}
}
Every field, you have to do a comparison and show the error message and as I see there is no direct form validation even though there is form component available in react native.
In One of my react native project, I added a form and later on click of Submit, I had written one validate function to check all my inputs.
For this, I used one nice javascript library-
npm library- validator
And for showing error message, you can use, Toast, ALert or Snackbar
Would be nice if you provide some thoughts or code on how you would think it can be approached. But the way i did it was pretty simple, on my component state i got the following object:
this.state = {
loading: false,
username: {
text: '',
valid: false
},
password: {
text: '',
valid: false
},
isLoginValid: false
};
Then on the TextInput for username, i would first, bind its value to this.state.username.text, also, during onChangeText I just do a simple validation of the field, if the form is quite big, you may have a switch(fieldtype) where you have for each field, what treatment you want to apply a.k.a validation.
onChangeText={ (text) => { this.validateInput(text, 'username')}} (username would be the form input on the state object)
For instance, for username you want only to be length != 0 and length <= 8 characters, for email you may run a RegExp() with the email validation and also its length, for password a different logic, etc... after that i just simply save the state for that field input and if it's valid or not. Like this:
validateInput(text, fieldname) {
let stateObject = {
text: text,
valid: text.length !== 0
}
this.setState({ [fieldname]: stateObject }, () => {
this.checkValidation();
});
}
In checkValidation I check for all the input fields and if every one is valid, i set formValid to true.
This formValid would for example, allow the user to tap on the "Login" button otherwise it applies an opacity of 0.5 to it and disables it.
The rest you may guess, is just playing around with the valid variables of each field to what you want to display and what not.
In a Register form I also added an X or a "Tick" icon if the input text field is ok or not. Let your imagination guide you.
Hope it helps.

Setting the validity of a Angular 2 control from within a custom component

I have a custom Ng2 component a I am using the Model-Driven approach.
<form [ngFormModel]="myForm" class="layout vertical relative">
<my-custom-comp ngControl="currentValue"></my-custom-comp>
</form>
So inside my custom component I have all the logic I need but I can't find a way to get a reference to the ngControl to set it to valid or invalid from inside my custom component.
You can check this link for a working example: https://github.com/byavv/angular2-playground/tree/master/client/app/modules/forms_explore
Some key aspects:
You need to implement ControValueAccessor.
export class Datepicker implements ControlValueAccessor {
Inject in your component the ngControl and register it:
constructor(private ngControl:NgControl)
ngControl.valueAccessor = this;
From within your component you should have a form that validates the field so you can subscribe to emit the correct value or set the error of the parent ngControl form.
this.dateForm = builder.group({
dateControl: ['', Validators.compose([Validators.required, CustomValidators.frenchDate])],
});
this.dateForm.valueChanges
.subscribe((val) => {
if (this.dateForm.valid) {
this.onChange.emit(this.dateToTimestamp(val.dateControl));
} else {
this.ngControl.control.setErrors({ "wrongDate": true });
}
});
this.myForm.controls['currentValue']....
but there is currently no way to explicitely set it to valid or invalid.
You can define a validator and change the criteria so it marks the control invalid.
See for example https://github.com/angular/angular/issues/4933
How to set VALID or INVALID on any formGroup
// Where this.form === FormGroup;
// FormGroup can be deeply nested, just call at the level you want to update.
// That level should have direct access to base FormControls
// Can be done in a validator function;
this.form.get('name').setErrors({required: true});
// => this.form.get('name').invalid === true;
// Perhaps on Submit, click, event NOT in validator function
Object.entries(this.form.controls).forEach(([key, ctrl]) => {
ctrl.updateValueAndValidity();
});
// => this.form.get('name').invalid === false;
// => this.form.get('name').valid === true;
// => this.form.get('name').errors === null;

Ignore placeholder values on ajax form submit

I'm using the jQuery ajax form plugin in my WordPress plugin's settings page. Before I started using ajax, I had this script that compared text input values to placeholder values, and if they matched, set the text input value to null. But it no longer works now that I'm using ajax. With the jQuery ajax form plugin, I can pass arguments in a beforeSerialize function, or in a beforeSubmit function. I think it would need to be done in the beforeSerialize. Anyway, I'm not sure how to make this work. Here is the script that was working before I switched to ajax:
$('[placeholder]').focus(function() {
var input = $(this);
if (input.val() == input.attr('placeholder')) {
input.val('');
input.removeClass('placeholder');
}
}).blur(function() {
var input = $(this);
if (input.val() == '' || input.val() == input.attr('placeholder')) {
input.addClass('ssfa-placeholder');
input.val(input.attr('placeholder'));
}
}).blur().parents('form').submit(function() {
$(this).find('[placeholder]').each(function() {
var input = $(this);
if (input.val() == input.attr('placeholder')) {
input.val('');
}
})
});
And here is my current script for the ajax form submit:
var svn = $("#ssfa-saving"),
bck = $("#ssfa-saving-backdrop"),
svd = $("#ssfa-settings-saved");
$("#ssfa-form").ajaxForm({
beforeSend: function() {
svn.fadeIn('slow');
bck.fadeIn('fast');
},
success: function(){
svn.fadeOut('slow');
svd.delay(1000).fadeIn('slow').delay( 2500 ).fadeOut('slow');
bck.delay( 4500 ).fadeOut('slow');
}
});
Any ideas on how I can get the ajax submit (either beforeSerialize or beforeSend ) to ignore placeholder values? This first script above was a really simple solution for regular post submit. I'm hoping I can find something just as simple for ajax.
UPDATE
I worked out a basic way of doing it but it involves calling each text field that has a placeholder, so it's not exactly elegant like the original script, but this is functional:
$("#ssfa-form").ajaxForm({
beforeSerialize: function() {
var permex = $('input#exclusions');
$('input[id^=bs]').each(function(){
var bs = $(this);
if (bs.val() === 'Display Name')
bs.removeAttr('value');
});
$('input[id^=custom_]').each(function(){
var cs = $(this);
if (cs.val() === 'classname1|Display Name 1, classname2|Display Name 2')
cs.removeAttr('value');
});
if (permex.val() === '.avi, My Embarrasing Photograph, .tif')
permex.removeAttr('value');
},
beforeSend: function() {
etc.
And since it's a placeholder text, the text doesn't actually disappear when the value attribute is removed, so no one is really the wiser. I'm not over the moon with this, but it works. If I had a much larger form, this wouldn't be workable.
Open to better ideas....
Well, I played around with it quite a bit more and found a way to get the original code to work with ajax submit. It's quite simple actually. I just had to specify the element within which to search for the placeholder attr. Here it is:
beforeSerialize: function() {
$("#ssfa-form").find('[placeholder]').each(function() {
var input = $(this);
if (input.val() == input.attr('placeholder')) {
input.val('');
}
})
},
etc.
To track the issue, see:
https://github.com/mathiasbynens/jquery-placeholder/issues/30
https://github.com/mathiasbynens/jquery-placeholder/issues/197

How do I submit a form using a store under ExtJs?

Is there a way to have a form submit create an object in a store under ExtJs 4?
It seems strange to me that the grid is built completely around the store mechanism and I see no obvious way to plug a form into a store. But I am most likely just missing something.
You can add a model instance to a store upon form submit using this code:
onSaveClick: function()
{
var iForm = this.getFormPanel().getForm(),
iValues = iForm.getValues(),
iStore = this.getTasksStore();
iStore.add( iValues );
},
This is within an MVC controller, so this is the controller.
For model editing, you can 'bind' a form to a model instance using loadRecord:
iFormPanel.loadRecord( this.selection );
You can then update the model instance using updateRecord():
iFormPanel.getForm().updateRecord();
Just for fun (and as it might help some), it is similar to the following code:
onSaveClick: function()
{
var iForm = this.getFormPanel().getForm(),
iRecord = iForm.getRecord(),
iValues = iForm.getValues();
iRecord.set ( iValues );
},
If your store is has autoSync: true. An Update (or Create) call will be made via the configured proxy. If there's no autoSync, you'll have to sync your store manually.
You can subclass Ext.form.action.Action to provide load/save actions for a Form to be performed on a Store. The only gotcha is that somehow there's no "official" way to select any non-standard Action in Ext.form.Basic, so I'd suggest an unofficial override:
Ext.define('Ext.form.Advanced', {
override: 'Ext.form.Basic',
submit: function(options) {
var me = this,
action;
options = options || {};
action = options.submitAction || me.submitAction;
if ( action ) {
return me.doAction(action, options);
}
else {
return me.callParent(arguments);
}
},
load: function(options) {
var me = this,
action;
options = options || {};
action = options.loadAction || me.loadAction;
if ( action ) {
return me.doAction(action, options);
}
else {
return me.callParent(arguments);
}
}
});
And, having created the Actions you need, you could then use them in a Form Panel:
Ext.define('My.form.Panel', {
extend: 'Ext.form.Panel',
requires: [ 'Ext.form.Advanced' ],
loadAction: 'My.load.Action',
submitAction: 'My.submit.Action',
...
});
There are other ways and shortcuts though.