Skip to content

Commit 689fc72

Browse files
authored
fix(module:cascader): cannot update value when it is missing in options (#9124)
* fix(module:cascader): bugfix when ngModel value not existed in options * fix(module:cascader): accept request change
1 parent 925a6a5 commit 689fc72

3 files changed

Lines changed: 82 additions & 28 deletions

File tree

components/cascader/cascader-tree.service.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ export class NzCascaderTreeService extends NzTreeBaseService {
2222
label: 'label',
2323
value: 'value'
2424
};
25-
missingNodeList: NzTreeNode[] = [];
2625

2726
override treeNodePostProcessor = (node: NzTreeNode): void => {
2827
node.key = this.getOptionValue(node);
@@ -76,7 +75,6 @@ export class NzCascaderTreeService extends NzTreeBaseService {
7675
conductCheckPaths(paths: NzTreeNodeKey[][] | null, checkStrictly: boolean): void {
7776
this.checkedNodeList = [];
7877
this.halfCheckedNodeList = [];
79-
this.missingNodeList = [];
8078
const existsPathList: NzTreeNodeKey[][] = [];
8179
const calc = (nodes: NzTreeNode[]): void => {
8280
nodes.forEach(node => {
@@ -102,13 +100,13 @@ export class NzCascaderTreeService extends NzTreeBaseService {
102100
};
103101
calc(this.rootNodes);
104102
this.refreshCheckState(checkStrictly);
105-
this.missingNodeList = this.getMissingNodeList(paths, existsPathList);
103+
// handle missing node
104+
this.handleMissingNodeList(paths, existsPathList);
106105
}
107106

108-
conductSelectedPaths(paths: NzTreeNodeKey[][], isMulti: boolean): void {
107+
conductSelectedPaths(paths: NzTreeNodeKey[][]): void {
109108
this.selectedNodeList.forEach(node => (node.isSelected = false));
110109
this.selectedNodeList = [];
111-
this.missingNodeList = [];
112110
const existsPathList: NzTreeNodeKey[][] = [];
113111
const calc = (nodes: NzTreeNode[]): boolean =>
114112
nodes.every(node => {
@@ -118,21 +116,24 @@ export class NzCascaderTreeService extends NzTreeBaseService {
118116
node.isSelected = true;
119117
this.setSelectedNodeList(node);
120118
existsPathList.push(nodePath);
121-
if (!isMulti) {
122-
// if not support multi select
123-
return false;
124-
}
119+
return false;
125120
} else {
126121
node.isSelected = false;
127122
}
128123
if (node.children.length > 0) {
129-
// Recursion
130124
return calc(node.children);
131125
}
132126
return true;
133127
});
134128
calc(this.rootNodes);
135-
this.missingNodeList = this.getMissingNodeList(paths, existsPathList);
129+
this.handleMissingNodeList(paths, existsPathList);
130+
}
131+
132+
private handleMissingNodeList(paths: NzTreeNodeKey[][] | null, existsPathList: NzTreeNodeKey[][]): void {
133+
const missingNodeList = this.getMissingNodeList(paths, existsPathList);
134+
missingNodeList.forEach(node => {
135+
this.setSelectedNodeList(node);
136+
});
136137
}
137138

138139
private getMissingNodeList(paths: NzTreeNodeKey[][] | null, existsPathList: NzTreeNodeKey[][]): NzTreeNode[] {
@@ -165,6 +166,12 @@ export class NzCascaderTreeService extends NzTreeBaseService {
165166
node = childNode;
166167
}
167168

169+
if (this.isMultiple) {
170+
node.isChecked = true;
171+
node.isHalfChecked = false;
172+
} else {
173+
node.isSelected = true;
174+
}
168175
return node;
169176
}
170177
}

components/cascader/cascader.component.ts

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -815,23 +815,22 @@ export class NzCascaderComponent
815815
* @param shouldUpdateValue if false, only update selected nodes
816816
*/
817817
const updateNodesAndValue = (shouldUpdateValue: boolean): void => {
818-
this.selectedNodes = [
819-
...this.treeService.missingNodeList,
820-
...(this.nzMultiple ? this.getCheckedNodeList() : this.getSelectedNodeList())
821-
].sort((a, b) => {
822-
const indexA = value.indexOf(a.key);
823-
const indexB = value.indexOf(b.key);
824-
if (indexA !== -1 && indexB !== -1) {
825-
return indexA - indexB;
826-
}
827-
if (indexA !== -1) {
828-
return -1;
829-
}
830-
if (indexB !== -1) {
831-
return 1;
818+
this.selectedNodes = [...(this.nzMultiple ? this.getCheckedNodeList() : this.getSelectedNodeList())].sort(
819+
(a, b) => {
820+
const indexA = value.indexOf(a.key);
821+
const indexB = value.indexOf(b.key);
822+
if (indexA !== -1 && indexB !== -1) {
823+
return indexA - indexB;
824+
}
825+
if (indexA !== -1) {
826+
return -1;
827+
}
828+
if (indexB !== -1) {
829+
return 1;
830+
}
831+
return 0;
832832
}
833-
return 0;
834-
});
833+
);
835834
if (shouldUpdateValue) {
836835
this.cascaderService.values = this.selectedNodes.map(node =>
837836
this.getAncestorOptionList(node).map(o => this.cascaderService.getOptionValue(o))
@@ -857,7 +856,7 @@ export class NzCascaderComponent
857856
if (multiple) {
858857
this.treeService.conductCheckPaths(value, this.treeService.isCheckStrictly);
859858
} else {
860-
this.treeService.conductSelectedPaths(value, multiple);
859+
this.treeService.conductSelectedPaths(value);
861860
}
862861
};
863862

components/cascader/cascader.spec.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,6 +1689,18 @@ describe('cascader', () => {
16891689
expect(element.classList.contains('ant-select-dropdown-placement-topLeft')).toBe(false);
16901690
expect(element.classList.contains('ant-select-dropdown-placement-topRight')).toBe(true);
16911691
}));
1692+
1693+
it('should cascade work when the value of ngModel that is not existed in options', fakeAsync(() => {
1694+
fixture.detectChanges();
1695+
testComponent.values = ['zhejiang', 'a'];
1696+
testComponent.cascader.setMenuVisible(true);
1697+
fixture.detectChanges();
1698+
getItemAtColumnAndRow(1, 1)!.click();
1699+
getItemAtColumnAndRow(2, 1)!.click();
1700+
getItemAtColumnAndRow(3, 1)!.click();
1701+
fixture.detectChanges();
1702+
expect(testComponent.values).toEqual(['zhejiang', 'hangzhou', 'xihu']);
1703+
}));
16921704
});
16931705

16941706
describe('multiple', () => {
@@ -1866,6 +1878,42 @@ describe('cascader', () => {
18661878
fixture.detectChanges();
18671879
expect(testComponent.onChanges).toHaveBeenCalledWith([['light']]);
18681880
}));
1881+
1882+
describe('should cascade work when the value of ngModel includes nodes that are not existed in options', () => {
1883+
it('should remove item work', fakeAsync(() => {
1884+
setValues(2);
1885+
testComponent.values![0] = ['light', 'a'];
1886+
tick();
1887+
fixture.detectChanges();
1888+
const removeBtn = cascader.queryAll(By.css('.ant-select-selection-item-remove'))[0];
1889+
removeBtn.nativeElement.click();
1890+
fixture.detectChanges();
1891+
const tags = cascader.queryAll(By.directive(NzSelectItemComponent));
1892+
expect(tags.length).toBe(1);
1893+
}));
1894+
1895+
it('should add item work', fakeAsync(() => {
1896+
spyOn(testComponent, 'onChanges');
1897+
setValues(2);
1898+
testComponent.values![0] = ['light', 'a'];
1899+
console.log(testComponent.values);
1900+
tick();
1901+
fixture.detectChanges();
1902+
cascader.componentInstance.setMenuVisible(true);
1903+
fixture.detectChanges();
1904+
const checkbox = getCheckboxAtColumnAndRow(2, 3)!;
1905+
checkbox.click();
1906+
fixture.detectChanges();
1907+
expect(testComponent.values!.length).toBe(3);
1908+
const selectedNodes = [
1909+
['light', 1],
1910+
['light', 'a'],
1911+
['light', 2]
1912+
];
1913+
expect(testComponent.values).toEqual(selectedNodes);
1914+
expect(testComponent.onChanges).toHaveBeenCalledWith(selectedNodes);
1915+
}));
1916+
});
18691917
});
18701918

18711919
describe('load data lazily', () => {

0 commit comments

Comments
 (0)