Why is a Angular Form Validated even before i press the Submit button? - forms

I have a Reactive Angular Form. When the page Loads the form is already checked for the errors.
I am trying to use a scroll to error directive in order to scroll and focus on the error div but the form never goes there it is already validated.
import { ElementRef, HostBinding, Input } from '#angular/core';
import { Directive } from '#angular/core';
#Directive({
selector: '[scrollTo]'
})
export class ScrollDirective {
constructor(private elRef:ElementRef) {}
#HostBinding('hidden') isError:boolean = false;
#Input() set scrollTo(cond) {
console.log(cond);
this.isError = cond;
if(cond) {
this.elRef.nativeElement.scrollIntoView();
this.elRef.nativeElement.focus();
}
}
}
This is where i am checking the error but it is already checked and if i put the scrollTo outside it scrolls to at first .
<div class="row">
<div class="col-md-12">
<span *ngIf="user.get('email').touched && !user.get('email').valid && !user.get('email').pristine">
<small [scrollTo] = "user.get('email').valid">Invalid email</small>
</span>
</div>
</div>
Update
Now i am using it like this
<div class="col-md-4" [scrollTo] = "user.get('age').valid">
<md-input-container>
<input mdInput type="number" formControlName="age" placeholder="Age" validate-onblur>
</md-input-container>
</div>
The things is that on submit the form will show validation error and i want the focus and the scroll to move there ? please help ?

Move the directive implementation to the input so you can have the direct reference to it. To deactivate the firing at the start, set a property. Change that property directly in the template when form is submitted. Use DoCheck in the directive to track the input validity and fire the scrolling and focus:
HTML:
<form (submit)="start = user.get('email').valid; onSubmit(); " ....>
.....
<input [scrollTo] = "!user.get('email').valid && !start" ....>
<span [hidden] = "user.get('email').valid">
<small>Invalid email</small>
</span>
Component
start = true;
Directive:
export class ScrollDirective implements DoCheck {
constructor(private elRef:ElementRef) {}
#Input() scrollTo ;
ngDoCheck(){
if(this.scrollTo) {
this.elRef.nativeElement.scrollIntoView();
this.elRef.nativeElement.focus();
}
}
DEMO
Old answer:
You are checking user.get('email').touched in the wrapper which is false at the start, the whole span is removed and scrollTo is ignored. Move the directive to the actual input and make changes

Related

Angular validation message for maxlength attribute

I'm having some trouble with displaying error messages for the maxlength attribute in Angular.
Problem
Since the maxlength attribute don't allow more characters than the specified amount, I'm having trouble displaying my error message. Is there any way to turn of the default behavior (allow the user to type more characters), in order to display my error message.
Code for textarea
<textarea maxlength="10"
[(ngModel)]="title.value"
#title="ngModel"></textarea>
Code for Angular validation
<div *ngIf="title.errors && (title.dirty || title.touched)"
class="alert alert-danger">
<div [hidden]="!title.errors.maxlength">
Only 10 characters allowed.
</div>
</div>
If you want me to provide any additional information, please let me know.
you can work with Reactive forms to validate properly your form,
here is a simple example how to use reactive forms :
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
#Component({
selector: 'title-form',
template: `
<form novalidate [formGroup]="myForm">
<label>
<span>Full title</span>
<input type="text" placeholder="title.." formControlName="title">
</label>
<div*ngIf="myForm.controls['title'].touched && myForm.get('title').hasError('required')">
Name is required
</div>
<div *ngIf="myForm.controls['title'].touched && myForm.controls['title'].hasError('maxlength')">
Maximum of 10 characters
</div>
</form>
`
})
export class TitleFormComponent implements OnInit {
myForm: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.myForm = this.fb.group({
title: ['', [Validators.required, Validators.maxLength(10)]],
});
}
}
hope it helps u :)
You can achieve it by setting the condition directly on the length of the input. A span tag with *ngIf can show/hide the error message:
HTML
<textarea class="form-control" id="title"
type="number" name="title" [(ngModel)]="titleModel"></textarea>
<span style="color:red" *ngIf="titleModel?.length > 10">
10 max
</span>
Class:
...
titleModel = 'I have more than 10 characters'
...
DEMO

