Skip to content

Commit 90b7f74

Browse files
brunoromeiroalinelariguet
authored andcommitted
feat(multiselect): permite usar o p-options com any
Permite usar o `p-options` com `any` com `p-field-value` e `p-field-label` Fixes DTHFUI-6128
1 parent deeda5a commit 90b7f74

13 files changed

+228
-84
lines changed

projects/ui/src/lib/components/po-field/po-multiselect/po-multiselect-base.component.spec.ts

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import { fakeAsync, tick } from '@angular/core/testing';
55
import { Observable, of } from 'rxjs';
66

77
import { expectPropertiesValues, expectSettersMethod } from '../../../util-test/util-expect.spec';
8-
import { removeDuplicatedOptions, removeUndefinedAndNullOptions, sortOptionsByProperty } from '../../../utils/util';
8+
import {
9+
removeDuplicatedOptionsWithFieldValue,
10+
removeUndefinedAndNullOptionsWithFieldValue,
11+
sortOptionsByProperty
12+
} from '../../../utils/util';
913
import * as UtilsFunctions from '../../../utils/util';
1014

1115
import { PoLanguageService } from '../../../services/po-language/po-language.service';
@@ -125,10 +129,8 @@ describe('PoMultiselectBaseComponent:', () => {
125129
});
126130

127131
it('should set options', () => {
128-
spyOn(component, 'validAndSortOptions');
129132
component.options = [{ label: '1', value: '1' }];
130133
expect(component.options.length).toBe(1);
131-
expect(component.validAndSortOptions).toHaveBeenCalled();
132134
});
133135

