AngularUI Select2 ajax:选择不更新ng-model

时间:2014-01-14 17:42:17

标签: javascript ajax angularjs angular-ui jquery-select2

我最近更新了Angular 1.2.0rc1到Angular 1.2.5的解决方案。

我们正在使用AngularUI为下拉列表提供Select2 ajax功能。在从Angular 1.2.0rc1更新之前,一切正常; Select2将处理ajax调用并加载下拉数据,选择一个对象将填充该表单元素。

现在我们已经更新到1.2.5,ajax功能仍在加载数据 - 但是选择不会更新附加到带有ui-select2绑定的输入的ng模型。因此,表单无法验证(因为该字段是必需的),用户无法继续。

我一直在研究这个,一个可能的解决方案是重写解决方案以使用常规select2下拉列表,使用<select><option data-ng-repeat="each in myModel">标记并使用Angular自己的AJAX功能将数据附加到myModel。但是,我预见到这个问题(处理滚动等),我正在寻找一个更简单,更快速的解决方案。

之前是否有人遇到此问题?任何人都可以对这种情况有所了解吗?

1 个答案:

答案 0 :(得分:2)

在测试过程中,我发现模型上有一个$scope.watch用于此表单输入。事实证明,watch函数将运行三次 - 第一次使用正确的对象值;第二次,用字符串“Object object”作为对象的表示;第三次,空值。我通过检查newValue数据类型来修改它 - 如果它是一个字符串,则将模型重置为旧值。然而,虽然这有效,但我仍然不确定为什么简单地更改库会导致这种退化。

如果我有时间,我打算尝试使用简化的测试用例重现这一点。

更新2:我找到this question,这解释了这不起作用的根本原因。因此,看起来可以在指令上设置优先级并调用render函数。

代码就是这样:

angular.module('ui.select2', []).value('uiSelect2Config', {}).directive('uiSelect2', ['uiSelect2Config', '$timeout',
    function (uiSelect2Config, $timeout) {
        var options = {};
        if (uiSelect2Config) {
            angular.extend(options, uiSelect2Config);
        }
        return {
            require: 'ngModel',
            priority: 1, // This fixed it.
            compile: function (tElm, tAttrs) {
                ......

我们在我们的解决方案中使用了这个,虽然它并不完美(在某些情况下仍然存在数据绑定的一些问题; Select2喜欢因某些原因而返回字符串而不是对象)我们已经能够使它工作。

更新:我认为我在AngularUI的select2.js中发现了潜在的问题。

source code将以下内容定义为convertToSelect2Model下select2指令的一部分:

if (controller) {
    // Watch the model for programmatic changes
    scope.$watch(tAttrs.ngModel, function (current, old) {
        if (!current) {
            return;
        }
        if (current === old) {
            return;
        }
        controller.$render();
    }, true);
    controller.$render = function () {
        if (isSelect) {
            elm.select2('val', controller.$viewValue);
        } else {
            if (opts.multiple) {
                var viewValue = controller.$viewValue;
                if (angular.isString(viewValue)) {
                    viewValue = viewValue.split(',');
                }
                elm.select2(
                    'data', convertToSelect2Model(viewValue));
            } else {
                if (angular.isObject(controller.$viewValue)) {
                    elm.select2('data', controller.$viewValue);
                } else if (!controller.$viewValue) {
                    elm.select2('data', null);
                } else {
                    elm.select2('val', controller.$viewValue);
                }
            }
        }
    };

在旧版Angular中,这一切都很好。但是,对于Angular 1.2.5,这不起作用; $render函数实际上已经由Angular定义,因此永远不会调用写入的函数。将controller.$render函数重命名为controller.$renderui可修复基础问题。这是我的修复:

if (controller) {
    controller.$renderui = function () {
        if (isSelect) {
            elm.select2('val', controller.$viewValue);
        } else {
            if (opts.multiple) {
                elm.select2(
                    'data', convertToSelect2Model(controller.$viewValue));
            } else {
                if (angular.isObject(controller.$viewValue)) {
                    elm.select2('data', controller.$viewValue);
                } else if (!controller.$viewValue) {
                    elm.select2('data', null);
                } else {
                    elm.select2('val', controller.$viewValue);
                }
            }
        }
    };

    // Watch the model for programmatic changes
    scope.$watch(tAttrs.ngModel, function (current, old) {
        if (!current) {
            return
        }
        if (current == old) {
            return
        }
        controller.$renderui();
    }, true)

这解决了我遇到的很多问题,我遇到了Select2(在我的项目中使用)和绑定到ng模型(现在,当ng模型更改时,Select2将正确更新),包括我原来的问题

TLDR:AngularUI select2尝试定义controller.$render,但该功能已由Angular 1.2.5内部定义,并且尝试重新定义它似乎不起作用。重命名功能似乎解决了这个问题。

我希望这有助于某人。