UNPKG

ng-zorro-antd

Version:

An enterprise-class UI components based on Ant Design and Angular

1 lines 97.2 kB
{"version":3,"file":"ng-zorro-antd-cascader.mjs","sources":["../../components/cascader/typings.ts","../../components/cascader/utils.ts","../../components/cascader/cascader-li.component.ts","../../components/cascader/cascader.service.ts","../../components/cascader/cascader.component.ts","../../components/cascader/cascader.module.ts","../../components/cascader/public-api.ts","../../components/cascader/ng-zorro-antd-cascader.ts"],"sourcesContent":["/**\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE\n */\n\nimport { Observable } from 'rxjs';\n\nimport { NzSafeAny } from 'ng-zorro-antd/core/types';\n\nexport type NzCascaderExpandTrigger = 'click' | 'hover';\nexport type NzCascaderTriggerType = 'click' | 'hover';\nexport type NzCascaderSize = 'small' | 'large' | 'default';\n\nexport type NzCascaderFilter = (searchValue: string, path: NzCascaderOption[]) => boolean;\nexport type NzCascaderSorter = (a: NzCascaderOption[], b: NzCascaderOption[], inputValue: string) => number;\n\nexport interface NzCascaderOption {\n value?: NzSafeAny;\n label?: string;\n title?: string;\n disabled?: boolean;\n loading?: boolean;\n isLeaf?: boolean;\n parent?: NzCascaderOption;\n children?: NzCascaderOption[];\n\n [key: string]: NzSafeAny;\n}\n\nexport interface NzCascaderSearchOption extends NzCascaderOption {\n path: NzCascaderOption[];\n}\n\nexport interface NzShowSearchOptions {\n filter?: NzCascaderFilter;\n sorter?: NzCascaderSorter;\n}\n\nexport function isShowSearchObject(options: NzShowSearchOptions | boolean): options is NzShowSearchOptions {\n return typeof options !== 'boolean';\n}\n\n/**\n * To avoid circular dependency, provide an interface of `NzCascaderComponent`\n * for `NzCascaderService`.\n */\nexport interface NzCascaderComponentAsSource {\n inputValue: string;\n nzShowSearch: NzShowSearchOptions | boolean;\n nzLabelProperty: string;\n nzValueProperty: string;\n nzChangeOnSelect: boolean;\n\n nzChangeOn?(option: NzCascaderOption, level: number): boolean;\n\n nzLoadData?(node: NzCascaderOption, index: number): PromiseLike<NzSafeAny> | Observable<NzSafeAny>;\n}\n","/**\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE\n */\n\nimport { NzCascaderOption } from './typings';\n\nexport function isChildOption(o: NzCascaderOption): boolean {\n return o.isLeaf || !o.children || !o.children.length;\n}\n\nexport function isParentOption(o: NzCascaderOption): boolean {\n return !!o.children && !!o.children.length && !o.isLeaf;\n}\n","/**\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE\n */\n\nimport { Direction } from '@angular/cdk/bidi';\nimport { NgTemplateOutlet } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ElementRef,\n Input,\n OnInit,\n TemplateRef,\n ViewEncapsulation,\n numberAttribute\n} from '@angular/core';\n\nimport { NzHighlightModule } from 'ng-zorro-antd/core/highlight';\nimport { NzOutletModule } from 'ng-zorro-antd/core/outlet';\nimport { NzIconModule } from 'ng-zorro-antd/icon';\n\nimport { NzCascaderOption } from './typings';\n\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n selector: '[nz-cascader-option]',\n exportAs: 'nzCascaderOption',\n template: `\n @if (optionTemplate) {\n <ng-template\n [ngTemplateOutlet]=\"optionTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: option, index: columnIndex }\"\n />\n } @else {\n <div\n class=\"ant-cascader-menu-item-content\"\n [innerHTML]=\"optionLabel | nzHighlight: highlightText : 'g' : 'ant-cascader-menu-item-keyword'\"\n ></div>\n }\n\n @if (!option.isLeaf || option.children?.length || option.loading) {\n <div class=\"ant-cascader-menu-item-expand-icon\">\n @if (option.loading) {\n <span nz-icon nzType=\"loading\"></span>\n } @else {\n <ng-container *nzStringTemplateOutlet=\"expandIcon\">\n <span nz-icon [nzType]=\"$any(expandIcon)\"></span>\n </ng-container>\n }\n </div>\n }\n `,\n host: {\n class: 'ant-cascader-menu-item ant-cascader-menu-item-expanded',\n '[attr.title]': 'option.title || optionLabel',\n '[class.ant-cascader-menu-item-active]': 'activated',\n '[class.ant-cascader-menu-item-expand]': '!option.isLeaf',\n '[class.ant-cascader-menu-item-disabled]': 'option.disabled'\n },\n imports: [NgTemplateOutlet, NzHighlightModule, NzIconModule, NzOutletModule],\n standalone: true\n})\nexport class NzCascaderOptionComponent implements OnInit {\n @Input() optionTemplate: TemplateRef<NzCascaderOption> | null = null;\n @Input() option!: NzCascaderOption;\n @Input() activated = false;\n @Input() highlightText!: string;\n @Input() nzLabelProperty = 'label';\n @Input({ transform: numberAttribute }) columnIndex!: number;\n @Input() expandIcon: string | TemplateRef<void> = '';\n @Input() dir: Direction = 'ltr';\n\n readonly nativeElement: HTMLElement;\n\n constructor(\n private cdr: ChangeDetectorRef,\n elementRef: ElementRef\n ) {\n this.nativeElement = elementRef.nativeElement;\n }\n ngOnInit(): void {\n if (this.expandIcon === '' && this.dir === 'rtl') {\n this.expandIcon = 'left';\n } else if (this.expandIcon === '') {\n this.expandIcon = 'right';\n }\n }\n\n get optionLabel(): string {\n return this.option[this.nzLabelProperty];\n }\n\n markForCheck(): void {\n this.cdr.markForCheck();\n }\n}\n","/**\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE\n */\n\nimport { Injectable, OnDestroy } from '@angular/core';\nimport { BehaviorSubject, from, Subject } from 'rxjs';\nimport { finalize } from 'rxjs/operators';\n\nimport { NzSafeAny } from 'ng-zorro-antd/core/types';\nimport { arraysEqual, isNotNil } from 'ng-zorro-antd/core/util';\n\nimport {\n isShowSearchObject,\n NzCascaderComponentAsSource,\n NzCascaderFilter,\n NzCascaderOption,\n NzCascaderSearchOption\n} from './typings';\nimport { isChildOption, isParentOption } from './utils';\n\n/**\n * All data is stored and parsed in NzCascaderService.\n */\n@Injectable()\nexport class NzCascaderService implements OnDestroy {\n /** Activated options in each column. */\n activatedOptions: NzCascaderOption[] = [];\n\n /** An array to store cascader items arranged in different layers. */\n columns: NzCascaderOption[][] = [];\n\n /** If user has entered searching mode. */\n inSearchingMode = false;\n\n /** Selected options would be output to user. */\n selectedOptions: NzCascaderOption[] = [];\n\n values: NzSafeAny[] = [];\n\n readonly $loading = new BehaviorSubject<boolean>(false);\n\n /**\n * Emit an event to notify cascader it needs to redraw because activated or\n * selected options are changed.\n */\n readonly $redraw = new Subject<void>();\n\n /**\n * Emit an event when an option gets selected.\n * Emit true if a leaf options is selected.\n */\n readonly $optionSelected = new Subject<{\n option: NzCascaderOption;\n index: number;\n } | null>();\n\n /**\n * Emit an event to notify cascader it needs to quit searching mode.\n * Only emit when user do select a searching option.\n */\n readonly $quitSearching = new Subject<void>();\n\n /** To hold columns before entering searching mode. */\n private columnsSnapshot: NzCascaderOption[][] = [[]];\n\n /** To hold activated options before entering searching mode. */\n private activatedOptionsSnapshot: NzCascaderOption[] = [];\n\n private cascaderComponent!: NzCascaderComponentAsSource;\n\n /** Return cascader options in the first layer. */\n get nzOptions(): NzCascaderOption[] {\n return this.columns[0];\n }\n\n ngOnDestroy(): void {\n this.$redraw.complete();\n this.$quitSearching.complete();\n this.$optionSelected.complete();\n this.$loading.complete();\n }\n\n /**\n * Make sure that value matches what is displayed in the dropdown.\n */\n syncOptions(first: boolean = false): void {\n const values = this.values;\n const hasValue = values && values.length;\n const lastColumnIndex = values.length - 1;\n const initColumnWithIndex = (columnIndex: number): void => {\n const activatedOptionSetter = (): void => {\n const currentValue = values[columnIndex];\n\n if (!isNotNil(currentValue)) {\n this.$redraw.next();\n return;\n }\n\n const option =\n this.findOptionWithValue(columnIndex, values[columnIndex]) ||\n (typeof currentValue === 'object'\n ? currentValue\n : {\n [`${this.cascaderComponent.nzValueProperty}`]: currentValue,\n [`${this.cascaderComponent.nzLabelProperty}`]: currentValue\n });\n\n this.setOptionActivated(option, columnIndex, false, false);\n\n if (columnIndex < lastColumnIndex) {\n initColumnWithIndex(columnIndex + 1);\n } else {\n this.dropBehindColumns(columnIndex);\n this.selectedOptions = [...this.activatedOptions];\n this.$redraw.next();\n }\n };\n\n if (this.isLoaded(columnIndex) || !this.cascaderComponent.nzLoadData) {\n activatedOptionSetter();\n } else {\n const option = this.activatedOptions[columnIndex - 1] || {};\n this.loadChildren(option, columnIndex - 1, activatedOptionSetter);\n }\n };\n\n this.activatedOptions = [];\n this.selectedOptions = [];\n\n if (first && this.cascaderComponent.nzLoadData && !hasValue) {\n // Should also notify the component that value changes. Fix #3480.\n this.$redraw.next();\n return;\n } else {\n initColumnWithIndex(0);\n }\n }\n\n /**\n * Bind cascader component so this service could use inputs.\n */\n withComponent(cascaderComponent: NzCascaderComponentAsSource): void {\n this.cascaderComponent = cascaderComponent;\n }\n\n /**\n * Reset all options. Rebuild searching options if in searching mode.\n */\n withOptions(options: NzCascaderOption[] | null): void {\n this.columnsSnapshot = this.columns = options && options.length ? [options] : [];\n\n if (this.inSearchingMode) {\n this.prepareSearchOptions(this.cascaderComponent.inputValue);\n } else if (this.columns.length) {\n this.syncOptions();\n }\n }\n\n /**\n * Try to set a option as activated.\n *\n * @param option Cascader option\n * @param columnIndex Of which column this option is in\n * @param performSelect Select\n * @param loadingChildren Try to load children asynchronously.\n */\n setOptionActivated(\n option: NzCascaderOption,\n columnIndex: number,\n performSelect: boolean = false,\n loadingChildren: boolean = true\n ): void {\n if (option.disabled) {\n return;\n }\n this.activatedOptions[columnIndex] = option;\n this.trackAncestorActivatedOptions(columnIndex);\n this.dropBehindActivatedOptions(columnIndex);\n\n const isParent = isParentOption(option);\n\n if (isParent) {\n // Parent option that has children.\n this.setColumnData(option.children!, columnIndex + 1, option);\n } else if (!option.isLeaf && loadingChildren) {\n // Parent option that should try to load children asynchronously.\n this.loadChildren(option, columnIndex);\n } else if (option.isLeaf) {\n // Leaf option.\n this.dropBehindColumns(columnIndex);\n }\n\n // Actually perform selection to make an options not only activated but also selected.\n if (performSelect) {\n this.setOptionSelected(option, columnIndex);\n }\n\n this.$redraw.next();\n }\n\n setOptionSelected(option: NzCascaderOption, index: number): void {\n const changeOn = this.cascaderComponent.nzChangeOn;\n const shouldPerformSelection = (o: NzCascaderOption, i: number): boolean =>\n typeof changeOn === 'function' ? changeOn(o, i) : false;\n\n if (option.isLeaf || this.cascaderComponent.nzChangeOnSelect || shouldPerformSelection(option, index)) {\n this.selectedOptions = [...this.activatedOptions];\n this.prepareEmitValue();\n this.$redraw.next();\n this.$optionSelected.next({ option, index });\n }\n }\n\n setOptionDeactivatedSinceColumn(column: number): void {\n this.dropBehindActivatedOptions(column - 1);\n this.dropBehindColumns(column);\n this.$redraw.next();\n }\n\n /**\n * Set a searching option as selected, finishing up things.\n *\n * @param option\n */\n setSearchOptionSelected(option: NzCascaderSearchOption): void {\n this.activatedOptions = [option];\n this.selectedOptions = [...option.path];\n this.prepareEmitValue();\n this.$redraw.next();\n this.$optionSelected.next({ option, index: 0 });\n\n setTimeout(() => {\n // Reset data and tell UI only to remove input and reset dropdown width style.\n this.$quitSearching.next();\n this.$redraw.next();\n this.inSearchingMode = false;\n this.columns = [...this.columnsSnapshot];\n this.activatedOptions = [...this.selectedOptions];\n }, 200);\n }\n\n /**\n * Filter cascader options to reset `columns`.\n *\n * @param searchValue The string user wants to search.\n */\n prepareSearchOptions(searchValue: string): void {\n const results: NzCascaderOption[] = []; // Search results only have one layer.\n const path: NzCascaderOption[] = [];\n const defaultFilter: NzCascaderFilter = (i, p) =>\n p.some(o => {\n const label = this.getOptionLabel(o);\n return !!label && label.indexOf(i) !== -1;\n });\n const showSearch = this.cascaderComponent.nzShowSearch;\n const filter = isShowSearchObject(showSearch) && showSearch.filter ? showSearch.filter : defaultFilter;\n const sorter = isShowSearchObject(showSearch) && showSearch.sorter ? showSearch.sorter : null;\n const loopChild = (node: NzCascaderOption, forceDisabled = false): void => {\n path.push(node);\n const cPath = Array.from(path);\n if (filter(searchValue, cPath)) {\n const disabled = forceDisabled || node.disabled;\n const option: NzCascaderSearchOption = {\n disabled,\n isLeaf: true,\n path: cPath,\n [this.cascaderComponent.nzLabelProperty]: cPath.map(p => this.getOptionLabel(p)).join(' / ')\n };\n results.push(option);\n }\n path.pop();\n };\n const loopParent = (node: NzCascaderOption, forceDisabled = false): void => {\n const disabled = forceDisabled || node.disabled;\n path.push(node);\n node.children!.forEach(sNode => {\n if (!sNode.parent) {\n sNode.parent = node;\n }\n if (!sNode.isLeaf) {\n loopParent(sNode, disabled);\n }\n if (sNode.isLeaf || !sNode.children || !sNode.children.length) {\n loopChild(sNode, disabled);\n }\n });\n path.pop();\n };\n\n if (!this.columnsSnapshot.length) {\n this.columns = [[]];\n return;\n }\n\n this.columnsSnapshot[0].forEach(o => (isChildOption(o) ? loopChild(o) : loopParent(o)));\n\n if (sorter) {\n results.sort((a, b) => sorter(a.path, b.path, searchValue));\n }\n\n this.columns = [results];\n\n this.$redraw.next(); // Search results may be empty, so should redraw.\n }\n\n /**\n * Toggle searching mode by UI. It deals with things not directly related to UI.\n *\n * @param toSearching If this cascader is entering searching mode\n */\n toggleSearchingMode(toSearching: boolean): void {\n this.inSearchingMode = toSearching;\n\n if (toSearching) {\n this.activatedOptionsSnapshot = [...this.activatedOptions];\n this.activatedOptions = [];\n this.selectedOptions = [];\n this.$redraw.next();\n } else {\n // User quit searching mode without selecting an option.\n this.activatedOptions = [...this.activatedOptionsSnapshot];\n this.selectedOptions = [...this.activatedOptions];\n this.columns = [...this.columnsSnapshot];\n this.syncOptions();\n this.$redraw.next();\n }\n }\n\n /**\n * Clear selected options.\n */\n clear(): void {\n this.values = [];\n this.selectedOptions = [];\n this.activatedOptions = [];\n this.dropBehindColumns(0);\n this.$redraw.next();\n this.$optionSelected.next(null);\n }\n\n getOptionLabel(o: NzCascaderOption): string {\n return o[this.cascaderComponent.nzLabelProperty || 'label'] as string;\n }\n\n getOptionValue(o: NzCascaderOption): NzSafeAny {\n return o[this.cascaderComponent.nzValueProperty || 'value'];\n }\n\n /**\n * Try to insert options into a column.\n *\n * @param options Options to insert\n * @param columnIndex Position\n */\n private setColumnData(options: NzCascaderOption[], columnIndex: number, parent: NzCascaderOption): void {\n const existingOptions = this.columns[columnIndex];\n if (!arraysEqual(existingOptions, options)) {\n options.forEach(o => (o.parent = parent));\n this.columns[columnIndex] = options;\n this.dropBehindColumns(columnIndex);\n }\n }\n\n /**\n * Set all ancestor options as activated.\n */\n private trackAncestorActivatedOptions(startIndex: number): void {\n for (let i = startIndex - 1; i >= 0; i--) {\n if (!this.activatedOptions[i]) {\n this.activatedOptions[i] = this.activatedOptions[i + 1].parent!;\n }\n }\n }\n\n private dropBehindActivatedOptions(lastReserveIndex: number): void {\n this.activatedOptions = this.activatedOptions.splice(0, lastReserveIndex + 1);\n }\n\n private dropBehindColumns(lastReserveIndex: number): void {\n if (lastReserveIndex < this.columns.length - 1) {\n this.columns = this.columns.slice(0, lastReserveIndex + 1);\n }\n }\n\n /**\n * Load children of an option asynchronously.\n */\n loadChildren(\n option: NzCascaderOption | NzSafeAny,\n columnIndex: number,\n success?: VoidFunction,\n failure?: VoidFunction\n ): void {\n const loadFn = this.cascaderComponent.nzLoadData;\n\n if (loadFn) {\n // If there isn't any option in columns.\n this.$loading.next(columnIndex < 0);\n\n if (typeof option === 'object') {\n option.loading = true;\n }\n\n from(loadFn(option, columnIndex))\n .pipe(\n finalize(() => {\n option.loading = false;\n this.$loading.next(false);\n this.$redraw.next();\n })\n )\n .subscribe({\n next: () => {\n if (option.children) {\n this.setColumnData(option.children, columnIndex + 1, option);\n }\n success?.();\n },\n error: () => {\n option.isLeaf = true;\n failure?.();\n }\n });\n }\n }\n\n private isLoaded(index: number): boolean {\n return this.columns[index] && this.columns[index].length > 0;\n }\n\n /**\n * Find a option that has a given value in a given column.\n */\n private findOptionWithValue(columnIndex: number, value: NzCascaderOption | NzSafeAny): NzCascaderOption | null {\n const targetColumn = this.columns[columnIndex];\n if (targetColumn) {\n const v = typeof value === 'object' ? this.getOptionValue(value) : value;\n return targetColumn.find(o => v === this.getOptionValue(o))!;\n }\n return null;\n }\n\n private prepareEmitValue(): void {\n this.values = this.selectedOptions.map(o => this.getOptionValue(o));\n }\n}\n","/**\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE\n */\n\nimport { Direction, Directionality } from '@angular/cdk/bidi';\nimport { BACKSPACE, DOWN_ARROW, ENTER, ESCAPE, LEFT_ARROW, RIGHT_ARROW, UP_ARROW } from '@angular/cdk/keycodes';\nimport { CdkConnectedOverlay, ConnectionPositionPair, OverlayModule } from '@angular/cdk/overlay';\nimport { _getEventTarget } from '@angular/cdk/platform';\nimport { NgClass, NgStyle, NgTemplateOutlet } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ElementRef,\n EventEmitter,\n HostListener,\n Input,\n NgZone,\n OnChanges,\n OnDestroy,\n OnInit,\n Output,\n QueryList,\n Renderer2,\n SimpleChanges,\n TemplateRef,\n ViewChild,\n ViewChildren,\n ViewEncapsulation,\n booleanAttribute,\n forwardRef,\n inject,\n numberAttribute\n} from '@angular/core';\nimport { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { BehaviorSubject, EMPTY, Observable, fromEvent, of as observableOf } from 'rxjs';\nimport { distinctUntilChanged, map, startWith, switchMap, takeUntil, withLatestFrom } from 'rxjs/operators';\n\nimport { slideMotion } from 'ng-zorro-antd/core/animation';\nimport { NzConfigKey, NzConfigService, WithConfig } from 'ng-zorro-antd/core/config';\nimport { NzFormNoStatusService, NzFormPatchModule, NzFormStatusService } from 'ng-zorro-antd/core/form';\nimport { NzNoAnimationDirective } from 'ng-zorro-antd/core/no-animation';\nimport { DEFAULT_CASCADER_POSITIONS, NzOverlayModule } from 'ng-zorro-antd/core/overlay';\nimport { NzDestroyService } from 'ng-zorro-antd/core/services';\nimport {\n NgClassInterface,\n NgClassType,\n NgStyleInterface,\n NzSafeAny,\n NzStatus,\n NzValidateStatus\n} from 'ng-zorro-antd/core/types';\nimport { getStatusClassNames, toArray } from 'ng-zorro-antd/core/util';\nimport { NzEmptyModule } from 'ng-zorro-antd/empty';\nimport { NzCascaderI18nInterface, NzI18nService } from 'ng-zorro-antd/i18n';\nimport { NzIconModule } from 'ng-zorro-antd/icon';\n\nimport { NzCascaderOptionComponent } from './cascader-li.component';\nimport { NzCascaderService } from './cascader.service';\nimport {\n NzCascaderComponentAsSource,\n NzCascaderExpandTrigger,\n NzCascaderOption,\n NzCascaderSearchOption,\n NzCascaderSize,\n NzCascaderTriggerType,\n NzShowSearchOptions\n} from './typings';\n\nconst NZ_CONFIG_MODULE_NAME: NzConfigKey = 'cascader';\nconst defaultDisplayRender = (labels: string[]): string => labels.join(' / ');\n\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n selector: 'nz-cascader, [nz-cascader]',\n exportAs: 'nzCascader',\n preserveWhitespaces: false,\n template: `\n <div cdkOverlayOrigin #origin=\"cdkOverlayOrigin\" #trigger>\n @if (nzShowInput) {\n <div #selectContainer class=\"ant-select-selector\">\n <span class=\"ant-select-selection-search\">\n <input\n #input\n type=\"search\"\n class=\"ant-select-selection-search-input\"\n [style.opacity]=\"nzShowSearch ? '' : '0'\"\n [attr.autoComplete]=\"'off'\"\n [attr.expanded]=\"menuVisible\"\n [attr.autofocus]=\"nzAutoFocus ? 'autofocus' : null\"\n [readonly]=\"!nzShowSearch\"\n [disabled]=\"nzDisabled\"\n [(ngModel)]=\"inputValue\"\n (blur)=\"handleInputBlur()\"\n (focus)=\"handleInputFocus()\"\n (compositionstart)=\"handleInputCompositionstart()\"\n (compositionend)=\"handleInputCompositionend()\"\n />\n </span>\n @if (showLabelRender) {\n <span class=\"ant-select-selection-item\" [title]=\"labelRenderText\">\n @if (!isLabelRenderTemplate) {\n <ng-container>{{ labelRenderText }}</ng-container>\n } @else {\n <ng-template\n [ngTemplateOutlet]=\"nzLabelRender\"\n [ngTemplateOutletContext]=\"labelRenderContext\"\n ></ng-template>\n }\n </span>\n } @else {\n <span\n class=\"ant-select-selection-placeholder\"\n [style.visibility]=\"isComposing || inputValue ? 'hidden' : 'visible'\"\n >{{ showPlaceholder ? nzPlaceHolder || locale?.placeholder : null }}</span\n >\n }\n </div>\n @if (nzShowArrow) {\n <span class=\"ant-select-arrow\" [class.ant-select-arrow-loading]=\"isLoading\">\n @if (!isLoading) {\n <span nz-icon [nzType]=\"$any(nzSuffixIcon)\" [class.ant-cascader-picker-arrow-expand]=\"menuVisible\"></span>\n } @else {\n <span nz-icon nzType=\"loading\"></span>\n }\n\n @if (hasFeedback && !!status) {\n <nz-form-item-feedback-icon [status]=\"status\" />\n }\n </span>\n }\n @if (clearIconVisible) {\n <span class=\"ant-select-clear\">\n <span nz-icon nzType=\"close-circle\" nzTheme=\"fill\" (click)=\"clearSelection($event)\"></span>\n </span>\n }\n }\n <ng-content></ng-content>\n </div>\n <ng-template\n cdkConnectedOverlay\n nzConnectedOverlay\n [cdkConnectedOverlayHasBackdrop]=\"nzBackdrop\"\n [cdkConnectedOverlayOrigin]=\"origin\"\n [cdkConnectedOverlayPositions]=\"positions\"\n [cdkConnectedOverlayTransformOriginOn]=\"'.ant-cascader-dropdown'\"\n [cdkConnectedOverlayOpen]=\"menuVisible\"\n (overlayOutsideClick)=\"onClickOutside($event)\"\n (detach)=\"closeMenu()\"\n >\n <div\n class=\"ant-select-dropdown ant-cascader-dropdown ant-select-dropdown-placement-bottomLeft\"\n [class.ant-cascader-dropdown-rtl]=\"dir === 'rtl'\"\n [@slideMotion]=\"'enter'\"\n [@.disabled]=\"!!noAnimation?.nzNoAnimation\"\n [nzNoAnimation]=\"noAnimation?.nzNoAnimation\"\n (mouseenter)=\"onTriggerMouseEnter()\"\n (mouseleave)=\"onTriggerMouseLeave($event)\"\n >\n <div\n #menu\n class=\"ant-cascader-menus\"\n [class.ant-cascader-rtl]=\"dir === 'rtl'\"\n [class.ant-cascader-menus-hidden]=\"!menuVisible\"\n [class.ant-cascader-menu-empty]=\"shouldShowEmpty\"\n [ngClass]=\"menuCls\"\n [ngStyle]=\"nzMenuStyle\"\n >\n @if (shouldShowEmpty) {\n <ul class=\"ant-cascader-menu\" [style.width]=\"dropdownWidthStyle\" [style.height]=\"dropdownHeightStyle\">\n <li class=\"ant-cascader-menu-item ant-cascader-menu-item-disabled\">\n <nz-embed-empty\n class=\"ant-cascader-menu-item-content\"\n [nzComponentName]=\"'cascader'\"\n [specificContent]=\"nzNotFoundContent\"\n />\n </li>\n </ul>\n } @else {\n @for (options of cascaderService.columns; track options; let i = $index) {\n <ul\n class=\"ant-cascader-menu\"\n role=\"menuitemcheckbox\"\n [ngClass]=\"menuColumnCls\"\n [style.height]=\"dropdownHeightStyle\"\n [style.width]=\"dropdownWidthStyle\"\n >\n @for (option of options; track option.value) {\n <li\n nz-cascader-option\n [expandIcon]=\"nzExpandIcon\"\n [columnIndex]=\"i\"\n [nzLabelProperty]=\"nzLabelProperty\"\n [optionTemplate]=\"nzOptionRender\"\n [activated]=\"isOptionActivated(option, i)\"\n [highlightText]=\"inSearchingMode ? inputValue : ''\"\n [option]=\"option\"\n [dir]=\"dir\"\n (mouseenter)=\"onOptionMouseEnter(option, i, $event)\"\n (mouseleave)=\"onOptionMouseLeave(option, i, $event)\"\n (click)=\"onOptionClick(option, i, $event)\"\n ></li>\n }\n </ul>\n }\n }\n </div>\n </div>\n </ng-template>\n `,\n animations: [slideMotion],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => NzCascaderComponent),\n multi: true\n },\n NzCascaderService,\n NzDestroyService\n ],\n host: {\n '[attr.tabIndex]': '\"0\"',\n '[class.ant-select-in-form-item]': '!!nzFormStatusService',\n '[class.ant-select-lg]': 'nzSize === \"large\"',\n '[class.ant-select-sm]': 'nzSize === \"small\"',\n '[class.ant-select-allow-clear]': 'nzAllowClear',\n '[class.ant-select-show-arrow]': 'nzShowArrow',\n '[class.ant-select-show-search]': '!!nzShowSearch',\n '[class.ant-select-disabled]': 'nzDisabled',\n '[class.ant-select-open]': 'menuVisible',\n '[class.ant-select-focused]': 'isFocused',\n '[class.ant-select-single]': 'true',\n '[class.ant-select-rtl]': `dir === 'rtl'`\n },\n imports: [\n OverlayModule,\n FormsModule,\n NgTemplateOutlet,\n NzIconModule,\n NzFormPatchModule,\n NzOverlayModule,\n NzNoAnimationDirective,\n NgClass,\n NgStyle,\n NzEmptyModule,\n NzCascaderOptionComponent\n ],\n standalone: true\n})\nexport class NzCascaderComponent\n implements NzCascaderComponentAsSource, OnInit, OnDestroy, OnChanges, ControlValueAccessor\n{\n readonly _nzModuleName: NzConfigKey = NZ_CONFIG_MODULE_NAME;\n\n @ViewChild('selectContainer', { static: false }) selectContainer!: ElementRef;\n\n @ViewChild('input', { static: false })\n set input(input: ElementRef<HTMLInputElement> | undefined) {\n this.input$.next(input);\n }\n get input(): ElementRef<HTMLInputElement> | undefined {\n return this.input$.getValue();\n }\n /** Used to store the native `<input type=\"search\" />` element since it might be set asynchronously. */\n private input$ = new BehaviorSubject<ElementRef<HTMLInputElement> | undefined>(undefined);\n\n @ViewChild('menu', { static: false }) menu!: ElementRef;\n @ViewChild(CdkConnectedOverlay, { static: false }) overlay!: CdkConnectedOverlay;\n @ViewChildren(NzCascaderOptionComponent) cascaderItems!: QueryList<NzCascaderOptionComponent>;\n\n @Input() nzOptionRender: TemplateRef<{ $implicit: NzCascaderOption; index: number }> | null = null;\n @Input({ transform: booleanAttribute }) nzShowInput = true;\n @Input({ transform: booleanAttribute }) nzShowArrow = true;\n @Input({ transform: booleanAttribute }) nzAllowClear = true;\n @Input({ transform: booleanAttribute }) nzAutoFocus = false;\n @Input({ transform: booleanAttribute }) nzChangeOnSelect = false;\n @Input({ transform: booleanAttribute }) nzDisabled = false;\n @Input() nzColumnClassName?: string;\n @Input() nzExpandTrigger: NzCascaderExpandTrigger = 'click';\n @Input() nzValueProperty = 'value';\n @Input() nzLabelRender: TemplateRef<typeof this.labelRenderContext> | null = null;\n @Input() nzLabelProperty = 'label';\n @Input() nzNotFoundContent?: string | TemplateRef<void>;\n @Input() @WithConfig() nzSize: NzCascaderSize = 'default';\n @Input() @WithConfig() nzBackdrop = false;\n @Input() nzShowSearch: boolean | NzShowSearchOptions = false;\n @Input() nzPlaceHolder: string = '';\n @Input() nzMenuClassName?: string;\n @Input() nzMenuStyle: NgStyleInterface | null = null;\n @Input({ transform: numberAttribute }) nzMouseEnterDelay: number = 150; // ms\n @Input({ transform: numberAttribute }) nzMouseLeaveDelay: number = 150; // ms\n @Input() nzStatus: NzStatus = '';\n\n @Input() nzTriggerAction: NzCascaderTriggerType | NzCascaderTriggerType[] = ['click'] as NzCascaderTriggerType[];\n @Input() nzChangeOn?: (option: NzCascaderOption, level: number) => boolean;\n @Input() nzLoadData?: (node: NzCascaderOption, index: number) => PromiseLike<NzSafeAny> | Observable<NzSafeAny>;\n // TODO: RTL\n @Input() nzSuffixIcon: string | TemplateRef<void> = 'down';\n @Input() nzExpandIcon: string | TemplateRef<void> = '';\n\n @Input()\n get nzOptions(): NzCascaderOption[] | null {\n return this.cascaderService.nzOptions;\n }\n\n set nzOptions(options: NzCascaderOption[] | null) {\n this.cascaderService.withOptions(options);\n }\n\n @Output() readonly nzVisibleChange = new EventEmitter<boolean>();\n @Output() readonly nzSelectionChange = new EventEmitter<NzCascaderOption[]>();\n @Output() readonly nzSelect = new EventEmitter<{ option: NzCascaderOption; index: number } | null>();\n @Output() readonly nzClear = new EventEmitter<void>();\n\n prefixCls: string = 'ant-select';\n statusCls: NgClassInterface = {};\n status: NzValidateStatus = '';\n hasFeedback: boolean = false;\n\n /**\n * If the dropdown should show the empty content.\n * `true` if there's no options.\n */\n shouldShowEmpty: boolean = false;\n\n el: HTMLElement;\n menuVisible = false;\n isLoading = false;\n labelRenderText?: string;\n labelRenderContext = {};\n onChange = Function.prototype;\n onTouched = Function.prototype;\n positions: ConnectionPositionPair[] = [...DEFAULT_CASCADER_POSITIONS];\n\n /**\n * Dropdown's with in pixel.\n */\n dropdownWidthStyle?: string;\n dropdownHeightStyle: 'auto' | '' = '';\n isFocused = false;\n\n locale!: NzCascaderI18nInterface;\n dir: Direction = 'ltr';\n\n isComposing = false;\n\n private inputString = '';\n private isOpening = false;\n private delayMenuTimer?: ReturnType<typeof setTimeout>;\n private delaySelectTimer?: ReturnType<typeof setTimeout>;\n private isNzDisableFirstChange: boolean = true;\n\n get inSearchingMode(): boolean {\n return this.cascaderService.inSearchingMode;\n }\n\n set inputValue(inputValue: string) {\n this.inputString = inputValue;\n this.toggleSearchingMode(!!inputValue);\n }\n\n get inputValue(): string {\n return this.inputString;\n }\n\n get menuCls(): NgClassType {\n return { [`${this.nzMenuClassName}`]: !!this.nzMenuClassName };\n }\n\n get menuColumnCls(): NgClassType {\n return { [`${this.nzColumnClassName}`]: !!this.nzColumnClassName };\n }\n\n private get hasInput(): boolean {\n return !!this.inputValue;\n }\n\n private get hasValue(): boolean {\n return this.cascaderService.values && this.cascaderService.values.length > 0;\n }\n\n get showLabelRender(): boolean {\n return this.hasValue;\n }\n\n get showPlaceholder(): boolean {\n return !(this.hasInput || this.hasValue);\n }\n\n get clearIconVisible(): boolean {\n return this.nzAllowClear && !this.nzDisabled && (this.hasValue || this.hasInput);\n }\n\n get isLabelRenderTemplate(): boolean {\n return !!this.nzLabelRender;\n }\n\n noAnimation = inject(NzNoAnimationDirective, { host: true, optional: true });\n nzFormStatusService = inject(NzFormStatusService, { optional: true });\n private nzFormNoStatusService = inject(NzFormNoStatusService, { optional: true });\n\n constructor(\n public cascaderService: NzCascaderService,\n public nzConfigService: NzConfigService,\n private ngZone: NgZone,\n private cdr: ChangeDetectorRef,\n private i18nService: NzI18nService,\n private destroy$: NzDestroyService,\n private elementRef: ElementRef,\n private renderer: Renderer2,\n private directionality: Directionality\n ) {\n this.el = elementRef.nativeElement;\n this.cascaderService.withComponent(this);\n this.renderer.addClass(this.elementRef.nativeElement, 'ant-select');\n this.renderer.addClass(this.elementRef.nativeElement, 'ant-cascader');\n }\n\n ngOnInit(): void {\n this.nzFormStatusService?.formStatusChanges\n .pipe(\n distinctUntilChanged((pre, cur) => {\n return pre.status === cur.status && pre.hasFeedback === cur.hasFeedback;\n }),\n withLatestFrom(this.nzFormNoStatusService ? this.nzFormNoStatusService.noFormStatus : observableOf(false)),\n map(([{ status, hasFeedback }, noStatus]) => ({ status: noStatus ? '' : status, hasFeedback })),\n takeUntil(this.destroy$)\n )\n .subscribe(({ status, hasFeedback }) => {\n this.setStatusStyles(status, hasFeedback);\n });\n\n const srv = this.cascaderService;\n\n srv.$redraw.pipe(takeUntil(this.destroy$)).subscribe(() => {\n // These operations would not mutate data.\n this.checkChildren();\n this.setDisplayLabel();\n this.cdr.detectChanges();\n this.reposition();\n this.setDropdownStyles();\n });\n\n srv.$loading.pipe(takeUntil(this.destroy$)).subscribe(loading => {\n this.isLoading = loading;\n });\n\n srv.$optionSelected.pipe(takeUntil(this.destroy$)).subscribe(data => {\n if (!data) {\n this.onChange([]);\n this.nzSelect.emit(null);\n this.nzSelectionChange.emit([]);\n } else {\n const { option, index } = data;\n const shouldClose = option.isLeaf || (this.nzChangeOnSelect && this.nzExpandTrigger === 'hover');\n if (shouldClose) {\n this.delaySetMenuVisible(false);\n }\n this.onChange(this.cascaderService.values);\n this.nzSelectionChange.emit(this.cascaderService.selectedOptions);\n this.nzSelect.emit({ option, index });\n this.cdr.markForCheck();\n }\n });\n\n srv.$quitSearching.pipe(takeUntil(this.destroy$)).subscribe(() => {\n this.inputString = '';\n this.dropdownWidthStyle = '';\n });\n\n this.i18nService.localeChange.pipe(startWith(), takeUntil(this.destroy$)).subscribe(() => {\n this.setLocale();\n });\n\n this.nzConfigService\n .getConfigChangeEventForComponent(NZ_CONFIG_MODULE_NAME)\n .pipe(takeUntil(this.destroy$))\n .subscribe(() => {\n this.cdr.markForCheck();\n });\n\n this.dir = this.directionality.value;\n this.directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => {\n this.dir = this.directionality.value;\n srv.$redraw.next();\n });\n\n this.setupChangeListener();\n this.setupKeydownListener();\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n const { nzStatus } = changes;\n if (nzStatus) {\n this.setStatusStyles(this.nzStatus, this.hasFeedback);\n }\n }\n\n ngOnDestroy(): void {\n this.clearDelayMenuTimer();\n this.clearDelaySelectTimer();\n }\n\n registerOnChange(fn: () => {}): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => {}): void {\n this.onTouched = fn;\n }\n\n writeValue(value: NzSafeAny): void {\n this.cascaderService.values = toArray(value);\n this.cascaderService.syncOptions(true);\n }\n\n delaySetMenuVisible(visible: boolean, delay: number = 100, setOpening: boolean = false): void {\n this.clearDelayMenuTimer();\n if (delay) {\n if (visible && setOpening) {\n this.isOpening = true;\n }\n this.delayMenuTimer = setTimeout(() => {\n this.setMenuVisible(visible);\n this.cdr.detectChanges();\n this.clearDelayMenuTimer();\n if (visible) {\n setTimeout(() => {\n this.isOpening = false;\n }, 100);\n }\n }, delay);\n } else {\n this.setMenuVisible(visible);\n }\n }\n\n setMenuVisible(visible: boolean): void {\n if (this.nzDisabled || this.menuVisible === visible) {\n return;\n }\n if (visible) {\n this.cascaderService.syncOptions();\n this.scrollToActivatedOptions();\n }\n\n if (!visible) {\n this.inputValue = '';\n }\n\n this.menuVisible = visible;\n this.nzVisibleChange.emit(visible);\n this.cdr.detectChanges();\n }\n\n private clearDelayMenuTimer(): void {\n if (this.delayMenuTimer) {\n clearTimeout(this.delayMenuTimer);\n this.delayMenuTimer = undefined;\n }\n }\n\n clearSelection(event?: Event): void {\n if (event) {\n event.preventDefault();\n event.stopPropagation();\n }\n\n this.labelRenderText = '';\n this.labelRenderContext = {};\n this.inputValue = '';\n this.setMenuVisible(false);\n this.cascaderService.clear();\n this.nzClear.emit();\n }\n\n getSubmitValue(): NzSafeAny[] {\n return this.cascaderService.selectedOptions.map(o => this.cascaderService.getOptionValue(o));\n }\n\n focus(): void {\n if (!this.isFocused) {\n (this.input?.nativeElement || this.el).focus();\n this.isFocused = true;\n }\n }\n\n blur(): void {\n if (this.isFocused) {\n (this.input?.nativeElement || this.el).blur();\n this.isFocused = false;\n }\n }\n\n handleInputBlur(): void {\n this.menuVisible ? this.focus() : this.blur();\n }\n\n handleInputFocus(): void {\n this.focus();\n }\n\n handleInputCompositionstart(): void {\n this.isComposing = true;\n }\n\n handleInputCompositionend(): void {\n this.isComposing = false;\n }\n\n @HostListener('click')\n onTriggerClick(): void {\n if (this.nzDisabled) {\n return;\n }\n if (this.nzShowSearch) {\n this.focus();\n }\n if (this.isActionTrigger('click')) {\n this.delaySetMenuVisible(!this.menuVisible, 100);\n }\n this.onTouched();\n }\n\n @HostListener('mouseenter')\n onTriggerMouseEnter(): void {\n if (this.nzDisabled || !this.isActionTrigger('hover')) {\n return;\n }\n\n this.delaySetMenuVisible(true, this.nzMouseEnterDelay, true);\n }\n\n @HostListener('mouseleave', ['$event'])\n onTriggerMouseLeave(event: MouseEvent): void {\n if (this.nzDisabled || !this.menuVisible || this.isOpening || !this.isActionTrigger('hover')) {\n event.preventDefault();\n return;\n }\n const mouseTarget = event.relatedTarget as HTMLElement;\n const hostEl = this.el;\n const menuEl = this.menu && (this.menu.nativeElement as HTMLElement);\n if (hostEl.contains(mouseTarget) || (menuEl && menuEl.contains(mouseTarget))) {\n return;\n }\n this.delaySetMenuVisible(false, this.nzMouseLeaveDelay);\n }\n\n onOptionMouseEnter(option: NzCascaderOption, columnIndex: number, event: Event): void {\n event.preventDefault();\n if (this.nzExpandTrigger === 'hover') {\n if (!option.isLeaf) {\n this.delaySetOptionActivated(option, columnIndex, false);\n } else {\n this.cascaderService.setOptionDeactivatedSinceColumn(columnIndex);\n }\n }\n }\n\n onOptionMouseLeave(option: NzCascaderOption, _columnIndex: number, event: Event): void {\n event.preventDefault();\n if (this.nzExpandTrigger === 'hover' && !option.isLeaf) {\n this.clearDelaySelectTimer();\n }\n }\n\n onOptionClick(option: NzCascaderOption, columnIndex: number, event: Event): void {\n if (event) {\n event.preventDefault();\n }\n if (option && option.disabled) {\n return;\n }\n\n this.el.focus();\n this.inSearchingMode\n ? this.cascaderService.setSearchOptionSelected(option as NzCascaderSearchOption)\n : this.cascaderService.setOptionActivated(option, columnIndex, true);\n }\n\n onClickOutside(event: MouseEvent): void {\n const target = _getEventTarget(event);\n if (!this.el.contains(target as Node)) {\n this.closeMenu();\n }\n }\n\n private isActionTrigger(action: 'click' | 'hover'): boolean {\n return typeof this.nzTriggerAction === 'string'\n ? this.nzTriggerAction === action\n : this.nzTriggerAction.indexOf(action) !== -1;\n }\n\n private onEnter(): void {\n const columnIndex = Math.max(this.cascaderService.activatedOptions.length - 1, 0);\n const option = this.cascaderService.activatedOptions[columnIndex];\n if (option && !option.disabled) {\n this.inSearchingMode\n ? this.cascaderService.setSearchOptionSelected(option as NzCascaderSearchOption)\n : this.cascaderService.setOptionActivated(option, columnIndex, true);\n }\n }\n\n private moveUpOrDown(isUp: boolean): void {\n const columnIndex = Math.max(this.cascaderService.activatedOptions.length - 1, 0);\n const activeOption = this.cascaderService.activatedOptions[columnIndex];\n const options = this.cascaderService.columns[columnIndex] || [];\n const length = options.length;\n let nextIndex = -1;\n if (!activeOption) {\n // Not selected options in this column\n nextIndex = isUp ? length : -1;\n } else {\n nextIndex = options.indexOf(activeOption);\n }\n\n while (true) {\n nextIndex = isUp ? nextIndex - 1 : nextIndex + 1;\n if (nextIndex < 0 || nextIndex >= length) {\n break;\n }\n const nextOption = options[nextIndex];\n if (!nextOption || nextOption.disabled) {\n continue;\n }\n this.cascaderService.setOptionActivated(nextOption, columnIndex);\n break;\n }\n }\n\n private moveLeft(): void {\n const options = this.cascaderService.activatedOptions;\n if (options.length) {\n options.pop(); // Remove the last one\n }\n }\n\n private moveRight(): void {\n const length = this.cascaderService.activatedOptions.length;\n const options = this.cascaderService.columns[length];\n if (options && options.length) {\n const nextOpt = options.find(o => !o.disabled);\n if (nextOpt) {\n this.cascaderService.setOptionActivated(nextOpt, length);\n }\n }\n }\n\n private clearDelaySelectTimer(): void {\n if (this.delaySelectTimer) {\n clearTimeout(this.delaySelectTimer);\n this.delaySelectTimer = undefined;\n }\n }\n\n private delaySetOptionActivated(option: NzCascaderOption, columnIndex: number, performSelect: boolean): void {\n this.clearDelaySelectTimer();\n this.delaySelectTimer = setTimeout(() => {\n this.cascaderService.setOptionActivated(option, columnIndex, performSelect);\n this.delaySelectTimer = undefined;\n }, 150);\n }\n\n private toggleSearchingMode(toSearching: boolean): void {\n if (this.inSearchingMode !== toSearching) {\n this.cascaderService.toggleSearchingMode(toSearching);\n }\n\n if (this.inSearchingMode) {\n this.cascaderService.prepareSearchOptions(this.inputValue);\n }\n }\n\n isOptionActivated(option: NzCascaderOption, index: number): boolean {\n const activeOpt = this.cascaderService.activatedOptions[index];\n return activeOpt === option;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.nzDisabled = (this.isNzDisableFirstChange && this.nzDisabled) || isDisabled;\n this.isNzDisableFirstChange = false;\n if (this.nzDisabled) {\n this.closeMenu();\n }\n }\n\n closeMenu(): void {\n this.blur();\n this.clearDelayMenuTimer();\n this.setMenuVisible(false);\n }\n\n /**\n * Reposition the cascader panel. When a menu opens, the cascader expands\n * and may exceed the boundary of browser's window.\n */\n private reposition(): void {\n if (this.overlay && this.overlay.overlayRef && this.menuVisible) {\n Promise.resolve().then(() => {\n this.overlay.overlayRef.updatePosition();\n this.cdr.markForCheck();\n });\n }\n }\n\n /**\n * When a cascader options is changed, a child needs to know that it should re-render.\n */\n private checkChildren(): void {\n if (this.cascaderItems) {\n this.cascaderItems.forEach(item => item.markForCheck());\n }\n }\n\n private setDisplayLabel(): void {\n const selectedOptions = this.cascaderService.selectedOptions;\n const labels: string[] = selectedOptions.map(o => this.cascaderService.getOptionLabel(o));\n\n if (this.isLabelRenderTemplate) {\n this.labelRenderContext = { labels, selectedOptions };\n }\n this.labelRenderText = defaultDisplayRender.call(this, labels);\n }\n\n private setDropdownStyles(): void {\n const firstColumn = this.cascaderService.columns[0];\n\n this.shouldShowEmpty =\n (this.inSearchingMode && (!firstColumn || !firstColumn.length)) || // Should show empty when there's no searching result\n (!(this.nzOptions && this.nzOptions.length) && !this.nzLoadData); // Should show when there's no options and developer does not use nzLoadData\n this.dropdownHeightStyle = this.shouldShowEmpty ? 'auto' : '';\n\n if (this.input) {\n this.dropdownWidthStyle =\n this.inSearchingMode || this.shouldShowEmpty ? `${this.selectContainer.nativeElement.offsetWidth}px` : '';\n }\n }\n\n private setStatusStyles(status: NzValidateStatus, hasFeedback: boolean): void {\n // set inner status\n this.status = status;\n this.hasFeedback = hasFeedback;\n this.cdr.markForCheck();\n // render status if nzStatus is set\n this.statusCls = getStatusClassNames(this.prefixCls, status, hasFeedback);\n Object.keys(this.statusCls).forEach(status => {\n if (this.statusCls[status]) {\n this.renderer.addClass(this.elementRef.nativeElement, status);\n } else {\n this.renderer.removeClass(this.elementRef.nativeElement, status);\n }\n });\n }\n\n private setLocale(): void {\n this.locale = this.i18nService.getLocaleData('global');\n this.cdr.markForCheck();\n }\n\n private scrollToActivatedOptions(): void {\n // The `scrollIntoView` is a native DOM API, which doesn't require Angular to run\n // a change detection when a promise microtask is resolved.\n this.ngZone.runOutsideAngular(() => {\n Promise.resolve().then(() => {\n // scroll only until option menu view is ready\n this.cascaderItems\n .toArray()\n .filter(e => e.activated)\n .forEach(e => {\n e.nativeElement.scrollIntoView({ block: 'start', inline: 'nearest' });\n });\n });\n });\n }\n\n private setupChangeListener(): void {\n this.input$\n .pipe(\n s