TSK-1039: Autofocus AccessID field after adding a new AccessID in Workbasket

This commit is contained in:
Tristan Eisermann 2020-06-15 10:52:21 +02:00 committed by Tristan2357
parent 716ad7a7df
commit 015ba38a25
3 changed files with 87 additions and 49 deletions

View File

@ -42,7 +42,7 @@
'has-warning': (accessItemsClone[index].accessId !== accessItem.value.accessId), 'has-warning': (accessItemsClone[index].accessId !== accessItem.value.accessId),
'has-error': !accessItem.value.accessId }"> 'has-error': !accessItem.value.accessId }">
<taskana-type-ahead formControlName="accessId" placeHolderMessage="* Access id is required" [validationValue]="toogleValidationAccessIdMap.get(index)" <taskana-type-ahead formControlName="accessId" placeHolderMessage="* Access id is required" [validationValue]="toogleValidationAccessIdMap.get(index)"
[displayError]="!isFieldValid('accessItem.value.accessId', index)" (selectedItem)="accessItemSelected($event, index)"></taskana-type-ahead> [displayError]="!isFieldValid('accessItem.value.accessId', index)" (selectedItem)="accessItemSelected($event, index)" (inputField)="focusNewInput($event)"></taskana-type-ahead>
</td> </td>
<ng-template #accessIdInput> <ng-template #accessIdInput>
<td class="input-group text-align text-width"> <td class="input-group text-align text-width">
@ -50,7 +50,7 @@
!accessItem.value.accessId && formSubmitAttempt}"> !accessItem.value.accessId && formSubmitAttempt}">
<input type="text" class="form-control" formControlName="accessId" placeholder="{{accessItem.invalid? <input type="text" class="form-control" formControlName="accessId" placeholder="{{accessItem.invalid?
'* Access id is required': ''}}" '* Access id is required': ''}}"
[@validation]="toogleValidationAccessIdMap.get(index)"> [@validation]="toogleValidationAccessIdMap.get(index)" #htmlInputElement>
</div> </div>
</td> </td>
</ng-template> </ng-template>
@ -94,4 +94,4 @@
</button> </button>
<taskana-spinner [isRunning]="requestInProgress" [positionClass]=""></taskana-spinner> <taskana-spinner [isRunning]="requestInProgress" [positionClass]=""></taskana-spinner>
</div> </div>
</div> </div>

View File

@ -1,14 +1,21 @@
import { Component, Input, OnChanges, OnDestroy, SimpleChanges } from '@angular/core'; import { Component,
ElementRef,
Input,
OnChanges,
OnDestroy, QueryList,
SimpleChanges,
ViewChildren } from '@angular/core';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { Select } from '@ngxs/store'; import { Select } from '@ngxs/store';
import { FormArray, FormBuilder, Validators } from '@angular/forms'; import { FormArray, FormBuilder, FormControlDirective, Validators } from '@angular/forms';
import { Workbasket } from 'app/shared/models/workbasket'; import { Workbasket } from 'app/shared/models/workbasket';
import { customFieldCount, WorkbasketAccessItems } from 'app/shared/models/workbasket-access-items'; import { customFieldCount, WorkbasketAccessItems } from 'app/shared/models/workbasket-access-items';
import { WorkbasketAccessItemsResource } from 'app/shared/models/workbasket-access-items-resource'; import { WorkbasketAccessItemsResource } from 'app/shared/models/workbasket-access-items-resource';
import { ACTION } from 'app/shared/models/action'; import { ACTION } from 'app/shared/models/action';
import { SavingInformation, SavingWorkbasketService } from 'app/administration/services/saving-workbaskets.service'; import { SavingInformation,
SavingWorkbasketService } from 'app/administration/services/saving-workbaskets.service';
import { WorkbasketService } from 'app/shared/services/workbasket/workbasket.service'; import { WorkbasketService } from 'app/shared/services/workbasket/workbasket.service';
import { RequestInProgressService } from 'app/shared/services/request-in-progress/request-in-progress.service'; import { RequestInProgressService } from 'app/shared/services/request-in-progress/request-in-progress.service';
import { highlight } from 'theme/animations/validation.animation'; import { highlight } from 'theme/animations/validation.animation';
@ -17,7 +24,9 @@ import { AccessIdDefinition } from 'app/shared/models/access-id';
import { EngineConfigurationSelectors } from 'app/shared/store/engine-configuration-store/engine-configuration.selectors'; import { EngineConfigurationSelectors } from 'app/shared/store/engine-configuration-store/engine-configuration.selectors';
import { NOTIFICATION_TYPES } from '../../../shared/models/notifications'; import { NOTIFICATION_TYPES } from '../../../shared/models/notifications';
import { NotificationService } from '../../../shared/services/notifications/notification.service'; import { NotificationService } from '../../../shared/services/notifications/notification.service';
import { AccessItemsCustomisation, CustomField, getCustomFields } from '../../../shared/models/customisation'; import { AccessItemsCustomisation,
CustomField,
getCustomFields } from '../../../shared/models/customisation';
@Component({ @Component({
selector: 'taskana-workbasket-access-items', selector: 'taskana-workbasket-access-items',
@ -35,6 +44,9 @@ export class WorkbasketAccessItemsComponent implements OnChanges, OnDestroy {
@Input() @Input()
active: string; active: string;
@ViewChildren('htmlInputElement') inputs: QueryList<ElementRef>;
badgeMessage = ''; badgeMessage = '';
@Select(EngineConfigurationSelectors.accessItemsCustomisation) accessItemsCustomization$: Observable<AccessItemsCustomisation>; @Select(EngineConfigurationSelectors.accessItemsCustomisation) accessItemsCustomization$: Observable<AccessItemsCustomisation>;
@ -52,6 +64,7 @@ export class WorkbasketAccessItemsComponent implements OnChanges, OnDestroy {
toogleValidationAccessIdMap = new Map<number, boolean>(); toogleValidationAccessIdMap = new Map<number, boolean>();
private initialized = false; private initialized = false;
private added = false;
constructor( constructor(
private workbasketService: WorkbasketService, private workbasketService: WorkbasketService,
@ -71,13 +84,10 @@ export class WorkbasketAccessItemsComponent implements OnChanges, OnDestroy {
this.customFields$ = this.accessItemsCustomization$.pipe(getCustomFields(customFieldCount)); this.customFields$ = this.accessItemsCustomization$.pipe(getCustomFields(customFieldCount));
} }
setAccessItemsGroups(accessItems: Array<WorkbasketAccessItems>) { ngAfterViewInit() {
const AccessItemsFormGroups = accessItems.map(accessItem => this.formBuilder.group(accessItem)); this.inputs.changes.subscribe(next => {
AccessItemsFormGroups.forEach(accessItemGroup => { if (this.added) next.last.nativeElement.focus();
accessItemGroup.controls.accessId.setValidators(Validators.required);
}); });
const AccessItemsFormArray = this.formBuilder.array(AccessItemsFormGroups);
this.AccessItemsForm.setControl('accessItemsGroups', AccessItemsFormArray);
} }
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
@ -89,6 +99,39 @@ export class WorkbasketAccessItemsComponent implements OnChanges, OnDestroy {
} }
} }
private init() {
if (!this.workbasket._links.accessItems) {
return;
}
this.requestInProgress = true;
this.accessItemsubscription = this.workbasketService.getWorkBasketAccessItems(this.workbasket._links.accessItems.href)
.subscribe((accessItemsResource: WorkbasketAccessItemsResource) => {
this.accessItemsResource = accessItemsResource;
this.setAccessItemsGroups(accessItemsResource.accessItems);
this.accessItemsClone = this.cloneAccessItems(accessItemsResource.accessItems);
this.accessItemsResetClone = this.cloneAccessItems(accessItemsResource.accessItems);
this.requestInProgress = false;
});
this.savingAccessItemsSubscription = this.savingWorkbaskets.triggeredAccessItemsSaving()
.subscribe((savingInformation: SavingInformation) => {
if (this.action === ACTION.COPY) {
this.accessItemsResource._links.self.href = savingInformation.url;
this.setWorkbasketIdForCopy(savingInformation.workbasketId);
this.onSave();
}
});
this.initialized = true;
}
setAccessItemsGroups(accessItems: Array<WorkbasketAccessItems>) {
const AccessItemsFormGroups = accessItems.map(accessItem => this.formBuilder.group(accessItem));
AccessItemsFormGroups.forEach(accessItemGroup => {
accessItemGroup.controls.accessId.setValidators(Validators.required);
});
const AccessItemsFormArray = this.formBuilder.array(AccessItemsFormGroups);
this.AccessItemsForm.setControl('accessItemsGroups', AccessItemsFormArray);
}
addAccessItem() { addAccessItem() {
const workbasketAccessItems = new WorkbasketAccessItems(); const workbasketAccessItems = new WorkbasketAccessItems();
workbasketAccessItems.workbasketId = this.workbasket.workbasketId; workbasketAccessItems.workbasketId = this.workbasket.workbasketId;
@ -97,6 +140,7 @@ export class WorkbasketAccessItemsComponent implements OnChanges, OnDestroy {
newForm.controls.accessId.setValidators(Validators.required); newForm.controls.accessId.setValidators(Validators.required);
this.accessItemsGroups.push(newForm); this.accessItemsGroups.push(newForm);
this.accessItemsClone.push(workbasketAccessItems); this.accessItemsClone.push(workbasketAccessItems);
this.added = true;
} }
clear() { clear() {
@ -140,39 +184,6 @@ export class WorkbasketAccessItemsComponent implements OnChanges, OnDestroy {
this.accessItemsGroups.controls[row].get('accessName').setValue(accessItem.name); this.accessItemsGroups.controls[row].get('accessName').setValue(accessItem.name);
} }
ngOnDestroy(): void {
if (this.accessItemsubscription) {
this.accessItemsubscription.unsubscribe();
}
if (this.savingAccessItemsSubscription) {
this.savingAccessItemsSubscription.unsubscribe();
}
}
private init() {
this.initialized = true;
if (!this.workbasket._links.accessItems) {
return;
}
this.requestInProgress = true;
this.accessItemsubscription = this.workbasketService.getWorkBasketAccessItems(this.workbasket._links.accessItems.href)
.subscribe((accessItemsResource: WorkbasketAccessItemsResource) => {
this.accessItemsResource = accessItemsResource;
this.setAccessItemsGroups(accessItemsResource.accessItems);
this.accessItemsClone = this.cloneAccessItems(accessItemsResource.accessItems);
this.accessItemsResetClone = this.cloneAccessItems(accessItemsResource.accessItems);
this.requestInProgress = false;
});
this.savingAccessItemsSubscription = this.savingWorkbaskets.triggeredAccessItemsSaving()
.subscribe((savingInformation: SavingInformation) => {
if (this.action === ACTION.COPY) {
this.accessItemsResource._links.self.href = savingInformation.url;
this.setWorkbasketIdForCopy(savingInformation.workbasketId);
this.onSave();
}
});
}
private onSave() { private onSave() {
this.requestInProgressService.setRequestInProgress(true); this.requestInProgressService.setRequestInProgress(true);
this.workbasketService.updateWorkBasketAccessItem( this.workbasketService.updateWorkBasketAccessItem(
@ -212,4 +223,20 @@ export class WorkbasketAccessItemsComponent implements OnChanges, OnDestroy {
getAccessItemCustomProperty(customNumber: number): string { getAccessItemCustomProperty(customNumber: number): string {
return `permCustom${customNumber}`; return `permCustom${customNumber}`;
} }
focusNewInput(input: ElementRef) {
if (this.added) {
input.nativeElement.focus();
}
}
ngOnDestroy(): void {
if (this.accessItemsubscription) {
this.accessItemsubscription.unsubscribe();
}
if (this.savingAccessItemsSubscription) {
this.savingAccessItemsSubscription.unsubscribe();
}
}
} }

View File

@ -1,4 +1,11 @@
import { Component, OnInit, Input, ViewChild, forwardRef, Output, EventEmitter } from '@angular/core'; import { Component,
OnInit,
Input,
ViewChild,
forwardRef,
Output,
EventEmitter,
OnChanges, ElementRef, AfterViewInit } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { TypeaheadMatch } from 'ngx-bootstrap/typeahead'; import { TypeaheadMatch } from 'ngx-bootstrap/typeahead';
@ -21,7 +28,7 @@ import { AccessIdDefinition } from 'app/shared/models/access-id';
} }
] ]
}) })
export class TypeAheadComponent implements OnInit, ControlValueAccessor { export class TypeAheadComponent implements AfterViewInit, ControlValueAccessor {
dataSource: any; dataSource: any;
typing = false; typing = false;
@ -46,6 +53,9 @@ export class TypeAheadComponent implements OnInit, ControlValueAccessor {
@Output() @Output()
selectedItem = new EventEmitter<AccessIdDefinition>(); selectedItem = new EventEmitter<AccessIdDefinition>();
@Output()
inputField = new EventEmitter<ElementRef>();
@ViewChild('inputTypeAhead', { static: false }) @ViewChild('inputTypeAhead', { static: false })
private inputTypeAhead; private inputTypeAhead;
@ -96,7 +106,8 @@ export class TypeAheadComponent implements OnInit, ControlValueAccessor {
constructor(private accessIdsService: AccessIdsService) { constructor(private accessIdsService: AccessIdsService) {
} }
ngOnInit() { ngAfterViewInit() {
this.inputField.emit(this.inputTypeAhead);
} }
initializeDataSource() { initializeDataSource() {