Merged PR 693: Fix Reading Age Filter and Added Error Message

Fix Reading Age Filter and Added Error Message
This commit is contained in:
Nino Righi
2021-05-28 12:35:43 +00:00
committed by Lorenz Hilpert
parent 751a175673
commit 58536ef3f7
8 changed files with 175 additions and 93 deletions

View File

@@ -1,7 +1,8 @@
import { StringDictionary } from '@cmf/core';
import { InputGroupDTO, InputDTO, OptionDTO, InputType } from '@swagger/cat';
import { SelectFilterGroup, SelectFilter, SelectFilterOption, FilterType, Filter, FilterOption, RangeFilter } from '@ui/filter';
import { InputDTO, OptionDTO, InputType } from '@swagger/cat';
import { SelectFilter, SelectFilterOption, FilterType, Filter, FilterOption, RangeFilter } from '@ui/filter';
import { RangeFilterOption } from 'apps/ui/filter/src/lib/models/range-filter-option';
import { isRangeFilterOption, isSelectFilterOption } from 'apps/ui/filter/src/lib/type-guards';
import { cloneDeep } from 'lodash';
export function fromInputDto(input: InputDTO): Filter {
@@ -100,9 +101,17 @@ export function mapSelectedFilterToParams(source: Filter[]): string {
const dict: StringDictionary<string> = {};
for (const filter of source) {
const options: FilterOption[] = filter.options;
const selected = options.filter((o) => o.selected);
// TODO: Start und Stop values bei range-filter mit in die Params
// console.log(selected);
const selected = options.filter((o) => o.type === 'select' && o.selected);
const integerRange = options.filter((o) => o.type === 'integer-range' && o.selected);
if (integerRange.length > 0) {
dict[filter.key] = integerRange
.map((o) => {
if (isRangeFilterOption(o)) {
return o.id + '-' + o.value;
}
})
.join(';');
}
if (selected.length > 0) {
dict[filter.key] = selected.map((o) => o.id).join(';');
}
@@ -117,11 +126,19 @@ export function mapSelectedParamsToFilter(params: string, defaultFilters: Filter
// ist der selectedFilter ein leerstring ist dann sind alle Filter nicht selected
if (params != undefined) {
filters?.forEach((filter) => {
filter?.options?.forEach((option) => {
option.selected = keys.includes(String(option.id));
filter?.options?.forEach((option: FilterOption) => {
if (isSelectFilterOption(option)) {
option.selected = keys.includes(String(option.id));
}
if (isRangeFilterOption(option)) {
const valueAndNumber = keys.find((key) => key?.startsWith(String(option.id)))?.split('-');
if (valueAndNumber) {
option.selected = true;
option.value = valueAndNumber[1];
}
}
});
});
}
return filters;
}

View File

@@ -1 +1,2 @@
export * from './range-filter.component';
export * from './range-filter.validator';

View File

@@ -1,12 +1,12 @@
<div #selectFilter class="isa-card">
<div class="select-filter-options isa-mt-10">
<div class="isa-mt-10">
<p class="range-filter-headline">{{ filter.description }}</p>
<div [formGroup]="formGroup" class="range-input-wrapper">
<input
class="range-filter-input"
uiInput
type="string"
[tabindex]="0"
[tabindex]="1"
[formControlName]="'start'"
[placeholder]="filter.options[0].name"
/>
@@ -19,5 +19,8 @@
[placeholder]="filter.options[1].name"
/>
</div>
<p *ngIf="error" class="range-filter-error">
&bdquo;Von&ldquo; darf maximal so groß sein wie &bdquo;bis&ldquo;
</p>
</div>
</div>

View File

@@ -1,7 +1,3 @@
.select-filter-options {
@apply pr-px-50;
}
.range-filter-headline {
@apply font-semibold text-base;
}
@@ -20,3 +16,8 @@
.range-filter-input::placeholder {
@apply font-bold text-xl text-cool-grey;
}
.range-filter-error {
@apply font-semibold text-base;
color: #f70400;
}

View File

@@ -1,7 +1,9 @@
import { Component, ChangeDetectionStrategy, Input, ElementRef, ChangeDetectorRef, OnInit, Output, EventEmitter } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, ValidationErrors } from '@angular/forms';
import { RangeFilter, RangeFilterOption, SelectFilter } from '../../../models';
import { FormBuilder, FormGroup } from '@angular/forms';
import { RangeFilter, SelectFilter } from '../../../models';
import { isRangeFilter } from '../../../type-guards';
import { FilterGroup } from '../../filter-group';
import { startValidator, stopValidator } from './range-filter.validator';
@Component({
selector: 'ui-range-filter',
@@ -21,82 +23,37 @@ export class UiRangeFilterComponent implements OnInit {
formGroup: FormGroup;
startValidator = (opt: RangeFilterOption) => (control: AbstractControl): null | ValidationErrors => {
const stopCtrl = control?.parent?.get('stop');
if (control.value !== opt.value) {
this.updateValue(control.value, opt, control, { compareControlValue: stopCtrl.value, target: 'stop' });
}
return null;
};
stopValidator = (opt: RangeFilterOption) => (control: AbstractControl): null | ValidationErrors => {
const startCtrl = control?.parent?.get('start');
if (control.value !== opt.value) {
this.updateValue(control.value, opt, control, { compareControlValue: startCtrl.value, target: 'start' });
}
return null;
};
error: boolean;
constructor(protected cdr: ChangeDetectorRef, public filterGroup: FilterGroup, private fb: FormBuilder) {}
ngOnInit() {
this.formGroup = this.fb.group({
start: this.fb.control(this.filter.options[0].value, [this.startValidator(this.filter.options[0])]),
stop: this.fb.control(this.filter.options[1].value, [this.stopValidator(this.filter.options[1])]),
start: this.fb.control(this.filter.options[0].value, [startValidator(this.filter, this.filterGroup, this.preSelectCategory)]),
stop: this.fb.control(this.filter.options[1].value, [stopValidator(this.filter, this.filterGroup, this.preSelectCategory)]),
});
this.updateControl();
this.filterGroup.updateView.subscribe((_) => {
this.updateControl();
this.cdr.markForCheck();
});
this.filterGroup.updateView.subscribe((_) => this.cdr.markForCheck());
}
updateValue(
value: string,
option: RangeFilterOption,
control: AbstractControl,
compareValue: { compareControlValue: string; target: 'start' | 'stop' }
) {
value?.length > 0 ? (option.selected = true) : (option.selected = false);
option.value = this.checkValue(value, option, compareValue);
this.updateControl(option.value, control);
this.filterGroup.toggleOption(this.filter, option);
this.preSelectCategory(this.filter);
this.cdr.markForCheck();
// Correctly Update Input Field Values on open Filter Overlay and on Filter Reset
updateControl() {
const readingAgeFilter = this.filterGroup.value.find((filter) => filter.key === 'reading-age');
if (isRangeFilter(readingAgeFilter)) {
if (this.formGroup.get('start').hasError('valueRange') || this.formGroup.get('stop').hasError('valueRange')) {
this.error = true;
} else {
this.error = false;
}
this.formGroup.get('start').setValue(readingAgeFilter.options[0].value);
this.formGroup.get('stop').setValue(readingAgeFilter.options[1].value);
}
}
updateControl(value: string, control: AbstractControl) {
control.setValue(value, { emitEvent: false });
}
checkValue(value: string, option: RangeFilterOption, compareValue: { compareControlValue: string; target: 'start' | 'stop' }): string {
let validatedResult =
Number(value) <= Number(option.maxValue) && Number(value) >= Number(option.minValue)
? value
.replace(/[^0-9.]/g, '') // Only Numbers
.replace(/^(?:0+(?=[1-9])|0+(?=0$))/gm, '') // No leading Zeros and only one Zero allowed
.replace(/(\..*)\./g, '$1') // No special characters
: Number(value) > Number(option.maxValue)
? option.maxValue
: Number(value) < Number(option.minValue)
? option.minValue
: '';
// Checking if start value is smaller than stop value
// if (compareValue.target === 'stop' && compareValue.compareControlValue !== '') {
// if (Number(compareValue.compareControlValue) < Number(validatedResult)) {
// validatedResult = compareValue.compareControlValue;
// }
// }
// Checking if start value is smaller than stop value and vice versa
// if (compareValue.target === 'start' && compareValue.compareControlValue !== '') {
// if (Number(compareValue.compareControlValue) > Number(validatedResult)) {
// // validatedResult = compareValue.compareControlValue;
// }
// }
return validatedResult;
}
preSelectCategory(category: SelectFilter | RangeFilter) {
this.category.emit(category);
}
private preSelectCategory = () => {
this.category.emit(this.filter);
};
}

View File

@@ -0,0 +1,86 @@
import { AbstractControl, ValidationErrors } from '@angular/forms';
import { RangeFilter, RangeFilterOption } from '../../../models';
import { FilterGroup } from '../../filter-group';
export function startValidator(filter: RangeFilter, filterGroup: FilterGroup, preSelectCategory: () => void) {
return (control: AbstractControl): null | ValidationErrors => {
const startOption: RangeFilterOption = filter.options[0];
if (control.value !== startOption.value) {
preSelectCategory();
validateAndSetInput(control, startOption);
const compare = compareWithStopValue(startOption.value, control);
if (compare.error) {
startOption.value = compare.value;
filterGroup.setRangeOption(filter, startOption);
return { valueRange: 'error' };
} else {
filterGroup.setRangeOption(filter, startOption);
}
}
return null;
};
}
export function stopValidator(filter: RangeFilter, filterGroup: FilterGroup, preSelectCategory: () => void) {
return (control: AbstractControl): null | ValidationErrors => {
const stopOption: RangeFilterOption = filter.options[1];
if (control.value !== stopOption.value) {
preSelectCategory();
validateAndSetInput(control, stopOption);
filterGroup.setRangeOption(filter, stopOption);
const compare = compareWithStartValue(stopOption.value, control);
if (compare.error) {
control?.parent?.get('start')?.setValue('');
return { valueRange: 'error' };
}
}
return null;
};
}
function validateAndSetInput(control: AbstractControl, option: RangeFilterOption) {
control?.value?.length > 0 ? (option.selected = true) : (option.selected = false);
option.value = checkValue(control, option);
}
function checkValue(control: AbstractControl, option: RangeFilterOption): string {
let validatedResult = control.value;
validatedResult = checkFormat(validatedResult);
validatedResult = checkBoundaries(validatedResult, option);
return validatedResult;
}
function checkFormat(value: string): string {
return value
.replace(/[^0-9.]/g, '') // Only Numbers
.replace(/^(?:0+(?=[1-9])|0+(?=0$))/gm, '') // No leading Zeros and only one Zero allowed
.replace(/(\..*)\./g, '$1'); // No special characters
}
function checkBoundaries(value: string, option: RangeFilterOption): string {
if (Number(value) <= Number(option.maxValue) && Number(value) >= Number(option.minValue)) {
return value;
} else {
return Number(value) > Number(option.maxValue) ? option.maxValue : Number(value) < Number(option.minValue) ? option.minValue : '';
}
}
function compareWithStartValue(value: string, control: AbstractControl): { error: boolean } {
const result = { error: false };
const startCtrl = control?.parent?.get('start');
if (Number(startCtrl.value) > Number(value) && startCtrl.value.length > 0 && value !== '') {
result.error = true;
}
return result;
}
function compareWithStopValue(value: string, control: AbstractControl): { error: boolean; value: string } {
const result = { error: false, value };
const stopCtrl = control?.parent?.get('stop');
if (Number(value) > Number(stopCtrl.value) && stopCtrl.value.length > 0) {
result.value = stopCtrl.value;
result.error = true;
}
return result;
}

View File

@@ -1,5 +1,5 @@
import { EventEmitter } from '@angular/core';
import { Filter, FilterOption } from '../models';
import { Filter, FilterOption, RangeFilter, RangeFilterOption, SelectFilter, SelectFilterOption } from '../models';
import { isRangeFilterOption, isSelectFilterOption } from '../type-guards';
export abstract class FilterGroup {
@@ -25,21 +25,20 @@ export abstract class FilterGroup {
this.updateView.emit();
}
toggleOption(filter: Filter, option: FilterOption) {
toggleOption(filter: SelectFilter, option: SelectFilterOption) {
const optionRef = this.getOptionRef(filter, option);
if (isSelectFilterOption(optionRef)) {
optionRef.selected = !optionRef.selected;
this.valueChange.emit(this.value);
this.updateView.emit();
}
}
setRangeOption(filter: RangeFilter, option: RangeFilterOption) {
const optionRef = this.getOptionRef(filter, option);
if (isRangeFilterOption(optionRef)) {
console.log('BEFORE: ', optionRef, option);
optionRef.selected = option.selected;
if (isRangeFilterOption(option)) {
optionRef.value = option.value;
}
console.log(optionRef, option);
optionRef.value = option.value;
this.valueChange.emit(this.value);
this.updateView.emit();
}

View File

@@ -1,6 +1,6 @@
import { Component, ChangeDetectionStrategy, Input, EventEmitter, Output, ChangeDetectorRef } from '@angular/core';
import { Filter, FilterOption } from '../../models';
import { isSelectFilter } from '../../type-guards';
import { Filter, FilterOption, RangeFilterOption, SelectFilterOption } from '../../models';
import { isRangeFilter, isRangeFilterOption, isSelectFilter } from '../../type-guards';
import { isSelectFilterOption } from '../../type-guards/select-filter-option.type-guard';
import { cloneFilter } from '../../utils';
@@ -25,6 +25,10 @@ export class UiSelectedFilterOptionsComponent {
const selected = filter.options.filter((f) => f.selected);
return [...aggr, ...selected];
}
if (isRangeFilter(filter)) {
const range = filter.options.filter((f) => f.selected);
return [...aggr, ...range];
}
return [...aggr];
}, []);
@@ -64,6 +68,14 @@ export class UiSelectedFilterOptionsComponent {
});
}
}
if (isRangeFilter(filter)) {
if (isRangeFilter(val)) {
val.options.forEach((option) => {
result = result || filter.options.find((o) => o.id === option.id).selected !== option.selected;
});
}
}
});
return result;
@@ -80,6 +92,12 @@ export class UiSelectedFilterOptionsComponent {
unselectOptions(o.options);
}
}
if (isRangeFilterOption(o)) {
if (o.id === option.id) {
o.selected = false;
}
}
});
}