angular 2, validate form if at least one input is typed

I'm quite new to Angular, and I've already searched the web, without finding a correct solution for my situation.
I have a dynamic form created by a *ngFor. I need to disabled the submit button if the inputs are all empty and show the alert div; but I need to enable the submit if at least one of those forms contains something different from ''.
Here is my html code
<form class="form-inline" #form="ngForm">
<div class="form-group" *ngFor="let meta of state.metaById; let i = index" style="margin: 5px">
<label>{{meta.nome}}</label>
<input type="text" class="form-control" #nome (blur)="inputInArray(nome.value, i);">
</div>
<button type="button" class="btn btn-primary" (click)="getCustomUnitaDocumentaliRow(this.param)" [disabled]="fieldNotCompiled">invia</button>
</form>
<div class="alert-notification" [hidden]="!fieldNotCompiled">
<div class="alert alert-danger">
<strong>Va compilato almeno un campo.</strong>
</div>
</div>
and here is my Typescript code
inputInArray(nome: string, indice) {
if (this.state.controlloMetaId = true) {
this.state.metadatoForm[indice] = nome;
}
// this.fieldNotCompiled = false;
for (const i in this.state.metaById) {
console.log(this.state.metadatoForm);
if (isUndefined(this.state.metadatoForm[i]) || this.state.metadatoForm[i] === '') {
this.fieldNotCompiled = true && this.fieldNotCompiled;
} else {
this.fieldNotCompiled = false && this.fieldNotCompiled;
}
console.log(this.fieldNotCompiled);
}
With this code I can check the first time a user type something in one input, but it fails if it empty one of them (or all of them)
Thanks for your time
UPDATE
Check if any input got a change that is different from empty or space, just by doing:
<input ... #nome (input)="fieldNotCompiled = !nome.value.trim()" ....>
DEMO
You can set a listener to the form changes:
#ViewChild('form') myForm: NgForm;
....
ngOnInit() {
this.myForm.valueChanges.subscribe((value: any) => {
console.log("One of the inputs has changed");
});
}

Angular Form validation on child components

I've written a dynamic form in which there is a main part and sub parts based on a type that's selected in the main part (widget.type). Showing and hiding the sub parts is done with an ngSwitch.
HTML of the form looks like this:
<form class="widget-form cc-form" (ngSubmit)="saveChanges()" novalidate>
<div class="forms-group">
<label for="title" i18n="##title">Titel</label>
<input class="form-control" id="title" name="title" type="text" [(ngModel)]="widget.title" required />
</div>
<div class="forms-group">
<label class="checkbox-label" for="show" i18n>
<input id="show" name="show" type="checkbox" [(ngModel)]="widget.show" /> <span>Titel tonen in app</span>
</label>
</div>
<div class="forms-group">
<label for="type" i18n="##type">Type</label>
<select class="form-control" id="type" name="type" [(ngModel)]="widget.type" required>
<option value="text-widget" i18n="##Text">Tekst</option>
<option value="tasklist-widget" i18n="##Tasklists">Takenlijst</option>
<option value="image-widget" i18n="##Text">Afbeelding(en)</option>
<option value="video-widget" i18n="##Video">Youtube</option>
<option value="link-widget" i18n="##Link">Link</option>
<option value="contacts-widget" i18n="##Contacts">Contactpersonen</option>
<option value="attachment-widget" i18n="##Attachments">Bijlage(n)</option>
</select>
</div>
<ng-container [ngSwitch]="widget.type">
<text-widget *ngSwitchCase="'text-widget'" [data]="widget"></text-widget>
<tasklist-widget *ngSwitchCase="'tasklist-widget'" [data]="widget"></tasklist-widget>
<image-widget *ngSwitchCase="'image-widget'" [data]="widget"></image-widget>
<video-widget *ngSwitchCase="'video-widget'" [data]="widget"></video-widget>
<link-widget *ngSwitchCase="'link-widget'" [data]="widget"></link-widget>
<contacts-widget *ngSwitchCase="'contacts-widget'" [data]="widget"></contacts-widget>
<attachment-widget *ngSwitchCase="'attachment-widget'" [data]="widget"></attachment-widget>
</ng-container>
</form>
Every widget is it's own component.
The problem is that the form validation only checks the inputs from the main part and disregards the sub part (widget components). How can I make sure the input fields from the widgets are included in the validation?
I tried adding an isValid() method to the widget components but I couldn't get the instances of the components, probably because they are used in an ngSwitch. #ContentChild, #ContentChildren, #ViewChild etc. all returned undefined.
For future googlers,
I had a similar issue to this, albeit with fewer child components and after digging through #penleychan's aforementioned thread on the subject I found a little gem that solved this for me without the need to implement a custom directive.
import { ControlContainer, NgForm } from '#angular/forms';
#Component({
....
viewProviders: [{ provide: ControlContainer, useExisting: NgForm }],
})
This works for my nested form. Just needs to be added to components,
which ones directly contains inputs
https://github.com/angular/angular/issues/9600#issuecomment-522898551
Hope i'm not too late. I recently stumbled on this issue too with template approach since reactive form did not fit what I needed to do...
The issue is something to do with ControlValueAccessor that your component need to implement. However I couldn't get that working.
See: https://github.com/angular/angular/issues/9600
Solution provided by andreev-artem works well, and I also added my solution to wrap it inside ngModelGroup instead of in the form's root object controls property.
For your case you're not using ngModelGroup you could just have this directive
#Directive({
selector: '[provide-parent-form]',
providers: [
{
provide: ControlContainer,
useFactory: function (form: NgForm) {
return form;
},
deps: [NgForm]
}
]
})
export class ProvideParentForm {}
Usage: In your component at the root element before you have [(ngModel)] add the directive. Example:
<div provide-parent-form>
<input name="myInput" [(ngModel)]="myInput">
</div>
Now if you output your form object in your console or whatever you can see your component's controls under controls property of your form's object.
Decided to have an isValid method on the child component which indicates if the widget is filled out correctly. The form can only be saved when the form and widget component are both valid.
All widget components implement an IWidgetComponent interface which requires a changed EventEmitter property and an isValid method. One of the child widget components looks like this.
#Component({
selector: 'video-widget',
templateUrl: './video.component.html',
styleUrls: ['./video.component.css'],
providers: [YouTubeIdExistsValidator]
})
export class VideoComponent implements OnInit, OnDestroy, IWidgetComponent {
#Input("data")
widget: IWidget;
#Output("change")
changed = new EventEmitter<any>();
video: any;
modelChanged: Subject<string> = new Subject<string>();
public isValid(): boolean {
return this.widget.youtube_id && this.widget.youtube_id !== "" && this.video ? true : false;
}
constructor(private youtubeService: YoutubeService) {
this.modelChanged
.debounceTime(500) // wait 500ms after the last event before emitting last event
.distinctUntilChanged() // only emit if value is different from previous value
.subscribe(youtube_id => this.getYoutubeVideo(youtube_id));
}
ngOnDestroy(): void {
this.widget.youtube_id = "";
}
getYoutubeVideo(youtube_id: string) {
this.youtubeService
.getById(youtube_id)
.subscribe((video) => {
this.video = video;
// Indicate that video was changed
this.changed.emit();
}, (error) => {
this.video = null;
});
}
youtubeIdChanged(youtube_id: string) {
this.modelChanged.next(youtube_id);
}
ngOnInit() { }
}
The parent html looks like this:
<form #widgetForm novalidate>
...
<ng-container [ngSwitch]="widget.type">
<text-widget #ref *ngSwitchCase="'text-widget'" [data]="widget" (change)="saveChanges()"></text-widget>
<tasklist-widget #ref *ngSwitchCase="'tasklist-widget'" [data]="widget" (change)="saveChanges()"></tasklist-widget>
<image-widget #ref *ngSwitchCase="'image-widget'" [data]="widget" (change)="saveChanges()"></image-widget>
<video-widget #ref *ngSwitchCase="'video-widget'" [data]="widget" (change)="saveChanges()"></video-widget>
<link-widget #ref *ngSwitchCase="'link-widget'" [data]="widget" (change)="saveChanges()"></link-widget>
<contacts-widget #ref *ngSwitchCase="'contacts-widget'" [data]="widget" (change)="saveChanges()"></contacts-widget>
<attachment-widget #ref *ngSwitchCase="'attachment-widget'" [data]="widget" (change)="saveChanges()"></attachment-widget>
</ng-container>
...
</form>
Each time the widget changes an event is emitted (this.changed.emit()) which triggers the save form method in the parent component. In this method I check if the form and widget are valid, if it is then the data may be saved.
saveChanges() {
if (this.ref && this.ref.isValid() && this.widgetForm.valid) {
// save form
this.toastr.success("Saved!");
}
else {
this.toastr.warning("Form not saved!");
}
}

