Javascript Angularjs自定义select2指令

Javascript Angularjs自定义select2指令,javascript,jquery,angularjs,angularjs-directive,jquery-select2,Javascript,Jquery,Angularjs,Angularjs Directive,Jquery Select2,我为这个很棒的jquery插件创建了简单的自定义AngularJs指令,如下所示: 指令 app.directive("select2",function($timeout,$parse){ return { restrict: 'AC', link: function(scope, element, attrs) { $timeout(function() { $(element).select2(

我为这个很棒的jquery插件创建了简单的自定义AngularJs指令,如下所示:

指令

app.directive("select2",function($timeout,$parse){
    return {
        restrict: 'AC',
        link: function(scope, element, attrs) {
            $timeout(function() {
                $(element).select2();
            },200); 
        }
    };
});
在HTML模板中的用法:

<select class="form-control" select2 name="country"
data-ng-model="client.primary_address.country"
ng-options="c.name as c.name for c in client.countries">
     <option value="">Select Country</option>
</select>
PS:我知道现在有类似的模块,但它需要一些不同的标记形式的
,我的应用程序已经完全开发好了,我只想用select2替换普通的选择框


因此,您能否指导我如何解决此问题,并确保指令与最新行为保持同步?

我对select2不太熟悉(因此获取和设置控件中显示值的实际API可能不正确),但我建议将此作为备选方案:

app.directive("select2",function($timeout){
    return {
        restrict: 'AC',
        require: 'ngModel',
        link: function(scope, element, attrs, model) {

            $timeout(function() {
                element.select2();
            });

            model.$render = function() {
                element.select2("val",model.$viewValue);
            }
            element.on('change', function() {
                scope.$apply(function() {
                    model.$setViewValue(element.select2("val"));
                });
            })
        }
    };
});

第一个$timeout是必需的,因为您使用的是ng选项,所以这些选项在下一个摘要周期之前不会出现在DOM中。问题在于,如果应用程序稍后更改了国家/地区模型,则不会向控件中添加新选项

它不能直接回答您的问题,但请接受它,因为有一些人希望采用另一种方法,而不是坚持使用jQuery select2

我已经建立了自己的这个目的,因为我不满足于现有的不完全遵循角度原则,HTML第一

这仍处于早期阶段,但我认为所有的功能都可以在所有现代浏览器中使用

这些都是例子


我试图重现您的问题,但似乎效果良好。这是我想出的小提琴:

app.directive("select2",function($timeout,$parse){
    return {
        restrict: 'AC',
        link: function(scope, element, attrs) {
            var options = [],
                el = $(element),
                angularTriggeredChange = false,
                selectOptions = attrs["selectOptions"].split(" in "),
                property = selectOptions[0],
                optionsObject = selectOptions[1];
            // watch for changes to the defining data model
            scope.$watch(optionsObject, function(n, o){
                var data = [];
                // format the options for select2 data interface
                for(var i in n) {
                    var obj = {id: i, text: n[i][property]};
                    data.push(obj);
                }
                el.select2({data: data});
                // keep local copy of given options
                options = n;
            }, true);
            // watch for changes to the selection data model
            scope.$watch(attrs["selectSelection"], function(n, o) {
                // select2 is indexed by the array position,
                // so we iterate to find the right index
                for(var i in options) {
                    if(options[i][property] === n) {
                        angularTriggeredChange = true;
                        el.val(i).trigger("change");
                    }
                }
            }, true);
            // Watch for changes to the select UI
            el.select2().on("change", function(e){
                // if the user triggered the change, let angular know
                if(!angularTriggeredChange) { 
                    scope.$eval(attrs["selectSelection"]+"='"+options[e.target.value][property]+"'");
                    scope.$digest();
                }
                // if angular triggered the change, then nothing to update
                angularTriggeredChange = false;
            });

        }
    };
});

根据您使用的Angular和/或Select2的版本,您可能会有不同的行为,您可以指定吗

另外,如果要防止闪烁,请确保隐藏默认的
标记,以便在select2元素弹出之前不显示任何内容

这也是在我的JSFIDLE和CSS中完成的

.form-control { width: 200px; opacity: 0 }

这可能比你想象的要简单

请看看这个

基本上,所有插件Angularjs$watch都需要基于某些东西。我不能100%肯定jQuery-select2;但我认为这只是控件的正常DOM事件。(对于Angular$手表,它是一个“脏检查循环”)

我的想法是让我们相信jquery-Select2和AngularJS能够处理这些更改事件

我们只需要观察Angular方式的变化,并以Select2的方式更新select

var refreshSelect=function(){
如果(!element.select2Initialized)返回;
$timeout(函数(){
元素。触发器(“更改”);
});
};
//...
范围$watch(attrs.ngModel,刷新选择);

注意:我添加了2块新手表,我想您会喜欢的

Angular不喜欢由第三方插件修改模型数据。我的猜测基于这样一个事实,即您使用$timeout时,在更新选项或模型与select2插件之间存在竞争条件。我提出的解决方案是,让Angular不必亲自更新,而是根据指令手动进行更新,这样无论是谁在修改,都可以确保所有内容都匹配。这是我提出的指令:

app.directive("select2",function($timeout,$parse){
    return {
        restrict: 'AC',
        link: function(scope, element, attrs) {
            var options = [],
                el = $(element),
                angularTriggeredChange = false,
                selectOptions = attrs["selectOptions"].split(" in "),
                property = selectOptions[0],
                optionsObject = selectOptions[1];
            // watch for changes to the defining data model
            scope.$watch(optionsObject, function(n, o){
                var data = [];
                // format the options for select2 data interface
                for(var i in n) {
                    var obj = {id: i, text: n[i][property]};
                    data.push(obj);
                }
                el.select2({data: data});
                // keep local copy of given options
                options = n;
            }, true);
            // watch for changes to the selection data model
            scope.$watch(attrs["selectSelection"], function(n, o) {
                // select2 is indexed by the array position,
                // so we iterate to find the right index
                for(var i in options) {
                    if(options[i][property] === n) {
                        angularTriggeredChange = true;
                        el.val(i).trigger("change");
                    }
                }
            }, true);
            // Watch for changes to the select UI
            el.select2().on("change", function(e){
                // if the user triggered the change, let angular know
                if(!angularTriggeredChange) { 
                    scope.$eval(attrs["selectSelection"]+"='"+options[e.target.value][property]+"'");
                    scope.$digest();
                }
                // if angular triggered the change, then nothing to update
                angularTriggeredChange = false;
            });

        }
    };
});
我已经添加了属性
select options
select model
。这些将用于使用select2的界面填充和更新数据。以下是一个示例html:

<select id="sel" class="form-control" select2 name="country"
  select-selection="client.primary_address.country" 
  select-options="name in client.countries" >
     <option value="">Select Country</option>
</select>
<div>Selected: {{client.primary_address.country}}</div>

选择国家
所选:{{client.primary_address.country}
请注意,仍然可以对该指令进行一些清理,并且输入中存在一些问题,例如选择选项属性中的“in”。它也不会强制执行属性,但如果属性不存在,则会失败

另外请注意,我使用了Select2版本4,这可以从
el.val(I).trigger(“change”)
中看出。如果使用旧版本,您可能必须还原某些内容


这是指令的作用。

这与
选择2
有关吗?如果您删除
select2
指令,并使其成为一个普通的select元素,它是否按预期工作?是的,如果我删除它,它将按预期工作。我还在应用程序中使用
select2
,但我使用的是Angular的包装器,现在已被弃用。Select2给我带来了很多痛苦,顺便说一句,我建议你尽量避免:)@OmriAharon使用
ui-Select2
比创建自己已经编写的库要好。@pankajparkar是的,我建议使用推荐的
ui-select
。用控制作为方法是行不通的,你能让它发挥作用并把它包括在你的答案中吗?请参见选择中的更改反映在范围变量中,但范围变量中的更改不反映在选择中您的PRUNKR中存在打字错误。您应该更改按钮的ng单击以包括cc。tooI强烈建议使用“change.select2”事件而不是“change”。我总是得到无限的变化事件循环,直到我使用这个解决方案,当模型是一个对象时,这个解决方案不起作用。