Background
Im using Reactive Forms that expands over two tabs (first half of the page alone has tabs) and then a long page with Submit button at the bottom. I do the validation on click of the Submit button.
On clicking of Submit button, the page needs to scroll to the error form field when Validation fails.
I'm also able to load the tab based on errors in FormGroups.
Problem
submit(){
//code to select the desired tab
this.selectedTab.animationDone.subscribe(res => {
this.myElement.nativeElement.ownerDocument.getElementsByClassName('ng-invalid mat-form-field')[0].scrollIntoView({ behavior: 'smooth' });
});
}
All works fine until this point !!!
Once the Approve Button is clicked, the subscription subscribes, and the page scrolls to the errors whenever a new Tab is selected. I wanted to unsubscribe to this observable and later subscribe back whenever the Submit button is clicked again.
I tried unsubscribe, but I'm not able to subscribe to it again. This sometimes destroys the subscribe function and throws error
I think I'm missing something and reaching out for help. Thanks in advance !!
More Code as Requested
submit()
{
if(this.bookingForm.valid){
// do some actions ....
}
else {
this.bookingForm.markAllAsTouched();
//subscribing to the event before selecting the tab
this.selectedTab.animationDone.subscribe(res => {
this.myElement.nativeElement.ownerDocument.getElementsByClassName('ng-invalid mat-form-field')[0].scrollIntoView({ behavior: 'smooth' });
});
// code to select the Tab where the error occurs .....
this.selectedTab.selectedIndex = this.errorIndex;
//unsubscribe
this.selectedTab.animationDone.unsubscribe()
} // close of else block
}// close of submit Function
The unsubscribe (or similar function) is required as this should subscribe only when the Submit button is clicked. If it is not unsubscribed (or paused) then the subscribe function is being called everytime the tab is changed and the page keeps scrolling up and down based on errors.
Page View
This is just for demonstration.
EDIT 2 below
Here is the StackBlitz Link. This is just a sample page, My page has much more fields and form Groups compared to this.
Recreating the Issue
Scenario 1
Sorry for the late response. I was a little busy.
I didn't look into the animation
part that you were trying, instead I focused on the basics as we just needed to focus on the element if it is invalid
.
submit() {
if(this.testForm.valid) {
//Code to Submit
} else {
((document.getElementsByClassName('mat-input-element ng-invalid')[0]) as HTMLElement).focus();
}
}
I came across this very cool way to do this.
import { MatInput } from '@angular/material';
import { QueryList, ViewChildren } from '@angular/core';
@ViewChildren(MatInput) inputs: QueryList <MatInput>;
submit() {
if(this.testForm.valid) {
//Code to Submit
} else {
this.inputs.find(input => !input.ngControl.valid).focus();
}
}
Also, if you always want Tab 1
to be selected first if it is invalid
, then put the other condition in else/ else if
section -
if(this.testForm.get('tab1').errors) {
this.tabControl.selectedIndex = 0;
} else if(this.testForm.get('tab2').errors) {
this.tabControl.selectedIndex = 1;
}
One more thing, you'll have to put a slight delay before focusing on invalid
elements as creating input
element for Tab 2
in DOM takes nanoSeconds of time and it focuses on Details 1
if either of the Tab 1
or 2
is invalid
.
delay(ms: number) {
return new Promise( resolve => setTimeout(resolve, ms) );
}
Just call this before focus()
and after switching tabs check
await this.delay(1);
If you don't want to set delay
, just set focus()
on animationDone
event again and set the animationDuration
time to minimum so you wouldn't see focus
on Details 1
in real time.
Follow the Methods
links for complete flow and let me know if you face any issues.