I have a Reactive Form (#myform) with a button "foo", to which is attached (click) = myform.submit() so that the form will only submit when that button is clicked, and not on Enter events. The component listens for Enter events, and dispatches blur in that case, and the FormControls all have {updateOn: 'blur'} so blur events trigger updates (i.e., valueChanges, etc).
My problem is this: If I key data into an Input (or select an item in a Select) without hitting Enter or Tabbing out of the element and then click on the "foo", i.e. "Submit" button it seems that clicking the "foo" button only causes a blur event without triggering submission of the form (through myform.submit). I.e., the submit gets "swallowed" by the blur event.
How can I set this up so that if I don't Enter or Tab out of an element, and immediately click "foo", the element will be updated through the blur event AND THEN the form will ALSO be submitted?
I've tried things such as: 1. first processing the blur and then trying to programatically call myform.submit() in the component (by accessing the Form through @ViewChild), and 2. adding a (blur)= (submitNow && setTimeout(()=>myform.submit(),50) listener in the template view. At this point, I haven't even got this setTimeout call to compile, and none of the approaches I've tried seem to work so far.
The original code (simplified) is like this:
template details.component.html
<form #myform [formGroup]="form" method="POST" action="http://localhost:3000/users/{{userID}}/details">
<ng-container *ngFor="let sec of datArr">
<ng-container *ngFor="let row of sec; let i = index">
<input type="text"
[appTag]="row[datColEnum.tabCol]"
(keydown.enter)="onEnter($event)"
tabindex = "{{row[datColEnum.tabCol]}}"
name="{{row[datColEnum.controlNameCol]}}" value="{{row[datColEnum.valueCol]}}"
formControlName="{{row[datColEnum.controlNameCol]}}"/>
</ng-container>
</ng-container>
<button name="foo" type="button" (click)="myform.submit()">Submit</button>
</form>
component details.component.ts
export class DetailsComponent implements OnInit, AfterViewInit {
@ViewChildren(TagDirective) ipt!: QueryList<ElementRef>;
@ViewChild('myform', {static: true}) myform: any;
data: Object;
meta: Object;
datArr: any[] = [];
formCtls: any = {};
form: FormGroup = new FormGroup({});
tabIdx: number = 0;
focusItm: number = 0;
submitNow: boolean = false;
constructor(private member: MemberService, private route: ActivatedRoute, private el: ElementRef) { }
ngAfterViewInit() {
this.ipt.changes.subscribe(list=>{
setTimeout(()=>
list.filter(itm=>+itm.id===this.focusItm%this.tabIdx).forEach(itm=>{
if (!this.first_pass) this.navigateTo(itm.id);
}
),100)
})
}
doSubmit() {
this.submitNow = true;
}
navigateTo(ti: number) {
console.log(`NAVIGATING TO ITEM WITH TAB INDEX: ${ti}`)
this.ipt["_results"][ti-1].el.nativeElement.focus();
this.ipt["_results"][ti-1].el.nativeElement.style.borderWidth = "3px";
}
onEnter(e: Event) {
let cname = (<HTMLInputElement | HTMLSelectElement>e.target).name;
let idx = 0;
for (let itm of this.ipt["_results"]) {
if (itm.el.nativeElement.name === cname) {
this.focusItm = (itm.id+1)%this.ipt["_results"].length;
itm.el.nativeElement.style.borderWidth = "1px";
break;
}
}
e.target.dispatchEvent(new Event('blur'));
}
ngOnInit() {
. . .
. . .
//SET EVERYTHING UP
this.renderDataArray();
. . .
. . .
})
};
// SET UP ALL THE FormControls, etc. AND THE DATA MODEL datArr USED TO RENDER THE TEMPLATE
renderDataArray() {
this.datArr = [];
this.tabIdx = 0;
Object.keys(this.meta).forEach((sec,idx) => {
let rowCnt = this.getActiveArrayLen(sec);
for (let i = 0; i < rowCnt; i++) {
Object.keys(this.data[sec]).
.map((fld,idx)=>{
let itm = this.data[sec][fld];
this.tabIdx += 1;
let val = getVal();
let ctlName = sec+this.FLD_SPLIT_CHAR+fld;
this.removeControl(ctlName);
this.formCtls[ctlName] = new FormControl(val, {updateOn: 'blur'});
this.form.addControl(ctlName, this.formCtls[ctlName]);
of(this.tabIdx).
pipe(switchMap(ti=>this.formCtls[ctlName].valueChanges.
pipe(map(newVal => [newVal,ti]))
)
).subscribe(([newVal,ti])=>{
if (Array.isArray(itm["value"])) {
itm["value"][+(itm["value"].length || 1)-1]=newVal || '';
} else {
itm["value"]=newVal || '';
}
this.focusItm = (ti+1)%this.ipt["_results"].length;
this.renderDataArray();
});
. . .
. . .