AngularJS - validation of dynamic input fields - javascript

Let's say, we have an object like:
$scope.company = { name: { de: '', en: '' } };
and an input field saying:
<input type="text" ng-model="company.name[currentLanguage]" />
<button ng-click="currentLanguage='de'">Deutsch</button>
<button ng-click="currentLanguage='en'">English</button>
If the user fills in this field, the field receives the ng-valid class. If the user then changes the language ($scope.currentLanguage in fact), the input field is correctly updated (gets empty), but it has still the ng-valid class, which is wrong. The expected behavior would be rather ng-pristine. How to update this in real time?
Would be great to know that.
Cheers
PS. There isn't any more code. That's just it.
PS2. It is another Problem as you suggest in the duplicate thread. I do not use ng-repeat.

Once an input's value is changed in any way, it doesn't reset to ng-pristine unless you force it to.
You could manage the classes in your controller like so:
$scope.currentLanguage = 'de';
$scope.company = { name: { de: '', en: '' } };
$scope.setCurrentLanguage = function(str) {
$scope.currentLanguage = str;
var input = angular.element(document).find('input')[0];
if ($scope.company.name[str] == '') {
angular.element(input).removeClass('ng-dirty');
angular.element(input).removeClass('ng-invalid');
angular.element(input).addClass('ng-pristine');
} else {
angular.element(input).removeClass('ng-pristine');
angular.element(input).addClass('ng-dirty');
}
}
and in the html:
<input type="text" ng-model="company.name[currentLanguage]" />
<button ng-click="setCurrentLanguage('de')">Deutsch</button>
<button ng-click="setCurrentLanguage('en')">English</button>

Related

how can i to make #blur event work properly - Nuxtjs

I am trying to trigger a function that validates an input field when it loses focus using nuxtjs. When I focus out of the input field, the function isn't triggered but gets triggered when I focus in and start typing in another or the same input field.
<div class="control">
<input class="input" type="text" ref="email" #blur="validateMail()" v-model="email_p" name="email" />
</div>
this is the function call
methods: {
validateMail(){
let value = this.$refs.email.value
let mailformat = /^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
if (value.match(mailformat)) {
//validation passed
} else {
this.msg.email = 'Enter a valid email';
}
},
}
This may work fine
<template>
<div class="control">
<input
ref="email"
v-model="email_p"
class="input"
type="text"
name="email"
#blur="validateMail"
/>
message: {{ msg.email }}
</div>
</template>
<script>
export default {
data() {
return {
email_p: '',
msg: {
email: '',
},
}
},
methods: {
validateMail(value) {
const mailformat =
/^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
if (value.target.value.match(mailformat)) {
// validation passed
} else {
this.msg.email = 'Enter a valid email'
}
},
},
}
</script>
You don't need to use $refs to access the element, you can access the event directly.
If you want to get the value via $refs, you would need to wait for a full tick to trigger, to get the actual new value. Hence use the event passed by #blur, simpler, cleaner and less messy.
Also, value.target.value is important because it's receiving an event and not the HTML element itself.
PS: the event can also be written as #blur="validateMail($event)" if you want to be more explicit but it's not mandatory (it's passing it by itself already).

Reactive form validation for dynamic and hidden fields