134136
it('should set p-sort', () => {
@@ -205,13 +207,13 @@ describe('PoMultiselectBaseComponent:', () => {
205207
component.options = [{ label: '1', value: 1 }];
206208
component.sort = true;
207209

208-
spyOn(UtilsFunctions, 'removeUndefinedAndNullOptions');
209-
spyOn(UtilsFunctions, 'removeDuplicatedOptions');
210+
spyOn(UtilsFunctions, 'removeUndefinedAndNullOptionsWithFieldValue');
211+
spyOn(UtilsFunctions, 'removeDuplicatedOptionsWithFieldValue');
210212
spyOn(component, 'setUndefinedLabels');
211213
spyOn(UtilsFunctions, 'sortOptionsByProperty');
212214
component.validAndSortOptions();
213-
expect(removeUndefinedAndNullOptions).toHaveBeenCalled();
214-
expect(removeDuplicatedOptions).toHaveBeenCalled();
215+
expect(removeUndefinedAndNullOptionsWithFieldValue).toHaveBeenCalled();
216+
expect(removeDuplicatedOptionsWithFieldValue).toHaveBeenCalled();
215217
expect(component.setUndefinedLabels).toHaveBeenCalled();
216218
expect(sortOptionsByProperty).toHaveBeenCalled();
217219
});
@@ -220,13 +222,13 @@ describe('PoMultiselectBaseComponent:', () => {
220222
component.options = [{ label: '1', value: 1 }];
221223
component.sort = false;
222224

223-
spyOn(UtilsFunctions, 'removeUndefinedAndNullOptions');
224-
spyOn(UtilsFunctions, 'removeDuplicatedOptions');
225+
spyOn(UtilsFunctions, 'removeUndefinedAndNullOptionsWithFieldValue');
226+
spyOn(UtilsFunctions, 'removeDuplicatedOptionsWithFieldValue');
225227
spyOn(component, 'setUndefinedLabels');
226228
spyOn(UtilsFunctions, 'sortOptionsByProperty');
227229
component.validAndSortOptions();
228-
expect(removeUndefinedAndNullOptions).toHaveBeenCalled();
229-
expect(removeDuplicatedOptions).toHaveBeenCalled();
230+
expect(removeUndefinedAndNullOptionsWithFieldValue).toHaveBeenCalled();
231+
expect(removeDuplicatedOptionsWithFieldValue).toHaveBeenCalled();
230232
expect(component.setUndefinedLabels).toHaveBeenCalled();
231233
expect(sortOptionsByProperty).not.toHaveBeenCalled();
232234
});
@@ -235,13 +237,13 @@ describe('PoMultiselectBaseComponent:', () => {
235237
component.options = [];
236238
component.sort = false;
237239

238-
spyOn(UtilsFunctions, 'removeUndefinedAndNullOptions');
239-
spyOn(UtilsFunctions, 'removeDuplicatedOptions');
240+
spyOn(UtilsFunctions, 'removeUndefinedAndNullOptionsWithFieldValue');
241+
spyOn(UtilsFunctions, 'removeDuplicatedOptionsWithFieldValue');
240242
spyOn(component, 'setUndefinedLabels');
241243
spyOn(UtilsFunctions, 'sortOptionsByProperty');
242244
component.validAndSortOptions();
243-
expect(removeUndefinedAndNullOptions).not.toHaveBeenCalled();
244-
expect(removeDuplicatedOptions).not.toHaveBeenCalled();
245+
expect(removeUndefinedAndNullOptionsWithFieldValue).not.toHaveBeenCalled();
246+
expect(removeDuplicatedOptionsWithFieldValue).not.toHaveBeenCalled();
245247
expect(component.setUndefinedLabels).not.toHaveBeenCalled();
246248
expect(sortOptionsByProperty).not.toHaveBeenCalled();
247249
});

projects/ui/src/lib/components/po-field/po-multiselect/po-multiselect-base.component.ts

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import { debounceTime, distinctUntilChanged, switchMap, tap } from 'rxjs/operato
77
import {
88
convertToBoolean,
99
isTypeof,
10-
removeDuplicatedOptions,
11-
removeUndefinedAndNullOptions,
10+
removeDuplicatedOptionsWithFieldValue,
11+
removeUndefinedAndNullOptionsWithFieldValue,
1212
sortOptionsByProperty
1313
} from '../../../utils/util';
1414
import { requiredFailed } from './../validators';
@@ -136,8 +136,8 @@ export abstract class PoMultiselectBaseComponent implements ControlValueAccessor
136136
*/
137137
@Output('p-change') change: EventEmitter<any> = new EventEmitter<any>();
138138

139-
selectedOptions: Array<PoMultiselectOption> = [];
140-
visibleOptionsDropdown: Array<PoMultiselectOption> = [];
139+
selectedOptions: Array<PoMultiselectOption | any> = [];
140+
visibleOptionsDropdown: Array<PoMultiselectOption | any> = [];
141141
visibleDisclaimers = [];
142142
isServerSearching = false;
143143
isFirstFilter: boolean = true;
@@ -158,7 +158,7 @@ export abstract class PoMultiselectBaseComponent implements ControlValueAccessor
158158
private _filterMode?: PoMultiselectFilterMode = PoMultiselectFilterMode.startsWith;
159159
private _hideSearch?: boolean = false;
160160
private _literals: PoMultiselectLiterals;
161-
private _options: Array<PoMultiselectOption>;
161+
private _options: Array<PoMultiselectOption | any>;
162162
private _required?: boolean = false;
163163
private _sort?: boolean = false;
164164
private _autoHeight: boolean = false;
@@ -359,7 +359,7 @@ export abstract class PoMultiselectBaseComponent implements ControlValueAccessor
359359
/**
360360
* @description
361361
*
362-
* Nesta propriedade deve ser definida uma lista de objetos que implementam a interface PoMultiselectOption.
362+
* Nesta propriedade deve ser definida uma lista de objetos que será exibida no multiselect.
363363
* Esta lista deve conter os valores e os labels que serão apresentados na tela.
364364
*
365365
* > Essa propriedade é imutável, ou seja, sempre que quiser atualizar a lista de opções disponíveis
@@ -372,14 +372,23 @@ export abstract class PoMultiselectBaseComponent implements ControlValueAccessor
372372
* // evite, pois não atualiza a referência do objeto podendo gerar atrasos na atualização do template
373373
* this.options.push({ value: 'x', label: 'Nova opção' });
374374
* ```
375+
* > A lista pode ser definida utilizando um array com o valor representando `value` e `label` das seguintes formas:
376+
*
377+
* ```
378+
* <po-multiselect name="multiselect" p-label="PO Multiselect" [p-options]="[{value: 1, label: 'One'}, {value: 2, label: 'two'}]"> </po-multiselect>
379+
* ```
380+
*
381+
* ```
382+
* <po-multiselect name="multiselect" p-label="PO Multiselect" [p-options]="[{name: 'Roger', age: 28}, {name: 'Anne', age: 35}]" p-field-label="name" p-field-value="age"> </po-multiselect>
383+
* ```
384+
*
385+
* - Aconselha-se utilizar valores distintos no `label` e `value` dos itens.
375386
*/
376-
@Input('p-options') set options(options: Array<PoMultiselectOption>) {
387+
@Input('p-options') set options(options: Array<PoMultiselectOption | any>) {
377388
this._options = options;
378-
379-
this.validAndSortOptions();
380389
}
381390

382-
get options() {
391+
get options(): Array<PoMultiselectOption | any> {
383392
return this._options;
384393
}
385394

@@ -445,7 +454,7 @@ export abstract class PoMultiselectBaseComponent implements ControlValueAccessor
445454
* @default `label`
446455
*/
447456
@Input('p-field-label') set fieldLabel(value: string) {
448-
this._fieldLabel = value || PO_MULTISELECT_FIELD_LABEL_DEFAULT;
457+
this._fieldLabel = value ? value : PO_MULTISELECT_FIELD_LABEL_DEFAULT;
449458

450459
if (isTypeof(this.filterService, 'string') && this.service) {
451460
this.service.fieldLabel = this._fieldLabel;
@@ -469,7 +478,7 @@ export abstract class PoMultiselectBaseComponent implements ControlValueAccessor
469478
* @default `value`
470479
*/
471480
@Input('p-field-value') set fieldValue(value: string) {
472-
this._fieldValue = value || PO_MULTISELECT_FIELD_VALUE_DEFAULT;
481+
this._fieldValue = value ? value : PO_MULTISELECT_FIELD_VALUE_DEFAULT;
473482

474483
if (isTypeof(this.filterService, 'string') && this.service) {
475484
this.service.fieldValue = this._fieldValue;
@@ -499,6 +508,7 @@ export abstract class PoMultiselectBaseComponent implements ControlValueAccessor
499508
)
500509
.subscribe();
501510

511+
this.validAndSortOptions();
502512
this.updateList(this.options);
503513
}
504514

@@ -515,31 +525,31 @@ export abstract class PoMultiselectBaseComponent implements ControlValueAccessor
515525

516526
validAndSortOptions() {
517527
if (this.options && this.options.length) {
518-
removeUndefinedAndNullOptions(this.options);
519-
removeDuplicatedOptions(this.options);
528+
removeUndefinedAndNullOptionsWithFieldValue(this.options, this.fieldValue);
529+
removeDuplicatedOptionsWithFieldValue(this.options, this.fieldValue);
520530
this.setUndefinedLabels(this.options);
521531

522532
if (this.sort) {
523-
sortOptionsByProperty(this.options, 'label');
533+
sortOptionsByProperty(this.options, this.fieldLabel);
524534
}
525535
}
526536
}
527537

528538
setUndefinedLabels(options) {
529539
options.forEach(option => {
530-
if (!option['label']) {
531-
option.label = option.value;
540+
if (!option[this.fieldLabel]) {
541+
option[this.fieldLabel] = option[this.fieldValue];
532542
}
533543
});
534544
}
535545

536-
updateList(options: Array<PoMultiselectOption>) {
546+
updateList(options: Array<PoMultiselectOption | any>) {
537547
if (options) {
538548
this.visibleOptionsDropdown = options;
539549
}
540550
}
541551

542-
callOnChange(selectedOptions: Array<PoMultiselectOption>) {
552+
callOnChange(selectedOptions: Array<PoMultiselectOption | any>) {
543553
if (this.onModelChange) {
544554
this.onModelChange(this.getValuesFromOptions(selectedOptions));
545555
this.eventChange(selectedOptions);
@@ -553,20 +563,20 @@ export abstract class PoMultiselectBaseComponent implements ControlValueAccessor
553563
this.lastLengthModel = selectedOptions ? selectedOptions.length : null;
554564
}
555565

556-
getValuesFromOptions(selectedOptions: Array<PoMultiselectOption>) {
557-
return selectedOptions && selectedOptions.length ? selectedOptions.map(option => option.value) : [];
566+
getValuesFromOptions(selectedOptions: Array<PoMultiselectOption | any>) {
567+
return selectedOptions && selectedOptions.length ? selectedOptions.map(option => option[this.fieldValue]) : [];
558568
}
559569

560570
getLabelByValue(value) {
561-
const index = this.options.findIndex(option => option.value === value);
571+
const index = this.options.findIndex(option => option[this.fieldValue] === value);
562572
return this.options[index].label;
563573
}
564574

565-
searchByLabel(search: string, options: Array<PoMultiselectOption>, filterMode: PoMultiselectFilterMode) {
575+
searchByLabel(search: string, options: Array<PoMultiselectOption | any>, filterMode: PoMultiselectFilterMode) {
566576
if (search && options && options.length) {
567-
const newOptions: Array<PoMultiselectOption> = [];
577+
const newOptions: Array<PoMultiselectOption | any> = [];
568578
options.forEach(option => {
569-
if (option.label && this.compareMethod(search, option, filterMode)) {
579+
if (option[this.fieldLabel] && this.compareMethod(search, option, filterMode)) {
570580
newOptions.push(option);
571581
}
572582
});
@@ -588,15 +598,15 @@ export abstract class PoMultiselectBaseComponent implements ControlValueAccessor
588598
}
589599

590600
startsWith(search: string, option: PoMultiselectOption) {
591-
return option.label.toLowerCase().startsWith(search.toLowerCase());
601+
return option[this.fieldLabel].toLowerCase().startsWith(search.toLowerCase());
592602
}
593603

594604
contains(search: string, option: PoMultiselectOption) {
595-
return option.label.toLowerCase().indexOf(search.toLowerCase()) > -1;
605+
return option[this.fieldLabel].toLowerCase().indexOf(search.toLowerCase()) > -1;
596606
}
597607

598608
endsWith(search: string, option: PoMultiselectOption) {
599-
return option.label.toLowerCase().endsWith(search.toLowerCase());
609+
return option[this.fieldLabel].toLowerCase().endsWith(search.toLowerCase());
600610
}
601611

602612
validate(c: AbstractControl): { [key: string]: any } {
@@ -623,7 +633,7 @@ export abstract class PoMultiselectBaseComponent implements ControlValueAccessor
623633
} else {
624634
newOptions.forEach(newOption => {
625635
options.forEach(option => {
626-
if (option.value === newOption.value) {
636+
if (option[this.fieldValue] === newOption[this.fieldValue]) {
627637
this.selectedOptions.push(option);
628638
}
629639
});
@@ -643,7 +653,7 @@ export abstract class PoMultiselectBaseComponent implements ControlValueAccessor
643653
});
644654
} else {
645655
// Validar se todos os items existem entre os options, senão atualizar o model
646-
this.updateSelectedOptions(values.map(value => ({ value })));
656+
this.updateSelectedOptions(values.map(value => ({ [this.fieldValue]: value })));
647657

648658
if (this.selectedOptions && this.selectedOptions.length < values.length) {
649659
this.callOnChange(this.selectedOptions);
@@ -675,6 +685,6 @@ export abstract class PoMultiselectBaseComponent implements ControlValueAccessor
675685
}
676686
}
677687

678-
abstract applyFilter(value?: string): Observable<Array<PoMultiselectOption>>;
688+
abstract applyFilter(value?: string): Observable<Array<PoMultiselectOption | any>>;
679689
abstract updateVisibleItems(): void;
680690
}

projects/ui/src/lib/components/po-field/po-multiselect/po-multiselect-dropdown/po-multiselect-dropdown.component.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#searchElement
44
*ngIf="!hideSearch"
55
[p-literals]="literals"
6+
[p-field-value]="fieldValue"
67
[p-placeholder]="placeholderSearch"
78
(p-change)="callChangeSearch($event)"
89
>
@@ -28,7 +29,7 @@
2829

2930
<po-multiselect-item
3031
*ngFor="let option of visibleOptions"
31-
[p-label]="option.label"
32+
[p-label]="option[fieldLabel]"
3233
[p-selected]="isSelectedItem(option)"
3334
(p-change)="clickItem($event, option)"
3435
>

projects/ui/src/lib/components/po-field/po-multiselect/po-multiselect-dropdown/po-multiselect-dropdown.component.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ describe('PoMultiselectDropdownComponent:', () => {
2828
{ label: 'label1', value: 'value1' },
2929
{ label: 'label2', value: 'value2' }
3030
];
31+
32+
component.fieldLabel = 'label';
33+
component.fieldValue = 'value';
3134
});
3235

3336
it('should be created', () => {

projects/ui/src/lib/components/po-field/po-multiselect/po-multiselect-dropdown/po-multiselect-dropdown.component.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,18 @@ export class PoMultiselectDropdownComponent {
4343
@Input('p-selected-options') selectedOptions: Array<any> = [];
4444

4545
/** Propriedade que recebe a lista com todas as opções. */
46-
@Input('p-options') options: Array<PoMultiselectOption> = [];
46+
@Input('p-options') options: Array<PoMultiselectOption | any> = [];
4747

4848
/** Propriedade que recebe a lista de opções que deverão ser criadas no dropdown. */
49-
@Input('p-visible-options') visibleOptions: Array<PoMultiselectOption> = [];
49+
@Input('p-visible-options') visibleOptions: Array<PoMultiselectOption | any> = [];
5050

5151
/** Propriedade que indica se o campo "Selecionar todos" deverá ser escondido. */
5252
@Input('p-hide-select-all') hideSelectAll?: boolean = false;
5353

54+
@Input('p-field-value') fieldValue: string;
55+
56+
@Input('p-field-label') fieldLabel: string;
57+
5458
/** Evento disparado a cada tecla digitada na pesquisa. */
5559
@Output('p-change-search') changeSearch = new EventEmitter();
5660

@@ -90,7 +94,7 @@ export class PoMultiselectDropdownComponent {
9094
}
9195

9296
isSelectedItem(option: PoMultiselectOption) {
93-
return this.selectedOptions.some(selectedItem => selectedItem.value === option.value);
97+
return this.selectedOptions.some(selectedItem => selectedItem[this.fieldValue] === option[this.fieldValue]);
9498
}
9599

96100
clickItem(check, option) {
@@ -102,7 +106,7 @@ export class PoMultiselectDropdownComponent {
102106
}
103107

104108
onClickSelectAll() {
105-
const selectedValues = this.selectedOptions.map(({ value }) => value);
109+
const selectedValues = this.selectedOptions.map(({ [this.fieldValue]: value }) => value);
106110

107111
if (this.everyVisibleOptionsSelected(selectedValues)) {
108112
this.selectedOptions = [];
@@ -116,21 +120,23 @@ export class PoMultiselectDropdownComponent {
116120
if (checked) {
117121
this.selectedOptions.push(option);
118122
} else {
119-
this.selectedOptions = this.selectedOptions.filter(selectedOption => selectedOption.value !== option.value);
123+
this.selectedOptions = this.selectedOptions.filter(
124+
selectedOption => selectedOption[this.fieldValue] !== option[this.fieldValue]
125+
);
120126
}
121127
this.change.emit(this.selectedOptions);
122128
}
123129

124130
everyVisibleOptionsSelected(selectedValues) {
125-
return this.visibleOptions.every(visibleOption => selectedValues.includes(visibleOption.value));
131+
return this.visibleOptions.every(visibleOption => selectedValues.includes(visibleOption[this.fieldValue]));
126132
}
127133

128134
someVisibleOptionsSelected(selectedValues) {
129-
return this.visibleOptions.some(visibleOption => selectedValues.includes(visibleOption.value));
135+
return this.visibleOptions.some(visibleOption => selectedValues.includes(visibleOption[this.fieldValue]));
130136
}
131137

132138
getStateSelectAll() {
133-
const selectedValues = this.selectedOptions.map(({ value }) => value);
139+
const selectedValues = this.selectedOptions.map(({ [this.fieldValue]: value }) => value);
134140

135141
if (this.everyVisibleOptionsSelected(selectedValues)) {
136142
return true;
@@ -161,7 +167,7 @@ export class PoMultiselectDropdownComponent {
161167
const newSelectedOptions = [...this.selectedOptions];
162168

163169
for (const visibleOption of this.visibleOptions) {
164-
if (!selectedValues.includes(visibleOption.value)) {
170+
if (!selectedValues.includes(visibleOption[this.fieldValue])) {
165171
newSelectedOptions.push(visibleOption);
166172
}
167173
}

0 commit comments

Comments
 (0)