• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

atinc / ngx-tethys / f809b15f-539a-4d47-ba33-3b3f2e5d90a3

05 Dec 2023 03:14AM UTC coverage: 90.442% (+0.09%) from 90.349%
f809b15f-539a-4d47-ba33-3b3f2e5d90a3

Pull #2894

circleci

smile1016
feat(cascader): fix error
Pull Request #2894: feat(cascader): add service for cascader #INFR-10254

5340 of 6565 branches covered (0.0%)

Branch coverage included in aggregate %.

300 of 314 new or added lines in 2 files covered. (95.54%)

17 existing lines in 1 file now uncovered.

13319 of 14066 relevant lines covered (94.69%)

974.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.31
/src/cascader/cascader.service.ts
1
import { SelectionModel } from '@angular/cdk/collections';
2
import { Injectable, OnDestroy } from '@angular/core';
3
import { SelectOptionBase } from 'ngx-tethys/shared';
4
import { ThyCascaderOption, ThyCascaderSearchOption } from './types';
5
import { helpers, isArray, isEmpty, set } from 'ngx-tethys/util';
6
import { Id } from '@tethys/cdk/immutable';
7
import { Subject } from 'rxjs';
45✔
8
import { debounceTime, finalize, map } from 'rxjs/operators';
1✔
9
const defaultDisplayRender = (label: any) => label.join(' / ');
10