First off, I have an Angular reactive form that has a button that can add another FormArray to the form. All validation good and working as expected. Things have gotten a little tricky when introducing another dynamic control to the already dynamic form group. This control is shown/hidden based on a selection made in another form control.
When the control is shown I introduce validation, when the control is hidden the validation is cleared. This ensures that my form remains valid/invalid correctly.
Its acting a little buggy e.g. when I complete a group of inputs and add another dynamic group of inputs, both triggering the hidden control... then to amend the previous hidden input - the form remains true. e.g.
Selecting 123 triggers the "Other Code" control, removing the value should make the form invalid, but it stays true at this point.
I have a change function assigned to the select to determine whats been selected.
selectClick(x) {
const f = this.form;
let items = this.form.get('justificationItems') as FormArray;
if (x === '123') {
items.controls.forEach(i => {
console.log(i)
i['controls'].other.setValidators([Validators.required]);
// i['controls'].other.updateValueAndValidity();
});
} else {
items.controls.forEach(i => {
i['controls'].other.clearValidators();
// i['controls'].other.updateValueAndValidity();
});
}
f.updateValueAndValidity();
}
I suspect when changing the select property to trigger the above it does not do it to the correct index item, and it does it for all?
StackBlitz - https://stackblitz.com/edit/angular-prepopulate-dynamic-reactive-form-array-ufxsf9?file=src/app/app.component.ts
the best way to "clear/add Validators" really is enabled or disabled the formControls. Remember a formControl has as status one of this:
type FormControlStatus = 'VALID' | 'INVALID' | 'PENDING' | 'DISABLED';
So we can simple enabled/disabled the FormControl. Futhermore, when we create the formGroup we can created disabled, so at first will be not INVALID
Well, the code is a bit confussed. first the use of i['controls'].other (really you can use i.controls.other an a estrange mix using FormBuilder and new Form.
As always we has a FormArray we create a getter
get justificationItems()
{
return this.form.get('justificationItems') as FormArray;
}
In stead use two differents functions to create the form, we can use and unique
createJustificationField(x: any=null): FormGroup {
x=x || {name:null,description:null,code:null,other:null}
return new FormGroup({
name: new FormControl(x.name, [Validators.required]),
description: new FormControl(x.description, [Validators.required]),
code: new FormControl(x.code, [Validators.required]),
other: new FormControl({value:x.other,
disabled:x.code!=='123'},[Validators.required]),
});
}
See that we can use as
this.createJustificationField(..an object..)
this.createJustificationField()
Our functions: createForm, addItem and selectClick (I like more another name like codeChange but is a minor change) becomes like
createForm() {
this.service.getmodel().subscribe((response:any) => {
this.form = new FormGroup({
justificationItems: new FormArray(
response.justificationItems.map(x=>
this.createJustificationField(x))
),
});
});
}
addItem(): void {
this.justificationItems.push(this.createJustificationField());
this.form.updateValueAndValidity();
}
selectClick(x,i) {
if (x === '123')
this.justificationItems.at(i).get('other').enable()
else
this.justificationItems.at(i).get('other').disable()
this.form.updateValueAndValidity();
}
And the .html becomes more clear in the way
<form *ngIf="form" [formGroup]="form">
<div formArrayName="justificationItems">
<div
*ngFor="
let orgs of justificationItems.controls;
let i = index;
let last = last
"
[formGroupName]="i"
>
<label>Name </label>
<input formControlName="name" placeholder="Item name" /><br />
<label>Description </label>
<input
formControlName="description"
placeholder="Item description"
/><br />
<label>Code </label>
<select
(change)="selectClick($event.target.value, i)"
formControlName="code"
>
<option value="123">123</option>
<option value="456">456</option></select
><br />
<ng-container *ngIf="justificationItems.at(i).value.code === '123'">
<label>Other Code </label>
<input formControlName="other" placeholder="other" /><br /><br />
</ng-container>
<button
*ngIf="last"
[disabled]="justificationItems.at(i).invalid"
type="button"
(click)="addItem()"
>
Add Item
</button>
</div>
</div>
<button [disabled]="!form.valid" type="button">Submit</button>
</form>
<p>Is form valid: {{ form?.valid | json }}</p>
see the stackblitz
Root cause: When selectClick trigger, you clear or set validation for all controls other in array form. You should set only for one form in formArray.
I rewrite your function:
selectClick(x, index) {
const f = this.form;
let items = this.form.get('justificationItems') as FormArray;
if (x === '123') {
items.controls[index]['controls'].other.setValidators([Validators.required]);
} else {
items.controls.forEach(i => {
items.controls[index]['controls'].other.clearValidators();
i['controls'].other.updateValueAndValidity();
});
}
items.controls[index]['controls'].other.updateValueAndValidity();
}
change code in template:
<select
(change)="selectClick($event.target.value, i)"
formControlName="code"
>

How to set class to input when data returned from api. Vue

I have inputs with labels that rise when input has data, like materialize ( https://materializecss.com/text-inputs.html ).
I use my checkInputData function to check if input has any value and set 'with-data' class. It works when user input some data to input but when i initialize the component and data returned from api it does`n works.
How can i set 'with-data' class to my input when data returned from api?
My HTML
<input type="text" :value="name" #input="setName($event.target.value); checkInputData($event);" />
<label class="input-label">name</label>
checkInputData function
checkInputData(event) {
let input = event.target;
let hasValueClass = 'with-data';
if(input.value != '') {
input.classList.add(hasValueClass);
} else {
input.classList.remove(hasValueClass);
}
},
Use v-model for 2 way bind and use it for altering class(it will ignore the :value property).
<input type="text" v-model="nameValue" :class="{'with-data' : nameValue !== ''}" #input="setName($event.target.value);" />
<label class="input-label">name</label>
within data add nameValueproperty as well.
data(){
return {
/*other values*/
nameValue:''
}
}

Validating React-Bootstrap-Typeahead input

I am using a validation function in my form, that checks whether a field is empty or not. This works for normal text input boxes, but not for the Typeahead box.
I want to check if the Typeahead box is empty, and if it is, display an error message i.e. this.state.industryError
This is my state:
state = {
name: "",
industry: [],
nameError: "",
industryError: "",
}
And this is my Typeahead form element:
<Form>
<Label>Industry</Label>
<Typeahead
id="industryMultiSelect"
multiple
options={industries}
placeholder="Select industry..."
onChange={this.handleTypeaheadChangeIndustry} />
<div>
{this.state.industryError} // Where the error message appears
</div>
</Form>
My validate function is as follows:
validate = () => {
let nameError = "";
let industryError = "";
if(!this.state.name) { // Checks if field is empty
nameError = "Please enter a name";
}
if(this.state.industry === []) { // I think this is the problem
industryError = "Please enter an industry";
}
if (nameError || industryError) {
this.setState({nameError, industryError});
return false;
}
return true;
};
This is handleChange function I call for the typeahead:
handleTypeaheadChangeIndustry = selected => {
const industry = selected.map(option => option.value);
this.setState({industry})
};
It works fine with the name field because it equals an empty string. But with the Typeahead, it's not a string, and so I'm unsure what to set this.state.industry to.
Any help or advice is much appreciated, and I can quickly update my answer if you need more detail.
It's a little hard to pinpoint why your code isn't working without seeing everything. That said, I created a sandbox which I believe does what you're after using the code you posted above:
https://codesandbox.io/s/react-bootstrap-typeahead-form-validation-686qe
As #ravibagul91 notes, you should be using one of
if (this.state.industry.length === 0) { ... }
if (!this.state.industry.length) { ... }
rather than
if (this.state.industry === []) { ... }
Using the latter in the sandbox I posted causes the code not to work, while things work fine when using either of the first two.

How to make ng-required working on an object value instead of the value in the input?

I made a directive to translate in different languages using an input text. if I'm on english state and the input is empty but my model have a valid value in french. The validation only works on the value in the input text. I would like to make the attribute "required" to works if there is valid value in the part of my model that is not shown in the input. (there is no button in template, only the input and a dropdown to switch language)
ex:
<input class="form-control" type="text" ng-model="inputValue" ng-required="true">
<button ng-click="inputValue = translation.fr">Switch</button>
<script>
var default = "en";
function ctrl($scope) {
var default = "en";
$scope.translation = { fr: "Francais", en: "" }
$scope.inputValue = $scope.translation[default];
}
</script>
Use the librairy lodash
Your template:
<input class="form-control" type="text" ng-model="inputValue" ng-required="isRequired">
Javascript:
scope.isRequired = function() {
return !_.some(scope.inputValue, function(a) { return a !== ""; });
};

Categories