在 AngularJS 中使用 Select2(使用 MetroUI CSS)的依赖 drop-down 不工作
Dependent drop-down using Select2 (with MetroUI CSS) in AngularJS is not working
Select2 与 MetroUI CSS 配合 AngularJS 效果很好。
但是当我尝试使用相同的依赖下拉列表时,我遇到了 child drop-down 列表的视图没有相应更新以更改 parent drop-down 列表的问题.
这是一个堆栈代码段,展示了我遇到的问题。
(function () {
"use strict";
var app = angular.module("app", []);
app.controller("AppCtrl", AppCtrl);
function AppCtrl() {
var vm = this;
vm.ContextData = {
selectParents: [{ "id": 1, "name": "item 1" },
{ "id": 2, "name": "item 2" },
{ "id": 3, "name": "item 3" },
{ "id": 4, "name": "item 4" }],
selectChildren: [{ "id": 1, "parentId": 1, "name": "1 based on item1" },
{ "id": 2, "parentId": 1, "name": "2 based on item1" },
{ "id": 3, "parentId": 1, "name": "3 based on item1" },
{ "id": 4, "parentId": 1, "name": "4 based on item1" },
{ "id": 5, "parentId": 1, "name": "5 based on item1" },
{ "id": 6, "parentId": 1, "name": "6 based on item1" },
{ "id": 7, "parentId": 1, "name": "7 based on item1" },
{ "id": 8, "parentId": 2, "name": "8 based on item2" },
{ "id": 9, "parentId": 2, "name": "9 based on item2" },
{ "id": 10, "parentId": 2, "name": "10 based on item2" },
{ "id": 11, "parentId": 2, "name": "11 based on item2" },
{ "id": 12, "parentId": 2, "name": "12 based on item2" },
{ "id": 13, "parentId": 3, "name": "13 based on item3" },
{ "id": 14, "parentId": 3, "name": "14 based on item3" },
{ "id": 15, "parentId": 3, "name": "15 based on item3" },
{ "id": 16, "parentId": 3, "name": "16 based on item3" },
{ "id": 17, "parentId": 4, "name": "17 based on item4" },
{ "id": 18, "parentId": 4, "name": "18 based on item4" }]
};
}
})();
<body ng-app="app" ng-controller="AppCtrl as vm">
<div class="flex-grid">
<div class="row">
<div class="cell colspan2 margin10">
<div class="input-control full-size" data-role="select" data-placeholder="Select a parent" data-allow-clear="true">
<select class="full-size" style="display:none" ng-model="vm.selectedParent" ng-options="item.name for item in vm.ContextData.selectParents">
<option value=""></option>
</select>
</div>
</div>
<div class="cell colspan2 margin10">
<div class="input-control full-size" data-role="select" data-placeholder="Select a child" data-allow-clear="true">
<select class="full-size" style="display:none" ng-model="vm.selectedChild" ng-options="item.name for item in vm.ContextData.selectChildren | filter:{parentId:vm.selectedParent.id}">
<option value=""></option>
</select>
</div>
</div>
</div>
</div>
<div class="flex-grid margin10">
<div class="row">
<div class="cell colspan2">
Selected Parent :
</div>
<div class="cell colspan3">
{{vm.selectedParent}}
</div>
</div>
<div class="row">
<div class="cell colspan2">
selected Child :
</div>
<div class="cell colspan3">
{{vm.selectedChild}}
</div>
</div>
</div>
</body>
<link href="https://cdn.rawgit.com/olton/Metro-UI-CSS/master/build/css/metro-responsive.min.css" rel="stylesheet"/>
<link href="https://cdn.rawgit.com/olton/Metro-UI-CSS/master/build/css/metro.min.css" rel="stylesheet"/>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.1/js/select2.min.js"></script>
<script src="https://cdn.rawgit.com/olton/Metro-UI-CSS/master/build/js/metro.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.min.js"></script>
重现步骤:
- Select'Item 1'在parentselect(parentselect的place-holder是'select a parent' )
- 在 "Selected parent" 部分显示 selected parent 的正确值。
- Child select 绑定了正确的 child 选项。
- Select 来自 child select 的任何选项。
- 在 "Selected child" 部分显示 selected child 的正确值。
- 在Parentselect,select'Item 4'.
- 在 "Selected parent" 部分显示 selected parent 的正确值。
- Child select 已绑定,但选项未更新,它仍然显示 parent Item1.
的选项
- Select child select 中的任何项目,在 "Selected Child" 部分显示 child 的正确值,但 child 下拉列表显示错误的项目!
因此我的问题是,即使 child select 获得了正确的绑定选项,它的选项列表视图也不会刷新。
[我尝试了 select2.val(") 但 child 下拉列表的选项视图仍然没有刷新。]
另一个问题是:如果我清除 Parent select 中的 selection,它不会清除 Child 中的 selection ] select.
这里的问题涉及打开下拉列表后 Select2 如何缓存选项,因为它会将它认为正确的数据对象保存到 <option>
,即使 AngularJS 实际上正在更改选项稍后的。结果,Select2 仍将显示缓存数据,而 AngularJS 将识别选项已更改,您最终会看到像这样的奇怪边缘情况。
目前没有解决此问题的已知方法,除了强制 AngularJS 以某种方式不重用相关下拉列表中的选项。
请参阅此 codepen.io 以使从属 drop-down 列表正常工作。
解决的要点是:
- 给child一个ID drop-down
- 然后提供一个ng-change事件给parentdrop-down
- 在 parent drop-down 的 ng-change 事件中,通过其 ID
搜索 child drop-down
- 然后调用child上的select2函数drop-down清空选中的值和html
- 不要忘记在 child drop-down 上触发更改事件。
所以在ng-change事件中会是这样的(childSelect2是child drop-down的ID):
var ele = angular.element('#childSelect2');
ele.html("").val("").trigger("change");
另请注意,在单击 parent drop-down 中的叉号时,我们正在清除 child drop-down 对于我们正在调用的任何更改 ele.html("").val("").trigger("change");
这是预料之中的,因为如果 parent 中没有选择,那么在 child 中进行选择就没有意义。
但有了这个我们还需要清除 child drop-down 的 ng-model 因此在 ng-change 函数中设置 child drop-down 的模型为空(vm.selectedChild 是 ng-model for child drop-down)
vm.selectedChild = null;
虽然我发现 child 模型设置为 null 应该在调用 ele.html("").val("").trigger("change");
之前进行,因此我将其作为 ng-change 事件中的第一条语句. [我不知道为什么这个 placement-order 很重要,但只有在这个改变之后它才有效:)]
希望回答对其他人有所帮助并节省他们的时间。
快速参考:
HTML代码:
<body ng-app="app" ng-controller="AppCtrl as vm">
<div class="flex-grid">
<div class="row">
<div class="cell colspan2 margin10">
<div class="input-control full-size" data-role="select" data-placeholder="Select a parent" data-allow-clear="true">
<select class="full-size" style="display:none" ng-model="vm.selectedParent" ng-options="item.name for item in vm.ContextData.selectParents" ng-change="vm.SetSelect2()">
<option value=""></option>
</select>
</div>
</div>
<div class="cell colspan2 margin10">
<div class="input-control full-size" data-role="select" data-placeholder="Select a child" data-allow-clear="true">
<select class="full-size" style="display:none" ng-model="vm.selectedChild" ng-options="item.name for item in vm.ContextData.selectChildren | filter:{parentId:vm.selectedParent.id}" id="childSelect2">
<option value=""></option>
</select>
</div>
</div>
</div>
</div>
<div class="flex-grid margin10">
<div class="row">
<div class="cell colspan2">
Selected Parent :
</div>
<div class="cell colspan3">
{{vm.selectedParent}}
</div>
</div>
<div class="row">
<div class="cell colspan2">
selected Child :
</div>
<div class="cell colspan3">
{{vm.selectedChild}}
</div>
</div>
</div>
</body>
JS代码(angular模块和控制器):
(function () {
"use strict";
var app = angular.module("app", []);
app.controller("AppCtrl", AppCtrl);
function AppCtrl() {
var vm = this;
vm.ContextData = {
selectParents: [{ "id": 1, "name": "item 1" },
{ "id": 2, "name": "item 2" },
{ "id": 3, "name": "item 3" },
{ "id": 4, "name": "item 4" }],
selectChildren: [{ "id": 1, "parentId": 1, "name": "1 based on item1" },
{ "id": 2, "parentId": 1, "name": "2 based on item1" },
{ "id": 3, "parentId": 1, "name": "3 based on item1" },
{ "id": 4, "parentId": 1, "name": "4 based on item1" },
{ "id": 5, "parentId": 1, "name": "5 based on item1" },
{ "id": 6, "parentId": 1, "name": "6 based on item1" },
{ "id": 7, "parentId": 1, "name": "7 based on item1" },
{ "id": 8, "parentId": 2, "name": "8 based on item2" },
{ "id": 9, "parentId": 2, "name": "9 based on item2" },
{ "id": 10, "parentId": 2, "name": "10 based on item2" },
{ "id": 11, "parentId": 2, "name": "11 based on item2" },
{ "id": 12, "parentId": 2, "name": "12 based on item2" },
{ "id": 13, "parentId": 3, "name": "13 based on item3" },
{ "id": 14, "parentId": 3, "name": "14 based on item3" },
{ "id": 15, "parentId": 3, "name": "15 based on item3" },
{ "id": 16, "parentId": 3, "name": "16 based on item3" },
{ "id": 17, "parentId": 4, "name": "17 based on item4" },
{ "id": 18, "parentId": 4, "name": "18 based on item4" }]
};
vm.SetSelect2 = function () {
////debugger;
vm.selectedChild = null;
var ele = angular.element('#childSelect2');
//ele.html("");
//ele.val("").trigger("change");
ele.html("").val("").trigger("change");
}
}
})();
Select2 与 MetroUI CSS 配合 AngularJS 效果很好。 但是当我尝试使用相同的依赖下拉列表时,我遇到了 child drop-down 列表的视图没有相应更新以更改 parent drop-down 列表的问题.
这是一个堆栈代码段,展示了我遇到的问题。
(function () {
"use strict";
var app = angular.module("app", []);
app.controller("AppCtrl", AppCtrl);
function AppCtrl() {
var vm = this;
vm.ContextData = {
selectParents: [{ "id": 1, "name": "item 1" },
{ "id": 2, "name": "item 2" },
{ "id": 3, "name": "item 3" },
{ "id": 4, "name": "item 4" }],
selectChildren: [{ "id": 1, "parentId": 1, "name": "1 based on item1" },
{ "id": 2, "parentId": 1, "name": "2 based on item1" },
{ "id": 3, "parentId": 1, "name": "3 based on item1" },
{ "id": 4, "parentId": 1, "name": "4 based on item1" },
{ "id": 5, "parentId": 1, "name": "5 based on item1" },
{ "id": 6, "parentId": 1, "name": "6 based on item1" },
{ "id": 7, "parentId": 1, "name": "7 based on item1" },
{ "id": 8, "parentId": 2, "name": "8 based on item2" },
{ "id": 9, "parentId": 2, "name": "9 based on item2" },
{ "id": 10, "parentId": 2, "name": "10 based on item2" },
{ "id": 11, "parentId": 2, "name": "11 based on item2" },
{ "id": 12, "parentId": 2, "name": "12 based on item2" },
{ "id": 13, "parentId": 3, "name": "13 based on item3" },
{ "id": 14, "parentId": 3, "name": "14 based on item3" },
{ "id": 15, "parentId": 3, "name": "15 based on item3" },
{ "id": 16, "parentId": 3, "name": "16 based on item3" },
{ "id": 17, "parentId": 4, "name": "17 based on item4" },
{ "id": 18, "parentId": 4, "name": "18 based on item4" }]
};
}
})();
<body ng-app="app" ng-controller="AppCtrl as vm">
<div class="flex-grid">
<div class="row">
<div class="cell colspan2 margin10">
<div class="input-control full-size" data-role="select" data-placeholder="Select a parent" data-allow-clear="true">
<select class="full-size" style="display:none" ng-model="vm.selectedParent" ng-options="item.name for item in vm.ContextData.selectParents">
<option value=""></option>
</select>
</div>
</div>
<div class="cell colspan2 margin10">
<div class="input-control full-size" data-role="select" data-placeholder="Select a child" data-allow-clear="true">
<select class="full-size" style="display:none" ng-model="vm.selectedChild" ng-options="item.name for item in vm.ContextData.selectChildren | filter:{parentId:vm.selectedParent.id}">
<option value=""></option>
</select>
</div>
</div>
</div>
</div>
<div class="flex-grid margin10">
<div class="row">
<div class="cell colspan2">
Selected Parent :
</div>
<div class="cell colspan3">
{{vm.selectedParent}}
</div>
</div>
<div class="row">
<div class="cell colspan2">
selected Child :
</div>
<div class="cell colspan3">
{{vm.selectedChild}}
</div>
</div>
</div>
</body>
<link href="https://cdn.rawgit.com/olton/Metro-UI-CSS/master/build/css/metro-responsive.min.css" rel="stylesheet"/>
<link href="https://cdn.rawgit.com/olton/Metro-UI-CSS/master/build/css/metro.min.css" rel="stylesheet"/>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.1/js/select2.min.js"></script>
<script src="https://cdn.rawgit.com/olton/Metro-UI-CSS/master/build/js/metro.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.min.js"></script>
重现步骤:
- Select'Item 1'在parentselect(parentselect的place-holder是'select a parent' )
- 在 "Selected parent" 部分显示 selected parent 的正确值。
- Child select 绑定了正确的 child 选项。
- Select 来自 child select 的任何选项。
- 在 "Selected child" 部分显示 selected child 的正确值。
- 在Parentselect,select'Item 4'.
- 在 "Selected parent" 部分显示 selected parent 的正确值。
- Child select 已绑定,但选项未更新,它仍然显示 parent Item1. 的选项
- Select child select 中的任何项目,在 "Selected Child" 部分显示 child 的正确值,但 child 下拉列表显示错误的项目!
因此我的问题是,即使 child select 获得了正确的绑定选项,它的选项列表视图也不会刷新。
[我尝试了 select2.val(") 但 child 下拉列表的选项视图仍然没有刷新。]
另一个问题是:如果我清除 Parent select 中的 selection,它不会清除 Child 中的 selection ] select.
这里的问题涉及打开下拉列表后 Select2 如何缓存选项,因为它会将它认为正确的数据对象保存到 <option>
,即使 AngularJS 实际上正在更改选项稍后的。结果,Select2 仍将显示缓存数据,而 AngularJS 将识别选项已更改,您最终会看到像这样的奇怪边缘情况。
目前没有解决此问题的已知方法,除了强制 AngularJS 以某种方式不重用相关下拉列表中的选项。
请参阅此 codepen.io 以使从属 drop-down 列表正常工作。
解决的要点是:
- 给child一个ID drop-down
- 然后提供一个ng-change事件给parentdrop-down
- 在 parent drop-down 的 ng-change 事件中,通过其 ID 搜索 child drop-down
- 然后调用child上的select2函数drop-down清空选中的值和html
- 不要忘记在 child drop-down 上触发更改事件。
所以在ng-change事件中会是这样的(childSelect2是child drop-down的ID):
var ele = angular.element('#childSelect2');
ele.html("").val("").trigger("change");
另请注意,在单击 parent drop-down 中的叉号时,我们正在清除 child drop-down 对于我们正在调用的任何更改 ele.html("").val("").trigger("change");
这是预料之中的,因为如果 parent 中没有选择,那么在 child 中进行选择就没有意义。 但有了这个我们还需要清除 child drop-down 的 ng-model 因此在 ng-change 函数中设置 child drop-down 的模型为空(vm.selectedChild 是 ng-model for child drop-down)
vm.selectedChild = null;
虽然我发现 child 模型设置为 null 应该在调用 ele.html("").val("").trigger("change");
之前进行,因此我将其作为 ng-change 事件中的第一条语句. [我不知道为什么这个 placement-order 很重要,但只有在这个改变之后它才有效:)]
希望回答对其他人有所帮助并节省他们的时间。
快速参考: HTML代码:
<body ng-app="app" ng-controller="AppCtrl as vm">
<div class="flex-grid">
<div class="row">
<div class="cell colspan2 margin10">
<div class="input-control full-size" data-role="select" data-placeholder="Select a parent" data-allow-clear="true">
<select class="full-size" style="display:none" ng-model="vm.selectedParent" ng-options="item.name for item in vm.ContextData.selectParents" ng-change="vm.SetSelect2()">
<option value=""></option>
</select>
</div>
</div>
<div class="cell colspan2 margin10">
<div class="input-control full-size" data-role="select" data-placeholder="Select a child" data-allow-clear="true">
<select class="full-size" style="display:none" ng-model="vm.selectedChild" ng-options="item.name for item in vm.ContextData.selectChildren | filter:{parentId:vm.selectedParent.id}" id="childSelect2">
<option value=""></option>
</select>
</div>
</div>
</div>
</div>
<div class="flex-grid margin10">
<div class="row">
<div class="cell colspan2">
Selected Parent :
</div>
<div class="cell colspan3">
{{vm.selectedParent}}
</div>
</div>
<div class="row">
<div class="cell colspan2">
selected Child :
</div>
<div class="cell colspan3">
{{vm.selectedChild}}
</div>
</div>
</div>
</body>
JS代码(angular模块和控制器):
(function () {
"use strict";
var app = angular.module("app", []);
app.controller("AppCtrl", AppCtrl);
function AppCtrl() {
var vm = this;
vm.ContextData = {
selectParents: [{ "id": 1, "name": "item 1" },
{ "id": 2, "name": "item 2" },
{ "id": 3, "name": "item 3" },
{ "id": 4, "name": "item 4" }],
selectChildren: [{ "id": 1, "parentId": 1, "name": "1 based on item1" },
{ "id": 2, "parentId": 1, "name": "2 based on item1" },
{ "id": 3, "parentId": 1, "name": "3 based on item1" },
{ "id": 4, "parentId": 1, "name": "4 based on item1" },
{ "id": 5, "parentId": 1, "name": "5 based on item1" },
{ "id": 6, "parentId": 1, "name": "6 based on item1" },
{ "id": 7, "parentId": 1, "name": "7 based on item1" },
{ "id": 8, "parentId": 2, "name": "8 based on item2" },
{ "id": 9, "parentId": 2, "name": "9 based on item2" },
{ "id": 10, "parentId": 2, "name": "10 based on item2" },
{ "id": 11, "parentId": 2, "name": "11 based on item2" },
{ "id": 12, "parentId": 2, "name": "12 based on item2" },
{ "id": 13, "parentId": 3, "name": "13 based on item3" },
{ "id": 14, "parentId": 3, "name": "14 based on item3" },
{ "id": 15, "parentId": 3, "name": "15 based on item3" },
{ "id": 16, "parentId": 3, "name": "16 based on item3" },
{ "id": 17, "parentId": 4, "name": "17 based on item4" },
{ "id": 18, "parentId": 4, "name": "18 based on item4" }]
};
vm.SetSelect2 = function () {
////debugger;
vm.selectedChild = null;
var ele = angular.element('#childSelect2');
//ele.html("");
//ele.val("").trigger("change");
ele.html("").val("").trigger("change");
}
}
})();