select2,ng-model和angular

Noy*_*Noy 4 angularjs jquery-select2 angularjs-ng-change jquery-select2-3

使用jquery-select2(不是 ui-select)和angular,我试图将值设置为ng-model.

我已经尝试使用$watchng-change,但似乎没有与选择2选择一个项目后开火.

不幸的是,我使用的是购买的模板,不能使用angular-ui.

HTML:

<input type="hidden" class="form-control select2remote input-medium"
    ng-model="contact.person.id"
    value="{{ contact.person.id }}"
    data-display-value="{{ contact.person.name }}"
    data-remote-search-url="api_post_person_search"
    data-remote-load-url="api_get_person"
    ng-change="updatePerson(contact, contact.person)">
Run Code Online (Sandbox Code Playgroud)

ClientController:

$scope.updatePerson = function (contact, person) {
    console.log('ng change');
    console.log(contact);
    console.log(person);
} // not firing

$scope.$watch("client", function () {
    console.log($scope.client);
}, true); // not firing either
Run Code Online (Sandbox Code Playgroud)

JQuery集成:

var handleSelect2RemoteSelection = function () {
    if ($().select2) {
        var $elements = $('input[type=hidden].select2remote');
        $elements.each(function(){
            var $this = $(this);
            if ($this.data('remote-search-url') && $this.data('remote-load-url')) {
                $this.select2({
                    placeholder: "Select",
                    allowClear: true,
                    minimumInputLength: 1,
                    ajax: { // instead of writing the function to execute the request we use Select2's convenient helper
                        url: Routing.generate($this.data('remote-search-url'), {'_format': 'json'}),
                        type: 'post',
                        dataType: 'json',
                        delay: 250,
                        data: function (term, page) {
                            return {
                                query: term, // search term
                            };
                        },
                        results: function (data, page) { // parse the results into the format expected by Select2.
                            return {
                                results: $.map(data, function (datum) {
                                    var result = {
                                        'id': datum.id,
                                        'text': datum.name
                                    };
                                    for (var prop in datum) {
                                        if (datum.hasOwnProperty(prop)) {
                                            result['data-' + prop] = datum[prop];
                                        }
                                    }
                                    return result;
                                })
                            }
                        }
                    },
                    initSelection: function (element, callback) {
                        // the input tag has a value attribute preloaded that points to a preselected movie's id
                        // this function resolves that id attribute to an object that select2 can render
                        // using its formatResult renderer - that way the movie name is shown preselected
                        var id = $(element).val(),
                            displayValue = $(element).data('display-value');
                        if (id && id !== "") {
                            if (displayValue && displayValue !== "") {
                                callback({'id': $(element).val(), 'text': $(element).data('display-value')});
                            } else {
                                $.ajax(Routing.generate($this.data('remote-load-url'), {'id': id, '_format': 'json'}), {
                                    dataType: "json"
                                }).done(function (data) {
                                    callback({'id': data.id, 'text': data.name});
                                });
                            }
                        }
                    },
                });
            }
        });
    }
};
Run Code Online (Sandbox Code Playgroud)

任何建议将不胜感激!:)

UPDATE

我已经成功地将一个看似同样重现问题的插件放在一起- 现在看起来好像只有在第一次更改值时才会触发ng-watch和$ watch事件.尽管如此,在我的代码中(当添加进一步的复杂性,如动态添加和从集合中删除)时,它似乎甚至不会触发一次.

再次,非常感谢正确方向(或任何方向)的指针!

Joe*_*ger 6

您的示例存在许多问题.我不确定我是否能够提供"答案",但希望以下建议和解释能帮到你.

首先,你正在"混合"jQuery和Angular.一般来说,这确实不起作用.例如:

在script.js中,您运行

$(document).ready(function() {
  var $elements = $('input[type=hidden].select2remote');
  $elements.each(function() {
     //...
  });
});
Run Code Online (Sandbox Code Playgroud)

当DOM最初准备就绪时,此代码将运行一次.它将使用当前位于DOM中的select2remote类选择隐藏的输入元素,并在其上初始化select2插件.

问题是在运行此函数后添加的任何新输入[type = hidden] .select2remote元素都不会被初始化.例如,如果要异步加载数据并填充ng-repeat,就会发生这种情况.

修复方法是将select2初始化代码移动到指令,并将此指令放在每个输入元素上.Abridged,这个指令看起来像:

.directive('select2', function() {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function(scope, element, attr, ngModel) {

            //$this becomes element

            element.select2({
                //options removed for clarity
            });

            element.on('change', function() {
                 console.log('on change event');
                 var val = $(this).value;
                 scope.$apply(function(){
                     //will cause the ng-model to be updated.
                     ngModel.setViewValue(val);
                 });
            });
            ngModel.$render = function() {
                 //if this is called, the model was changed outside of select, and we need to set the value
                //not sure what the select2 api is, but something like:
                element.value = ngModel.$viewValue;
            }

        }
    }
});
Run Code Online (Sandbox Code Playgroud)

我很抱歉,我不熟悉select2来了解获取和设置控件当前值的API.如果您在评论中提供给我,我可以修改示例.

您的标记将更改为:

<input select2 type="hidden" class="form-control select2remote input-medium"
    ng-model="contact.person.id"
    value="{{ contact.person.id }}"
    data-display-value="{{ contact.person.name }}"
    data-remote-search-url="api_post_person_search"
    data-remote-load-url="api_get_person"
    ng-change="updatePerson(contact, contact.person)">
Run Code Online (Sandbox Code Playgroud)

实现此指令后,您可以删除整个script.js.

在您的控制器中,您有以下内容:

$('.select2remote').on('change', function () {
  console.log('change');
  var value = $(this).value;
  $scope.$apply(function () {
      $scope.contact.person.id = value;
  });
});
Run Code Online (Sandbox Code Playgroud)

这里有两个问题:

首先,你在控制器中使用jQuery,你真的不应该这样做.
其次,这行代码将在控制器实例化时在DOM 中的整个应用程序中使用select2remote类触发每个元素的更改事件.

Angular添加的元素(即通过ng-repeat)可能不会在其上注册更改侦听器,因为实例化控制器之后(在下一个摘要周期),它们将被添加到DOM中.

此外,控制器范围之外的具有更改事件的元素将修改控制器的$ scope的状态.对此的解决方案是将此功能转移到指令中并依赖于ng-model功能.

请记住,只要你离开Angular的上下文(即如果你使用的是jQuery的$ .ajax功能),就必须使用scope.$ apply()来重新输入Angular的执行上下文.

我希望这些建议可以帮到你.