我想在这里描述的问题非常复杂,我知道我的英语远非完美,所以如果有人有足够的耐心阅读这篇文章,我将非常感激。
Ad rem:我有一个自定义指令(称为元数据),其中我使用“angularjs-dropdown-multiselect”模块:http://dotansimha.github.io/angularjs-dropdown-multiselect/来提供多选字段。它工作正常,但问题是:指令被多次调用(因为我需要这种类型的几个字段),其中一些字段是相互依赖的。例如,我有两个字段:“居住地”和“人物”。当用户在多选字段中选择一个或多个居住地点时,应该过滤掉一些“人物”字段选项以仅留下居住在所选地点的人。我无法做到正确:无论我尝试什么,过滤过程都会触发“无限$ digest循环”异常。
每个字段的可能选项列表都是从json数据文件(称为meta_data.json)中提取的,该文件具有以下格式:
[{"person": "JeJu1929", "living-place": "Zabrid'"},
{"person": "VM1996", "living-place": "Velykyj Bereznyj"},
{"person": "VB1957", "living-place": "Zabrid'"}]
因此,当用户选择'Zabrid'时,'Person'字段的选项列表应该包含两个条目:'JeJu1929'和'VB1957'。 用户所做的选择存储在单独的工厂函数(称为 queryKeeper )以及上述文件中的数据中。
整个过程的架构如下:
angularjs-dropdown-multiselect要求选项列表包含格式为{id: value, label: option-name}
的对象,因此在获取选项列表的过程中,每个选项都从字符串类型转换为所需的对象格式,所以 - 对于'生活场所'字段 - 我最终得到的选项列表如下:
[{id:'Zabrid',label:'Zabrid'},{id:'Velykyj Bereznyj',label:'Velykyj Bereznyj'}]
当用户在字段中选择一些选项时,此信息将转到工厂函数(并存储在变量 metaFields 中)
假设我们要根据用户在“生活场所”字段中所做的选择过滤掉“人物”字段选项。过滤器函数可以选择(来自 metaFields 变量),数据对象( metaValues )以及要缩小的选项列表(在我们的示例中包含三个选项) :“JeJu1929”,“VM1996”,“VB1957”)。如果用户在“居住地”字段中选择了“Zabrid”选项,则过滤器功能会抛出“VM1996”,因为其居住地不是“Zabrid”。
到目前为止,一切正常。但是当过滤器返回新的选项列表(它是旧列表的子集)时,$ digest循环会一次又一次地触发,最后抛出“无限$ digest循环”异常。我认为问题在于对象的比较(即使对象实际上是相同的,角度仍然认为它们是不同的),但事实并非如此:当为“人物”字段选项调用过滤器函数时,它作为参数,比方说,四个选项,并返回,比方说,两个。然后调用过滤器用于其他字段(如“居住地点”等),再次为“人物”调用 - 使用相同的参数(即四个选项,而不是两个),就像第一次一样。这令人困惑,我被困住了。
代码的相关部分:
档案:metadata.html(模板)
<div>
<div ng-if="meta.type == 'select'" ng-dropdown-multiselect="" options="values|matchValues:meta.name" selected-model="model" extra-settings="multiSettings" translation-texts="hint" checkboxes="true">
</div>
文件:metadata.js(我的自定义指令)
corpus.directive('metadata', ['queryKeeper', 'gettextCatalog', function(queryKeeper, gettextcatalog) {
return {
templateUrl: 'jsapp/languageQuery/metadata/metadata.html',
restrict: 'E',
scope: {
index: '=',
place: '='
},
controller: function($scope, gettextCatalog) {
// function obtains the list of all possible options for the field
getValues = function () {
values = [];
metadata = queryKeeper.getMetaValues ();
name = $scope.meta.name.slice (5);
for (i = 0; i < metadata.length; ++i)
if (typeof metadata[i][name] != 'unknown')
{
var tmp = angular.copy (metadata)[i];
tmp['name'] = name;
tmp['id'] = metadata[i][name];
tmp['label'] = tmp[name];
values.push (tmp);
}
values = values.slice().sort(function(a,b){return a[name] > b[name]}).reduce(function(a,b){if (a.length == 0 || a.slice(-1)[0][name] !== b[name]) a.push(b);return a;},[]);
return values;
}
$scope.meta = queryKeeper.getMeta($scope.index, $scope.place); // provides access to the field data, like field name, choices made etc.
$scope.values = getValues (); // actual options list (to be filtered)
$scope.model = []; // stores the user choices
$scope.multiSettings = {showCheckAll: false, buttonClasses: 'btn btn-default meta', dynamicTitle: false, scrollable: true, scrollableHeight: '150px'}; // angularjs-dropdown-multiselect settings
$scope.hint = {buttonDefaultText: $scope.meta.hint};
// watch function that sends the user choices to the factory function
$scope.$watchCollection ('model', function (newValue) {
queryKeeper.setMetaMulti ($scope.index, $scope.place, newValue);
});
}
};
}]);
文件:corpus.js(主模块)
corpus.factory('queryKeeper', ['loadLanguages', 'loadMetaData', function(loadLanguages, loadMetaData) {
var metaFields = [];
var metaValues = [];
loadMetaData.getMetaFile('settings/meta_data.json').then(function(response) {
metaValues = angular.fromJson(response);});
return {
getMeta: function(i, place) {
return metaFields[i * 3 + place];
},
setMetaMulti: function (i, place, item) {
metaFields[i * 3 + place].multiValue = [];
for (j = 0; j < item.length; ++j)
{
metaFields[i * 3 + place].multiValue.push (item[j].id);
}
return item;
},
}
}]);
corpus.filter ('matchValues', ['queryKeeper', function (queryKeeper) {
return function (values, name) {
metaSet = queryKeeper.getMetaAll ();
var tag = '';
if (name == 'meta_living-place')
tag = 'variety';
else if (name == 'meta_person')
tag = 'living-place';
else
return values;
console.log ('init:', name, values);
metadata = queryKeeper.getMetaValues ();
newValues = [];
var found = [];
for (i = 0; i < metaSet.length; ++i) {
if (metaSet[i].name == 'meta_' + tag) {
found = metaSet[i].multiValue;
break;
}
}
if (typeof found == 'undefined' || found.length == 0)
return values;
for (j = 0; j < values.length; ++j) {
for (i = 0; i < found.length; ++i)
if (found[i] == values[j][tag])
newValues.push (values[j]);
}
console.log ('new:', name, newValues);
return newValues;
}
}]);
我花了一段时间,但我创建了JSFiddle:https://jsfiddle.net/HB7LU/17604/。令我惊讶的是,在我的代码中是另一个我不知道的问题:在我的模块中,我使用了'ngAnimate'模块,它阻止了选择列表的过滤。当我删除此依赖项时,列表开始正常运行。我很好奇为什么。仍然存在无限$ digest循环问题。