49✔
11
@Injectable()
49✔
12
export class ThyCascaderService implements OnDestroy {
49✔
13
    public selectionModel: SelectionModel<SelectOptionBase>;
49✔
14

49✔
15
    public columns: ThyCascaderOption[][] = [];
49✔
16

49✔
17
    public cascaderOptions: {
49✔
18
        labelProperty?: string;
49✔
19
        valueProperty?: string;
20
        isMultiple?: boolean;
21
        isOnlySelectLeaf?: boolean;
49✔
22
        isLabelRenderTemplate?: boolean;
11✔
23
        loadData?: (node: ThyCascaderOption, index?: number) => PromiseLike<any>;
24
    };
25

26
    public selectedOptions: ThyCascaderOption[] = [];
27

NEW
28
    public activatedOptions: ThyCascaderOption[] = [];
×
NEW
29

×
30
    public labelRenderText: string;
31

32
    public flattenOptions: ThyCascaderSearchOption[] = [];
33

165✔
34
    public leafNodes: ThyCascaderSearchOption[] = [];
165✔
35

36
    public isLoading = false;
37

165✔
38
    public searchResultList: ThyCascaderSearchOption[] = [];
116✔
39

40
    public defaultValue: any[];
41

49✔
42
    public value: any[];
43

44
    private prevSelectedOptions: Set<ThyCascaderOption> = new Set<ThyCascaderOption>();
45

56✔
46
    public valueChange$ = new Subject();
47

48
    constructor() {}
39✔
49

26✔
50
    public cascaderValueChange() {
51
        return this.valueChange$.pipe(
13✔
52
            map(() => {
13✔
53
                return {
31✔
54
                    value: this.getValues(),
19✔
55
                    isValueEqual: this.arrayEquals(this.value, this.getValues()),
56
                    isSelectionModelEmpty: this.selectionModel.isEmpty()
57
                };
58
            }),
59
            finalize(() => {
115✔
60
                this.defaultValue = null;
115✔
61
                this.value = this.getValues();
114✔
62
            }),
114✔
63
            debounceTime(100)
69✔
64
        );
65
    }
114✔
66

41✔
67
    public setCascaderOptions(options: {
68
        labelProperty?: string;
69
        valueProperty?: string;
115✔
70
        isMultiple?: boolean;
113✔
71
        isOnlySelectLeaf?: boolean;
72
        isLabelRenderTemplate?: boolean;
73
        loadData?: (node: ThyCascaderOption, index?: number) => PromiseLike<any>;
2✔
74
    }) {
2✔
75
        this.cascaderOptions = { ...this.cascaderOptions, ...options };
76
        this.initSelectionModel(this.cascaderOptions.isMultiple);
77
    }
78

20!
NEW
79
    public initSelectionModel(isMultiple?: boolean) {
×
80
        if (this.selectionModel) {
81
            this.selectionModel.clear();
20✔
82
        } else {
20✔
83
            this.selectionModel = new SelectionModel(isMultiple);
1✔
84
        }
85
    }
86

19✔
87
    public initColumns(columns: ThyCascaderOption[][]) {
88
        this.columns = columns;
89
    }
×
90

140✔
91
    public initActivatedOptions(menuVisible: boolean) {
140✔
92
        if (isEmpty(this.selectedOptions) || !menuVisible) {
123✔
93
            return;
123✔
94
        }
95
        this.activatedOptions = [...this.selectedOptions];
3✔
96
        this.activatedOptions.forEach((item, index) => {
97
            if (!isEmpty(item.children) && !item.isLeaf) {
98
                this.columns[index + 1] = item.children;
140✔
99
            }
20✔
100
        });
101
    }
140✔
102

85✔
103
    public initOptions(index: number) {
94✔
104
        const vs = this.defaultValue;
85✔
105
        const load = () => {
106
            this.activateOnInit(index, vs[index]);
55✔
107
            if (index < vs.length - 1) {
3✔
108
                this.initOptions(index + 1);
109
            }
110
            if (index === vs.length - 1) {
52!
NEW
111
                this.afterWriteValue();
×
112
            }
113
        };
114

140✔
115
        if (this.isLoaded(index) || !this.cascaderOptions.loadData) {
9✔
116
            load();
117
        } else {
118
            const node = this.activatedOptions[index - 1] || {};
119
            this.loadChildren(node, index - 1, load, this.afterWriteValue.bind(this));
5✔
120
        }
4✔
121
    }
4✔
122

3✔
123
    public clickOption(
3!
124
        option: ThyCascaderOption,
4✔
125
        index: number,
3✔
126
        event: Event | boolean,
127
        selectFn?: (option: ThyCascaderOption, index: number) => void
3✔
128
    ) {
1✔
129
        if (option && option.disabled && !this.cascaderOptions.isMultiple) {
130
            return;
131
        }
1✔
132

1✔
133
        const isSelect = event instanceof Event ? !this.cascaderOptions.isMultiple && option.isLeaf : true;
1!
134

1✔
135
        if (this.cascaderOptions.isMultiple && !option.isLeaf && this.cascaderOptions.isOnlySelectLeaf && isSelect) {
136
            this.toggleAllChildren(option, index, event as boolean, selectFn);
137
        } else {
138
            this.setActiveOption(option, index, isSelect, true, selectFn);
139
        }
1!
140
    }
141

142
    public setActiveOption(
143
        option: ThyCascaderOption,
114✔
144
        index: number,
114✔
145
        select: boolean,
10✔
146
        loadChildren: boolean = true,
10!
147
        selectFn?: (option: ThyCascaderOption, index: number) => void
148
    ): void {
149
        this.activatedOptions[index] = option;
10!
150
        for (let i = index - 1; i >= 0; i--) {
10!
151
            const originOption = this.activatedOptions[i + 1]?.parent;
152
            if (
153
                !this.activatedOptions[i] ||
114✔
154
                originOption?.[this.cascaderOptions.valueProperty] !== this.activatedOptions[i]?.[this.cascaderOptions.valueProperty]
114✔
155
            ) {
156
                this.activatedOptions[i] = originOption ?? this.activatedOptions[i];
157
            }
1,457✔
158
        }
1,402✔
159
        if (index < this.activatedOptions.length - 1) {
306✔
160
            this.activatedOptions = this.activatedOptions.slice(0, index + 1);
161
        }
162
        if (isArray(option.children) && option.children.length) {
1,096✔
163
            option.isLeaf = false;
164
            option.children.forEach(child => (child.parent = option));
165
            this.setColumnData(option.children, index + 1);
166
        } else if (!option.isLeaf && loadChildren) {
55✔
167
            this.loadChildren(option, index);
55✔
168
        } else {
5✔
169
            if (index < this.columns.length - 1) {
1✔
170
                this.columns = this.columns.slice(0, index + 1);
1✔
171
            }
172
        }
173

4✔
174
        if (select) {
175
            selectFn(option, index);
176
        }
55✔
177
    }
178

179
    private loadChildren(option: ThyCascaderOption, index: number, success?: () => void, failure?: () => void): void {
180
        if (this.cascaderOptions?.loadData) {
1✔
181
            this.isLoading = true;
1✔
182
            this.cascaderOptions?.loadData(option, index).then(
1✔
183
                () => {
3✔
184
                    option.loading = this.isLoading = false;
3✔
185
                    if (option.children) {
3✔
186
                        option.children.forEach(child => (child.parent = index < 0 ? undefined : option));
187
                        this.setColumnData(option.children, index + 1);
1✔
188
                    }
3✔
189
                    if (success) {
3✔
190
                        success();
2✔
191
                    }
192
                },
193
                () => {
194
                    option.loading = this.isLoading = false;
195
                    option.isLeaf = true;
1✔
196
                    if (failure) {
1!
197
                        failure();
1✔
198
                    }
4✔
199
                }
4✔
200
            );
3✔
201
        } else {
202
            this.setColumnData(option.children || [], index + 1);
203
        }
204
    }
205

1!
NEW
206
    private activateOnInit(index: number, value: any): void {
×
207
        let option = this.findOption(value, index);
208
        if (!option) {
209
            option =
210
                typeof value === 'object'
1✔
211
                    ? value
212
                    : {
213
                          [`${this.cascaderOptions.valueProperty || 'value'}`]: value,
6✔
214
                          [`${this.cascaderOptions.labelProperty || 'label'}`]: value
6✔
215
                      };
216
        }
18✔
217
        this.updatePrevSelectedOptions(option, true);
38✔
218
        this.setActiveOption(option, index, false, false);
57✔
219
    }
57✔
220

57✔
221
    public isSelectedOption(option: ThyCascaderOption, index: number): boolean {
57✔
222
        if (this.cascaderOptions?.isOnlySelectLeaf) {
57✔
223
            if (option.isLeaf) {
57✔
224
                return option.selected;
57✔
225
            } else {
226
                return this.checkSelectedStatus(option, true);
227
            }
228
        } else {
229
            const selectedOpts = this.selectionModel.selected;
230
            const appearIndex = selectedOpts.findIndex(item => {
231
                if (item.thyRawValue.value.length - 1 === index) {
232
                    const selectedItem = helpers.get(item, `thyRawValue.value.${index}`);
57✔
233
                    return helpers.shallowEqual(selectedItem, option);
57✔
234
                } else {
32✔
235
                    return false;
236
                }
237
            });
25✔
238
            return appearIndex >= 0;
239
        }
240
    }
241

242
    private toggleAllChildren(
6✔
243
        option: ThyCascaderOption,
6✔
244
        index: number,
29✔
245
        selected: boolean,
4✔
246
        selectFn?: (option: ThyCascaderOption, index: number) => void
247
    ): void {
248
        const allLeafs: {
249
            option: ThyCascaderOption;
250
            index: number;
251
        }[] = this.getAllLeafs(option, index, selected);
252
        option.selected = selected;
253

254
        while (allLeafs.length) {
255
            const { option, index } = allLeafs.shift();
256
            option.selected = !selected;
3,282!
NEW
257
            this.setActiveOption(option, index, true, null, selectFn);
×
258
        }
259

3,282✔
260
        for (let i = 0; i < this.activatedOptions.length; i++) {
3,577✔
261
            const option = this.activatedOptions[i];
631✔
262
            if (isArray(option.children) && option.children.length) {
263
                this.setColumnData(option.children, i + 1);
2,946✔
264
            }
860✔
265
        }
266
    }
267

1,791✔
268
    private getAllLeafs(
269
        option: ThyCascaderOption,
270
        index: number,
115✔
271
        selected: boolean
272
    ): {
273
        option: ThyCascaderOption;
114✔
274
        index: number;
114✔
275
    }[] {
111!
276
        let allLeafs: {
149✔
277
            option: ThyCascaderOption;
278
            index: number;
3✔
279
        }[] = [];
280
        if (option.children.length > 0) {
281
            for (const childOption of option.children) {
91✔
282
                childOption.parent = option;
87✔
283
                if (childOption.isLeaf && !childOption.selected === selected) {
87✔
284
                    allLeafs.push({
19✔
285
                        option: childOption,
286
                        index: index + 1
287
                    });
288
                } else if (!childOption.isLeaf) {
289
                    allLeafs = allLeafs.concat(this.getAllLeafs(childOption, index + 1, selected));
156✔
290
                }
156!
291
            }
246✔
292
        }
293
        return allLeafs;
156✔
294
    }
295

296
    public searchInLocal(searchText: string): void {
1✔
297
        this.forEachColumns();
1✔
298

1✔
299
        this.setSearchResultList(this.cascaderOptions.isOnlySelectLeaf ? this.leafNodes : this.flattenOptions, searchText);
300
    }
1✔
301

1✔
302
    private forEachColumns(
303
        currentLabel?: string[],
1✔
304
        currentValue: Id[] = [],
1!
305
        currentRowValue: ThyCascaderOption[] = [],
1✔
306
        list = this.columns[0]
307
    ) {
1✔
308
        list.forEach(item => {
309
            const curOptionLabel = this.getOptionLabel(item);
310
            const curOptionValue = this.getOptionValue(item);
8✔
311
            const label: string[] = currentLabel ? [...currentLabel, curOptionLabel] : [curOptionLabel];
8✔
312
            const valueList: Id[] = [...currentValue, curOptionValue];
8✔
313
            const rowValueList: ThyCascaderOption[] = [...currentRowValue, item];
314
            const isSelected = this.isSelectedOption(item, valueList.length - 1);
315

1✔
316
            const node = {
1✔
317
                labelList: label,
1✔
318
                valueList,
1✔
319
                selected: isSelected,
320
                thyRowValue: rowValueList,
321
                isLeaf: item.isLeaf,
1✔
322
                disabled: item.disabled
1✔
323
            };
1✔
324

1✔
325
            this.flattenOptions.push(node);
326
            if (item.children && item.children.length) {
327
                this.forEachColumns(label, valueList, rowValueList, item.children);
2✔
328
            } else {
2✔
329
                this.leafNodes.push(node);
4✔
330
            }
1✔
331
        });
332
    }
333

334
    private setSearchResultList(listOfOption: ThyCascaderSearchOption[], searchText: string) {
335
        this.searchResultList = [];
9✔
336
        listOfOption.forEach(item => {
9✔
337
            if (!item.disabled && item.isLeaf && item.labelList.join().toLowerCase().indexOf(searchText.toLowerCase()) !== -1) {
9✔
338
                this.searchResultList.push(item);
8✔
339
            }
340
        });
341
    }
1✔
342

1✔
343
    /**
1!
344
     * 检查所有所有子项的选择状态, 有一个不符合预期,就直接返回 false
1✔
345
     * @param option
1✔
346
     * @param trueOrFalse
347
     * @private
NEW
348
     */
×
349
    private checkSelectedStatus(option: ThyCascaderOption, isSelected: boolean): boolean {
350
        if (option.isLeaf) {
351
            return option.selected === isSelected;
1✔
352
        }
353
        for (const childOption of option.children) {
9✔
354
            if (isArray(childOption.children) && childOption.children.length && !this.checkSelectedStatus(childOption, isSelected)) {
355
                return false;
356
            }
108✔
357
            if (!childOption.selected === isSelected) {
29✔
358
                return false;
87✔
359
            }
29✔
360
        }
29✔
361
        return true;
362
    }
363

29✔
364
    private isLoaded(index: number): boolean {
365
        return this.columns[index] && this.columns[index].length > 0;
366
    }
367

57✔
368
    private findOption(option: any, index: number): ThyCascaderOption {
115✔
369
        const options: ThyCascaderOption[] = this.columns[index];
81✔
370
        if (options) {
81✔
371
            const value = typeof option === 'object' ? this.getOptionValue(option) : option;
81✔
372
            return options.find(o => value === this.getOptionValue(o));
28✔
373
        }
374
        return null;
375
    }
376

377
    private setColumnData(options: ThyCascaderOption[], index: number): void {
378
        if (!this.arrayEquals(this.columns[index], options)) {
116✔
379
            this.columns[index] = options;
130✔
380
            if (index < this.columns.length - 1) {
116✔
381
                this.columns = this.columns.slice(0, index + 1);
67✔
382
            }
383
        }
384
    }
385

49✔
386
    private getSubmitValue(originOptions: ThyCascaderOption[]): any[] {
4✔
387
        const values: any[] = [];
388
        (originOptions || []).forEach(option => {
389
            values.push(this.getOptionValue(option));
45✔
390
        });
45✔
391
        return values;
392
    }
49!
393

49✔
394
    public removeSelectedItem(item: SelectOptionBase) {
395
        const selectedItems = this.selectionModel.selected;
396
        const currentItem = selectedItems.find(i => {
397
            return helpers.shallowEqual(i.thyValue, item.thyValue);
398
        });
399
        this.deselectOption(currentItem);
400
        this.selectionModel.deselect(currentItem);
401
        // update selectedOptions
402
        const updatedSelectedItems = this.selectionModel.selected;
49✔
403
        if (isArray(updatedSelectedItems) && updatedSelectedItems.length) {
404
            this.selectedOptions = updatedSelectedItems[updatedSelectedItems.length - 1].thyRawValue.value;
405
        }
406

108!
NEW
407
        this.valueChange$.next();
×
408
    }
409

108✔
410
    public resetSearch() {
78✔
411
        this.searchResultList = [];
78✔
412
        this.leafNodes = [];
12✔
413
        this.flattenOptions = [];
414
    }
415

66✔
416
    public clearSelection() {
66✔
417
        this.labelRenderText = '';
66✔
418
        this.selectedOptions = [];
419
        this.activatedOptions = [];
420
        this.deselectAllSelected();
421
    }
30✔
422

30✔
423
    private deselectAllSelected() {
30✔
424
        const selectedOptions = this.selectionModel.selected;
30✔
425
        selectedOptions.forEach(item => this.deselectOption(item));
30!
426
        this.selectionModel.clear();
30✔
427
        this.valueChange$.next();
428
    }
NEW
429

×
NEW
430
    private deselectOption(option: SelectOptionBase) {
×
NEW
431
        const value: ThyCascaderOption[] = option.thyRawValue.value;
×
432
        value.forEach(item => {
433
            if (item.isLeaf && item.selected) {
434
                set(item, 'selected', false);
435
            }
436
        });
437
    }
108✔
438

108✔
439
    public selectOption(option: ThyCascaderOption, index: number): void {
108✔
440
        this.selectedOptions = this.activatedOptions;
108✔
441
        this.updatePrevSelectedOptions(option, false, index);
442
        if (option.selected) {
443
            this.buildDisplayLabel();
1,381✔
444
        } else {
1,364✔
445
            const selectedItems = this.selectionModel.selected;
1,364✔
446
            const currentItem = selectedItems.find(item => {
447
                if (item.thyRawValue.value.length - 1 === index) {
448
                    const selectedItem = helpers.get(item, `thyRawValue.value.${index}`);
17✔
449
                    return helpers.shallowEqual(selectedItem, option);
6✔
450
                } else {
451
                    return false;
452
                }
11✔
453
            });
11✔
454
            this.selectionModel.deselect(currentItem);
3✔
455
        }
3✔
456
        this.valueChange$.next();
457
    }
11✔
458

459
    private addSelectedState(selectOptions: ThyCascaderOption[]) {
460
        if (this.cascaderOptions.isMultiple && this.cascaderOptions.isOnlySelectLeaf) {
461
            selectOptions.forEach(opt => {
462
                if (opt.isLeaf) {
1,381✔
463
                    opt.selected = true;
183✔
464
                    set(opt, 'selected', true);
465
                }
1,198✔
466
            });
467
            this.addParentSelectedState(selectOptions);
468
        }
469
    }
22✔
470

48✔
471
    private addParentSelectedState(selectOptions: ThyCascaderOption[]) {
22✔
472
        selectOptions.forEach(opt => {
473
            if (opt.children && opt.children.length && opt.children.every(i => i.selected)) {
474
                opt.selected = true;
133✔
475
                set(opt, 'selected', true);
114✔
476
                if (opt.parent) {
33✔
477
                    this.addParentSelectedState([opt.parent]);
478
                }
114✔
479
            }
480
        });
481
    }
19✔
482

4✔
483
    private buildDisplayLabel(): void {
4✔
NEW
484
        const selectedOptions = [...this.selectedOptions];
×
485
        const labels: string[] = selectedOptions.map(o => this.getOptionLabel(o));
486

4✔
487
        if (labels.length === 0) {
488
            return;
19✔
489
        }
10✔
490

491
        let labelRenderContext;
492
        let labelRenderText;
9✔
493

494
        if (this.cascaderOptions.isLabelRenderTemplate) {
19✔
495
            labelRenderContext = { labels, selectedOptions };
10✔
496
        } else {
497
            labelRenderText = defaultDisplayRender.call(this, labels, selectedOptions);
19✔
498
            this.labelRenderText = labelRenderText;
499
        }
500

501
        if (this.labelRenderText || this.cascaderOptions.isLabelRenderTemplate) {
187!
502
            const selectedData: SelectOptionBase = {
503
                thyRawValue: {
504
                    value: selectedOptions,
452!
505
                    labelText: labelRenderText,
506
                    labelRenderContext: labelRenderContext
507
                },
138✔
508
                thyValue: labels,
80✔
509
                thyLabelText: labelRenderText
510
            };
58✔
511
            this.selectionModel.select(selectedData);
6✔
512
        }
513
    }
514

52✔
515
    public writeCascaderValue(value: any): void {
516
        if (!this.selectionModel) {
517
            this.initSelectionModel(this.cascaderOptions.isMultiple);
518
        }
102✔
519
        if (!this.cascaderOptions.isMultiple) {
78✔
520
            const vs = (this.defaultValue = this.toArray(value));
521
            if (vs.length) {
30✔
522
                this.initOptions(0);
523
            } else {
524
                this.value = vs;
1✔
525
                this.activatedOptions = [];
526
                this.afterWriteValue();
1✔
527
            }
528
        } else {
529
            const values = this.toArray(value);
530
            this.selectionModel.clear();
531
            values.forEach(item => {
532
                const vs = (this.defaultValue = this.toArray(item));
533
                if (vs.length) {
534
                    this.initOptions(0);
535
                } else {
536
                    this.value = vs;
537
                    this.activatedOptions = [];
538
                    this.afterWriteValue();
539
                }
540
            });
541
        }
542
    }
543

544
    private afterWriteValue() {
545
        this.selectedOptions = this.activatedOptions;
546
        this.value = this.getSubmitValue(this.selectedOptions);
547
        this.addSelectedState(this.selectedOptions);
548
        this.buildDisplayLabel();
549
    }
550

551
    public isActivatedOption(option: ThyCascaderOption, index: number): boolean {
552
        if (!this.cascaderOptions?.isMultiple || this.cascaderOptions.isOnlySelectLeaf) {
553
            const activeOpt = this.activatedOptions[index];
554
            return activeOpt === option;
555
        } else {
556
            if (option.isLeaf) {
557
                return option.selected;
558
            } else {
559
                const selectedOpts = this.selectionModel.selected;
560
                const appearIndex = selectedOpts.findIndex(item => {
561
                    const selectedItem = helpers.get(item, `thyRawValue.value.${index}`);
562
                    return helpers.shallowEqual(selectedItem, option);
563
                });
564
                return appearIndex >= 0;
565
            }
566
        }
567
    }
568

569
    public isHalfSelectedOption(option: ThyCascaderOption, index: number): boolean {
570
        if (!option.selected && this.cascaderOptions.isOnlySelectLeaf && !option.isLeaf && !this.checkSelectedStatus(option, false)) {
571
            return true;
572
        }
573
        return false;
574
    }
575

576
    public getValues() {
577
        let selectedItems: any[];
578
        const selected = this.selectionModel.selected;
579
        selectedItems = selected.map(item => this.getSubmitValue(item.thyRawValue.value));
580
        return this.cascaderOptions?.isMultiple ? selectedItems : selectedItems[0] ?? selectedItems;
581
    }
582

583
    private updatePrevSelectedOptions(option: ThyCascaderOption, isActivateInit: boolean, index?: number) {
584
        if (isActivateInit) {
585
            if (this.cascaderOptions.isOnlySelectLeaf && option.isLeaf) {
586
                set(option, 'selected', true);
587
            }
588
            this.prevSelectedOptions.add(option);
589
        } else {
590
            if (!this.cascaderOptions.isMultiple) {
591
                const prevSelectedOptions = Array.from(this.prevSelectedOptions);
592
                while (prevSelectedOptions.length) {
593
                    set(prevSelectedOptions.pop(), 'selected', false);
594
                }
595
                this.prevSelectedOptions = new Set([]);
596
            }
597
            if (this.cascaderOptions.isOnlySelectLeaf && !option.isLeaf && this.cascaderOptions.isMultiple) {
598
                set(option, 'selected', this.isSelectedOption(option, index));
599
            } else {
600
                set(option, 'selected', !this.isSelectedOption(option, index));
601
            }
602
            if (this.cascaderOptions.isOnlySelectLeaf && this.cascaderOptions.isMultiple && option.parent) {
603
                this.updatePrevSelectedOptions(option.parent, false, index - 1);
604
            }
605
            this.prevSelectedOptions.add(option);
606
        }
607
    }
608

609
    private getOptionLabel(option: ThyCascaderOption): any {
610
        return option[this.cascaderOptions.labelProperty || 'label'];
611
    }
612

613
    private getOptionValue(option: ThyCascaderOption): any {
614
        return option[this.cascaderOptions.valueProperty || 'value'];
615
    }
616

617
    toArray<T>(value: T | T[]): T[] {
618
        if (value == null) {
619
            return [];
620
        } else if (!Array.isArray(value)) {
621
            return [value];
622
        } else {
623
            return value;
624
        }
625
    }
626

627
    arrayEquals<T>(array1: T[], array2: T[]): boolean {
628
        if (!Array.isArray(array1) || !Array.isArray(array2) || array1.length !== array2.length) {
629
            return false;
630
        }
631

632
        return array1.every((element, index) => element === array2[index]);
633
    }
634

635
    ngOnDestroy(): void {}
636
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc