Merge pull request #5684 from tsiegleauq/vscroll-selection-bugs

Fix a bug where vscroll select lists lost content
This commit is contained in:
Emanuel Schütze 2020-11-07 18:26:39 +01:00 committed by GitHub
commit 22f9108b49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 65 additions and 10 deletions

View File

@ -3,7 +3,17 @@
[multiple]="multiple" [multiple]="multiple"
[panelClass]="{ 'os-search-value-selector': multiple }" [panelClass]="{ 'os-search-value-selector': multiple }"
[errorStateMatcher]="errorStateMatcher" [errorStateMatcher]="errorStateMatcher"
(openedChange)="openSelect($event)"
> >
<!-- Custom display of selected items -->
<mat-select-trigger>
<ng-container *ngIf="selectedItems?.length">
<span *ngFor="let item of selectedItems; let i = index">
{{ item.getTitle() | translate }}<span *ngIf="i < selectedItems.length - 1">, </span>
</span>
</ng-container>
</mat-select-trigger>
<mat-option> <mat-option>
<ngx-mat-select-search [formControl]="searchValue"></ngx-mat-select-search> <ngx-mat-select-search [formControl]="searchValue"></ngx-mat-select-search>
</mat-option> </mat-option>
@ -14,7 +24,7 @@
<mat-chip <mat-chip
*ngFor="let item of selectedItems" *ngFor="let item of selectedItems"
[removable]="true" [removable]="true"
(removed)="removeItem(item.id)" (removed)="removeChipItem(item)"
[disableRipple]="true" [disableRipple]="true"
> >
{{ item.getTitle() | translate }} {{ item.getTitle() | translate }}
@ -36,8 +46,12 @@
</mat-option> </mat-option>
<mat-divider></mat-divider> <mat-divider></mat-divider>
</ng-container> </ng-container>
<cdk-virtual-scroll-viewport class="vscroll-viewport" minBufferPx="200" maxBufferPx="300" [itemSize]="50"> <cdk-virtual-scroll-viewport class="vscroll-viewport" minBufferPx="400" maxBufferPx="600" [itemSize]="50">
<mat-option *cdkVirtualFor="let selectedItem of getFilteredItems()" [value]="selectedItem.id"> <mat-option
*cdkVirtualFor="let selectedItem of getFilteredItems()"
[value]="selectedItem.id"
(onSelectionChange)="onSelectionChange($event)"
>
{{ selectedItem.getTitle() | translate }} {{ selectedItem.getTitle() | translate }}
</mat-option> </mat-option>
</cdk-virtual-scroll-viewport> </cdk-virtual-scroll-viewport>

View File

@ -1,4 +1,5 @@
import { FocusMonitor } from '@angular/cdk/a11y'; import { FocusMonitor } from '@angular/cdk/a11y';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
@ -12,6 +13,7 @@ import {
ViewEncapsulation ViewEncapsulation
} from '@angular/core'; } from '@angular/core';
import { FormBuilder, FormControl, NgControl } from '@angular/forms'; import { FormBuilder, FormControl, NgControl } from '@angular/forms';
import { MatOptionSelectionChange } from '@angular/material/core';
import { MatFormFieldControl } from '@angular/material/form-field'; import { MatFormFieldControl } from '@angular/material/form-field';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
@ -19,6 +21,7 @@ import { Observable } from 'rxjs';
import { auditTime } from 'rxjs/operators'; import { auditTime } from 'rxjs/operators';
import { BaseFormControlComponentDirective } from 'app/shared/models/base/base-form-control'; import { BaseFormControlComponentDirective } from 'app/shared/models/base/base-form-control';
import { Identifiable } from 'app/shared/models/base/identifiable';
import { ParentErrorStateMatcher } from 'app/shared/parent-error-state-matcher'; import { ParentErrorStateMatcher } from 'app/shared/parent-error-state-matcher';
import { Selectable } from '../selectable'; import { Selectable } from '../selectable';
@ -55,6 +58,9 @@ export class SearchValueSelectorComponent extends BaseFormControlComponentDirect
@ViewChild('chipPlaceholder', { static: false }) @ViewChild('chipPlaceholder', { static: false })
public chipPlaceholder: ElementRef<HTMLElement>; public chipPlaceholder: ElementRef<HTMLElement>;
@ViewChild(CdkVirtualScrollViewport, { static: true })
public cdkVirtualScrollViewPort: CdkVirtualScrollViewport;
/** /**
* Decide if this should be a single or multi-select-field * Decide if this should be a single or multi-select-field
*/ */
@ -134,6 +140,8 @@ export class SearchValueSelectorComponent extends BaseFormControlComponentDirect
*/ */
private selectableItems: Selectable[]; private selectableItems: Selectable[];
public selectedIds: number[] = [];
public constructor( public constructor(
protected translate: TranslateService, protected translate: TranslateService,
formBuilder: FormBuilder, formBuilder: FormBuilder,
@ -144,6 +152,13 @@ export class SearchValueSelectorComponent extends BaseFormControlComponentDirect
super(formBuilder, focusMonitor, element, ngControl); super(formBuilder, focusMonitor, element, ngControl);
} }
public openSelect(event: boolean): void {
if (event) {
this.cdkVirtualScrollViewPort.scrollToIndex(0);
this.cdkVirtualScrollViewPort.checkViewportSize();
}
}
/** /**
* Function to get a list filtered by the entered search value. * Function to get a list filtered by the entered search value.
* *
@ -167,15 +182,30 @@ export class SearchValueSelectorComponent extends BaseFormControlComponentDirect
} }
} }
public removeItem(itemId: number): void { public removeChipItem(item: Selectable): void {
const items = <number[]>this.contentForm.value; this.addRemoveId(item.id);
items.splice(
items.findIndex(item => item === itemId),
1
);
this.contentForm.setValue(items);
} }
private addRemoveId(item: number): void {
const idx = this.selectedIds.indexOf(item);
if (idx > -1) {
this.selectedIds.splice(idx, 1);
} else {
this.selectedIds.push(item);
}
this.contentForm.setValue(this.selectedIds);
}
public onSelectionChange(change: MatOptionSelectionChange): void {
if (change.isUserInput) {
const value = change.source.value;
this.addRemoveId(value);
}
}
/**
* Satisfy parent
*/
public onContainerClick(event: MouseEvent): void { public onContainerClick(event: MouseEvent): void {
if ((event.target as Element).tagName.toLowerCase() !== 'select') { if ((event.target as Element).tagName.toLowerCase() !== 'select') {
// this.element.nativeElement.querySelector('select').focus(); // this.element.nativeElement.querySelector('select').focus();
@ -197,5 +227,16 @@ export class SearchValueSelectorComponent extends BaseFormControlComponentDirect
protected updateForm(value: Selectable[] | null): void { protected updateForm(value: Selectable[] | null): void {
this.contentForm.setValue(value); this.contentForm.setValue(value);
if (value?.length) {
/**
* Hack:
* for loaded or preselected form, add existing values to selected IDs.
* These are usually always numbers,
* Would be easier to absolutely always use Selectable and never use IDs,
* Could save some work, but every second form has to change for that.
* -> os4 todo
*/
this.selectedIds = value as any;
}
} }
} }