On Button Click Validate a template form in Angular 2

I am new to Angular 2.
I have created a simple template which has two text field, I want to required field validate those two fields.
Login Form
<form #loginForm="ngForm" (ngSubmit)="onSubmit(loginForm)" novalidate>
<div class="container">
<div class="form-group">
ooooo <label><b>Username</b></label>
<input type="text" placeholder="Enter Username" name="uname" required [(ngModel)]="UserData.uname" #uname="ngModel">
<div *ngIf="loginForm.invalid" class="alert alert-danger">
<div [hidden]="!uname.errors.required"> Name is required </div>
</div>
</div>
<div class="form-group">
<label><b>Password</b></label>
<input type="password" placeholder="Enter Password" name="pwd" required [(ngModel)]="UserData.pwd" #pwd="ngModel">
<div *ngIf="UserData.pwd.errors && (UserData.pwd.dirty || UserData.pwd.touched)" class="alert alert-danger">
<div [hidden]="!UserData.pwd.errors.required">Password is required </div>
</div>
<button type="submit" >Login</button>
</div>
</div>
</form>
My Component
import { Component } from "#angular/core"
import { User } from "./UserModel"
#Component({
selector: 'my-login',
templateUrl:"app/Login/login.html"
})
export class LoginComponent
{
//alert: any("hello");
UserData: User = new User("", "");
submitted = false;
onSubmit(form: any) {
alert("dfsdfsd" + form);
if (!form.invalid) {
alert(this.UserData.uname);
alert(this.UserData.pwd);
this.submitted = true;
}
}
}
What i want to implement is-
When the form loads no validation message should appear?
When user clicks on the submit button then the required message should appear?
In both the textbox i have applied different type of checks to show the message that is inconsistent? so there should be a consistent way to solve this.
Many thanks for the help.
Maybe make use of the submitted variable, and use that in template, to not show message, until submitted is true, which we set it as in the submit function.
Also you wouldn't really need the two-way-binding here, since the object your form produces is directly assignable to your UserData.
The validation messages I'd just set then simply like this, where we are targeting the username:
<div *ngIf="uname.errors?.required && submitted"> Name is required </div>
in your submit function I'd pass loginForm.value as parameter instead of just loginForm. This way you get the form object ready to be used :)
And in your function you can assign the object to your UserData variable.
onSubmit(form: any) {
this.submitted = true;
this.UserData = form;
}
If you do want to keep the two-way-binding, it's of course totally possible! :)
DEMO

