简体   繁体   English

select2,ng-model和angular

[英]select2, ng-model and angular

Using jquery-select2 ( not ui-select) and angular, I'm trying to set the value to the ng-model. 使用jquery-select2不是 ui-select)和angular,我试图将值设置为ng-model。

I've tried using $watch and ng-change , but none seem to fire after selecting an item with select2. 我尝试过使用$watchng-change ,但是在选择带有select2的项目之后似乎没有。

Unfortunately, I am using a purchased template and cannot use angular-ui. 不幸的是,我使用的是购买的模板,不能使用angular-ui。

HTML: 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)">

ClientController: 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

JQuery integration: 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});
                                });
                            }
                        }
                    },
                });
            }
        });
    }
};

Any advice would be greatly appreciated! 任何建议将不胜感激! :) :)

UPDATE UPDATE

I've managed to put together a plunk which seems to similarly reproduce the problem - it now appears as if the ng-watch and the $watch events are fired only when first changing the value. 我已经成功地将一个看似同样重现问题的插件放在一起 - 现在看起来好像只有在第一次更改值时才会触发ng-watch和$ watch事件。 Nevertheless, in my code (and when adding further complexity like dynamically adding and removing from the collection), it doesn't even seem to fire once. 尽管如此,在我的代码中(当添加进一步的复杂性,如动态添加和从集合中删除)时,它似乎甚至不会触发一次。

Again, pointers in the right direction (or in any direction really) would be greatly appreciated! 再次,非常感谢正确方向(或任何方向)的指针!

There are a number of issues with your example. 您的示例存在许多问题。 I'm not sure I am going to be able to provide an "answer", but hopefully the following suggestions and explanations will help you out. 我不确定我是否能够提供“答案”,但希望以下建议和解释能帮到你。

First, you are "mixing" jQuery and Angular. 首先,你正在“混合”jQuery和Angular。 In general, this really doesn't work. 一般来说,这确实不起作用。 For example: 例如:

In script.js, you run 在script.js中,您运行

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

This code is going to run once, when the DOM is initially ready. 当DOM最初准备就绪时,此代码将运行一次。 It will select hidden input elements with the select2remote class that are currently in the DOM and initialized the select2 plugin on them. 它将使用当前位于DOM中的select2remote类选择隐藏的输入元素,并在其上初始化select2插件。

The problem is that any new input[type=hidden].select2remote elements added after this function is run will not be initialized at all. 问题是在运行此函数后添加的任何新输入[type = hidden] .select2remote元素都不会被初始化。 This would happen if you are loading data asynchronously and populating an ng-repeat, for example. 例如,如果要异步加载数据并填充ng-repeat,就会发生这种情况。

The fix is to move the select2 initialization code to a directive, and place this directive on each input element. 修复方法是将select2初始化代码移动到指令,并将此指令放在每个输入元素上。 Abridged, this directive might look like: 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;
            }

        }
    }
});

I apologize that I'm not familiar enough with select2 to know the API for getting and setting the current value of the control. 我很抱歉,我不熟悉select2来了解获取和设置控件当前值的API。 If you provide that to me in a comment I can modify the example. 如果您在评论中提供给我,我可以修改示例。

Your markup would change to: 您的标记将更改为:

<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)">

After implementing this directive, you could remove the entirety of script.js. 实现此指令后,您可以删除整个script.js。

In your controller you have the following: 在您的控制器中,您有以下内容:

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

There are two problems here: 这里有两个问题:

First, you are using jQuery in a controller, which you really shouldn't do. 首先,你在控制器中使用jQuery,你真的不应该这样做。
Second, this line of code is going to fire a change event on every element with the select2remote class in the entire application that was in the DOM when the controller was instatiated . 其次,这行代码将在控制器实例化时在DOM中的整个应用程序中使用select2remote类触发每个元素的更改事件。

It is likely that elements added by Angular (ie through ng-repeat) will not have the change listener registered on them because they will be added to the DOM after the controller is instantiated (at the next digest cycle). Angular添加的元素(即通过ng-repeat)可能不会在其上注册更改侦听器,因为实例化控制器之后 (在下一个摘要周期),它们将被添加到DOM中。

Also, elements outside the scope of the controller that have change events will modify the state of the controller's $scope. 此外,控制器范围之外的具有更改事件的元素将修改控制器的$ scope的状态。 The solution to this, again, is to move this functionality into the directive and rely on ng-model functionality. 对此的解决方案是将此功能转移到指令中并依赖于ng-model功能。

Remember that anytime you leave Angular's context (ie if you are using jQuery's $.ajax functionality), you have to use scope.$apply() to reenter Angular's execution context. 请记住,只要你离开Angular的上下文(即如果你使用的是jQuery的$ .ajax功能),就必须使用scope。$ apply()来重新输入Angular的执行上下文。

I hope these suggestions help you out. 我希望这些建议可以帮到你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM