I have this sort of form:
this.form = this.formBuilder.group(
{
name: new FormControl('', Validators.required),
// more controls here
}
);
Html:
<form (ngSubmit)="onSubmit()" [formGroup]="form">
<div
[class.form-control--valid]="vm.name.valid && (vm.name.dirty || vm.name.touched)"
[class.form-control--invalid]="vm.name.invalid && (vm.name.dirty || vm.name.touched)">
<label for="form_name" >Your name</label>
<div >
<input type="text" id="form_name" name="name" formControlName="name">
</div>
<div *ngIf="vm.name.errors && (vm.name.dirty || vm.name.touched)">
<p *ngIf="vm.name.errors.required">First name is required</p>
</div>
</div>
<!-- more controls here-->
<button type="submit" [disabled]='!form.valid'>Submit</button>
</form>
vm
is equal to this.form.controls;
It seems to me that the html contains to much code related to form validation.
I wonder if it is possible to reduce the amount of markup code to avoid writing all that vm.name.invalid && (vm.name.dirty || vm.name.touched
.
Also I believe things like that could be simplified, it is a lot of boilerplate to:
[class.form-control--valid]="vm.name.valid && (vm.name.dirty || vm.name.touched)"
[class.form-control--invalid]="vm.name.invalid && (vm.name.dirty || vm.name.touched)"
CodePudding user response:
Angular yet add different class if valid or touched. See the docs
Based on it, I suggest these are the class you can add to your divs (*)
As always we can improve a "tag" sometimes it's util use a directive
@Directive({
selector: '[errorClass]'
})
export class ErrorClassDirective {
@ContentChild(NgControl) control:NgControl;
@HostBinding('class') get class()
{
if (!this.control)
return null
let className=this.control.invalid?'ng-invalid ':'ng-valid '
className =this.control.touched?'ng-touched ': 'ng-untouched'
className =this.control.dirty?'ng-dirty ': 'ng-pristine'
return className;
}
}
Then we can use some like
<form [formGroup]="form">
<div errorClass>
<label>Control</label>
<input formControlName="control" />
</div>
</form>
Now we can play with .css
e.g.
/*Some class to style the inputs and labels*/
label {
margin-right: 0.25rem;
}
input {
background-color: #fff;
background-clip: padding-box;
border: 1px solid #ced4da;
border-radius: 0.25rem;
padding: 0.25rem;
}
input:focus {
color: #212529;
background-color: #fff;
border-color: #86b7fe;
outline: 0;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
/*Here style the "div"*/
.ng-invalid.ng-touched:not(input),
.ng-invalid.ng-dirty:not(input) {
color: red;
}
/*Style the input invalid*/
input.ng-invalid.ng-touched,
input.ng-invalid.ng-dirty {
border-color: red;
outline: none;
}
input.ng-invalid.ng-touched:focus,
input.ng-invalid.ng-dirty:focus {
border-color: red;
box-shadow: 0 0 0 0.25rem rgba(255, 0, 0, 0.25);
}
See stackblitz
(*) with the new selector has you needn't any directive, We can simply use a class in the way
div:has(input.ng-invalid.ng-touched)
{
color:red
}
Unfortunaly this selector is not ready for some navigator (Firefox need User must explicitly enable this feature)