Trigger validation of all fields in Angular Form submit

I'm using this method: http://plnkr.co/edit/A6gvyoXbBd2kfToPmiiA?p=preview to only validate fields on blur. This works fine, but I would also like to validate them (and thus show the errors for those fields if any) when the user clicks the 'submit' button (not a real submit but a data-ng-click call to a function)
Is there some way to trigger validation on all the fields again when clicking that button?
What worked for me was using the $setSubmitted function, which first shows up in the angular docs in version 1.3.20.
In the click event where I wanted to trigger the validation, I did the following:
vm.triggerSubmit = function() {
vm.homeForm.$setSubmitted();
...
}
That was all it took for me. According to the docs it "Sets the form to its submitted state." It's mentioned here.
I know, it's a tad bit too late to answer, but all you need to do is, force all forms dirty. Take a look at the following snippet:
angular.forEach($scope.myForm.$error.required, function(field) {
field.$setDirty();
});
and then you can check if your form is valid using:
if($scope.myForm.$valid) {
//Do something
}
and finally, I guess, you would want to change your route if everything looks good:
$location.path('/somePath');
Edit: form won't register itself on the scope until submit event is trigger. Just use ng-submit directive to call a function, and wrap the above in that function, and it should work.
In case someone comes back to this later... None of the above worked for me. So I dug down into the guts of angular form validation and found the function they call to execute validators on a given field. This property is conveniently called $validate.
If you have a named form myForm, you can programmatically call myForm.my_field.$validate() to execute field validation. For example:
<div ng-form name="myForm">
<input required name="my_field" type="text" ng-blur="myForm.my_field.$validate()">
</div>
Note that calling $validate has implications for your model. From the angular docs for ngModelCtrl.$validate:
Runs each of the registered validators (first synchronous validators and then asynchronous validators). If the validity changes to invalid, the model will be set to undefined, unless ngModelOptions.allowInvalid is true. If the validity changes to valid, it will set the model to the last available valid $modelValue, i.e. either the last parsed value or the last value set from the scope.
So if you're planning on doing something with the invalid model value (like popping a message telling them so), then you need to make sure allowInvalid is set to true for your model.
You can use Angular-Validator to do what you want. It's stupid simple to use.
It will:
Only validate the fields on $dirty or on submit
Prevent the form from being submitted if it is invalid
Show custom error message after the field is $dirty or the form is submitted
See the demo
Example
<form angular-validator
angular-validator-submit="myFunction(myBeautifulForm)"
name="myBeautifulForm">
<!-- form fields here -->
<button type="submit">Submit</button>
</form>
If the field does not pass the validator then the user will not be able to submit the form.
Check out angular-validator use cases and examples for more information.
Disclaimer: I am the author of Angular-Validator
Well, the angular way would be to let it handle validation, - since it does at every model change - and only show the result to the user, when you want.
In this case you decide when to show the errors, you just have to set a flag:
http://plnkr.co/edit/0NNCpQKhbLTYMZaxMQ9l?p=preview
As far as I know there is a issue filed to angular to let us have more advanced form control. Since it is not solved i would use this instead of reinventing all the existing validation methods.
edit: But if you insist on your way, here is your modified fiddle with validation before submit. http://plnkr.co/edit/Xfr7X6JXPhY9lFL3hnOw?p=preview
The controller broadcast an event when the button is clicked, and the directive does the validation magic.
One approach is to force all attributes to be dirty. You can do that in each controller, but it gets very messy. It would be better to have a general solution.
The easiest way I could think of was to use a directive
it will handle the form submit attribute
it iterates through all form fields and marks pristine fields dirty
it checks if the form is valid before calling the submit function
Here is the directive
myModule.directive('submit', function() {
return {
restrict: 'A',
link: function(scope, formElement, attrs) {
var form;
form = scope[attrs.name];
return formElement.bind('submit', function() {
angular.forEach(form, function(field, name) {
if (typeof name === 'string' && !name.match('^[\$]')) {
if (field.$pristine) {
return field.$setViewValue(field.$value);
}
}
});
if (form.$valid) {
return scope.$apply(attrs.submit);
}
});
}
};
});
And update your form html, for example:
<form ng-submit='justDoIt()'>
becomes:
<form name='myForm' novalidate submit='justDoIt()'>
See a full example here: http://plunker.co/edit/QVbisEK2WEbORTAWL7Gu?p=preview
Here is my global function for showing the form error messages.
function show_validation_erros(form_error_object) {
angular.forEach(form_error_object, function (objArrayFields, errorName) {
angular.forEach(objArrayFields, function (objArrayField, key) {
objArrayField.$setDirty();
});
});
};
And in my any controllers,
if ($scope.form_add_sale.$invalid) {
$scope.global.show_validation_erros($scope.form_add_sale.$error);
}
Based on Thilak's answer I was able to come up with this solution...
Since my form fields only show validation messages if a field is invalid, and has been touched by the user I was able to use this code triggered by a button to show my invalid fields:
// Show/trigger any validation errors for this step
angular.forEach(vm.rfiForm.stepTwo.$error, function(error) {
angular.forEach(error, function(field) {
field.$setTouched();
});
});
// Prevent user from going to next step if current step is invalid
if (!vm.rfiForm.stepTwo.$valid) {
isValid = false;
}
<!-- form field -->
<div class="form-group" ng-class="{ 'has-error': rfi.rfiForm.stepTwo.Parent_Suffix__c.$touched && rfi.rfiForm.stepTwo.Parent_Suffix__c.$invalid }">
<!-- field label -->
<label class="control-label">Suffix</label>
<!-- end field label -->
<!-- field input -->
<select name="Parent_Suffix__c" class="form-control"
ng-options="item.value as item.label for item in rfi.contact.Parent_Suffixes"
ng-model="rfi.contact.Parent_Suffix__c" />
<!-- end field input -->
<!-- field help -->
<span class="help-block" ng-messages="rfi.rfiForm.stepTwo.Parent_Suffix__c.$error" ng-show="rfi.rfiForm.stepTwo.Parent_Suffix__c.$touched">
<span ng-message="required">this field is required</span>
</span>
<!-- end field help -->
</div>
<!-- end form field -->
Note: I know this is a hack, but it was useful for Angular 1.2 and earlier that didn't provide a simple mechanism.
The validation kicks in on the change event, so some things like changing the values programmatically won't trigger it. But triggering the change event will trigger the validation. For example, with jQuery:
$('#formField1, #formField2').trigger('change');
I like the this approach in handling validation on button click.
There is no need to invoke anything from controller,
it's all handled with a directive.
on github
You can try this:
// The controller
$scope.submitForm = function(form){
//Force the field validation
angular.forEach(form, function(obj){
if(angular.isObject(obj) && angular.isDefined(obj.$setDirty))
{
obj.$setDirty();
}
})
if (form.$valid){
$scope.myResource.$save(function(data){
//....
});
}
}
<!-- FORM -->
<form name="myForm" role="form" novalidate="novalidate">
<!-- FORM GROUP to field 1 -->
<div class="form-group" ng-class="{ 'has-error' : myForm.field1.$invalid && myForm.field1.$dirty }">
<label for="field1">My field 1</label>
<span class="nullable">
<select name="field1" ng-model="myresource.field1" ng-options="list.id as list.name for list in listofall"
class="form-control input-sm" required>
<option value="">Select One</option>
</select>
</span>
<div ng-if="myForm.field1.$dirty" ng-messages="myForm.field1.$error" ng-messages-include="mymessages"></div>
</div>
<!-- FORM GROUP to field 2 -->
<div class="form-group" ng-class="{ 'has-error' : myForm.field2.$invalid && myForm.field2.$dirty }">
<label class="control-label labelsmall" for="field2">field2</label>
<input name="field2" min="1" placeholder="" ng-model="myresource.field2" type="number"
class="form-control input-sm" required>
<div ng-if="myForm.field2.$dirty" ng-messages="myForm.field2.$error" ng-messages-include="mymessages"></div>
</div>
</form>
<!-- ... -->
<button type="submit" ng-click="submitForm(myForm)">Send</button>
I done something following to make it work.
<form name="form" name="plantRegistrationForm">
<div ng-class="{ 'has-error': (form.$submitted || form.headerName.$touched) && form.headerName.$invalid }">
<div class="col-md-3">
<div class="label-color">HEADER NAME
<span class="red"><strong>*</strong></span></div>
</div>
<div class="col-md-9">
<input type="text" name="headerName" id="headerName"
ng-model="header.headerName"
maxlength="100"
class="form-control" required>
<div ng-show="form.$submitted || form.headerName.$touched">
<span ng-show="form.headerName.$invalid"
class="label-color validation-message">Header Name is required</span>
</div>
</div>
</div>
<button ng-click="addHeader(form, header)"
type="button"
class="btn btn-default pull-right">Add Header
</button>
</form>
In your controller you can do;
addHeader(form, header){
let self = this;
form.$submitted = true;
...
}
You need some css as well;
.label-color {
color: $gray-color;
}
.has-error {
.label-color {
color: rgb(221, 25, 29);
}
.select2-choice.ui-select-match.select2-default {
border-color: #e84e40;
}
}
.validation-message {
font-size: 0.875em;
}
.max-width {
width: 100%;
min-width: 100%;
}
To validate all fields of my form when I want, I do a validation on each field of $$controls like this :
angular.forEach($scope.myform.$$controls, function (field) {
field.$validate();
});