我为这个很棒的jquery插件jQuery-Select2创建了简单的自定义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>
它按预期工作,我的普通select
元素被select2
插件替换。
然而,有一个问题,有时它显示默认值,即Select Country
,尽管在下拉列表中正确选择模型值。
现在,如果我将$timeout
间隔从200
增加到某个高值,请说1500
,它可以正常工作,但会延迟指令的呈现。此外,我认为这不适合它,因为我的数据是通过ajax加载的。
我也尝试按如下方式更新指令,但是没有运气:
app.directive("select2",function($timeout,$parse){
return {
restrict: 'AC',
require: 'ngModel',
link: function(scope, element, attrs) {
var modelAccessor = $parse(attrs.ngModel);
$timeout(function() {
$(element).select2();
});
scope.$watch(modelAccessor, function (val) {
if(val) {
$(element).select2("val",val);
}
});
}
};
});
PS:我知道有类似的模块存在ui-select,但它需要<ui-select></ui-select>
形式的一些不同的标记,我的应用程序已经完全开发,我只想替换普通的选择框与select2。
那么请您指导我如何解决此问题并确保该指令与最新行为保持同步?
答案 0 :(得分:3)
我对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-options,所以在下一个摘要周期之前,选项不会在DOM中。这样做的问题是,如果您的应用程序稍后更改了国家/地区模型,则不会向控件添加新选项。
答案 1 :(得分:3)
Angular不希望第三方插件修改模型数据。我的猜测是基于你使用$ timeout的事实,Angular更新选项或模型和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>
请注意,仍然可以对指令进行一些清理,并且对输入有任何假设,例如&#34; in&#34;在select-options属性中。它也没有强制执行属性,但如果它们不存在就会失败。
另请注意,我已使用Select2第4版,el.val(i).trigger("change")
证明了这一点。如果使用旧版本,则可能需要还原一些内容。
以下是行动中的jsfiddle demo指令。
答案 2 :(得分:1)
它没有直接回答你的问题,但请接受它,因为有些人想要采用另一种方法,而不是坚持使用jQuery select2。
我已经为此目的建立了自己的,因为我不满足现有的那些并不完全遵循Angular原则,HTML-first。
它还处于早期阶段,但我认为所有功能都适用于所有现代浏览器。
https://github.com/allenhwkim/angular-autocomplete
这些是例子
答案 3 :(得分:0)
我试图重现你的问题,它似乎运作良好。这是我提出的小提琴:
您可能有不同的行为,具体取决于您使用的Angular和/或Select2的版本,您能指定吗?
此外,如果您想防止闪烁,请务必隐藏默认的<select>
标记,以便在弹出select2元素之前不显示任何内容。
这也是在我的jsfiddle中用CSS
完成的.form-control { width: 200px; opacity: 0 }