Twitter Bootstrap预先选择未受KnockoutJS约束

时间:2013-07-10 08:05:35

标签: twitter-bootstrap knockout.js bootstrap-typeahead

好的,我已经和这个问题争了好几个小时,并将问题缩小到一个非常简单的Fiddle

问题是,当我在文本输入上使用twitter bootstrap的typeahead插件并进行选择时,该值不会在KnockoutJS ViewModel中更新。我知道我可以破解它的工作,但必须有一些我在这里缺少的东西。

基本上我拥有的是:

淘汰赛绑定

// Bind twitter typeahead
ko.bindingHandlers.typeahead = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var $element = $(element);
        var allBindings = allBindingsAccessor();
        var typeaheadArr = ko.utils.unwrapObservable(valueAccessor());

        $element.attr("autocomplete", "off")
                .typeahead({
                    'source': typeaheadArr,
                    'minLength': allBindings.minLength,
                    'items': allBindings.items,
                    'updater': allBindings.updater
                });
    }
};

淘汰ViewModel

function MyModel(){
    var self = this;
    self.productName = ko.observable();
    self.availableProducts = ['One', 'Two', 'Three'];
}

ko.applyBindings(new MyModel());

HTML

<input type="text" data-bind="typeahead:availableProducts, value:productName"/>

其余的东西只是来自Twitter Bootstrap。

3 个答案:

答案 0 :(得分:4)

一种解决方案是修改updater函数,您需要获取value绑定中使用的observable并使用函数参数更新它:

'updater': function(item) {
    allBindings.value(item);
    return item;
}

演示JSFiddle.

如果您不是绑定的作者,则可以使用updater选项指定updater函数

data-bind="typeahead:availableProducts, 
           updater: function(item) { productName(item); return item; }"

因为updater应返回所选项目,所以语法不太好。

演示JSFiddle.

答案 1 :(得分:4)

我更喜欢将自动填充建议列表与Knockout完全分开。一旦用户实际输入了值,我只希望Knockout知道。

这更接近user2576666的技术,因为当有选择或自动完成时,它使用Typeahead's custom events强制Knockout模型中的更新。但是,它不需要预先键入自定义绑定,也不需要将值存储在Knockout ViewModel中。这为更多可自定义的完成开辟了空间(例如使用Bloodhound),如果我们试图将其存储在Knockout模型中,这将不必要地变得非常繁琐。我的ViewModel绝对不是为我的用例存储自动完成选项的正确位置(而且,我建议,还有许多其他选项 - 特别是如果你有可能需要动态填充的大型列表)。 IMO这个版本也更容易理解:

var availableProducts = ['One', 'Two', 'Three'];

var substringMatcher = function(strs) {
  return function findMatches(q, cb) {
    var matches, substrRegex;
    matches = [];
    substrRegex = new RegExp(q, 'i');
    $.each(strs, function(i, str) {
      if (substrRegex.test(str)) {
        matches.push({ value: str });
      }
    });
    cb(matches);
  };
};


function MyModel(){
    var self = this;
    self.productName = ko.observable();
}

var myModel = new MyModel();

ko.applyBindings(myModel);

var onUpdated = function($e, datum) {
    myModel.productName(datum.value);
};

$(".typeahead")
    .typeahead(
       {hint: true, minLength: 1, highlight: true},
       {displayKey: 'value', source: substringMatcher(availableProducts)})
    .on('typeahead:autocompleted', onUpdated)
    .on('typeahead:selected', onUpdated); // for knockoutJS 

我当然将其保存为a JSFiddle

答案 2 :(得分:3)

这个更新程序的东西对我不起作用,这是做了什么

ko.bindingHandlers.typeahead = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var $element = $(element);
        var allBindings = allBindingsAccessor();
        var typeaheadArr = ko.utils.unwrapObservable(valueAccessor());

        var updateValues = function (val) {
            allBindings.value(val);
        };

        $element.attr("autocomplete", "off")
                .typeahead({
                    'local': typeaheadArr,
                    'minLength': allBindings.minLength,
                    'items': allBindings.items,
                }).on('typeahead:selected', function (el, item) {
                    updateValues(item.value);
                }).on('typeahead:autocompleted', function (el, item) {
                    updateValues(item.value);
                });
    }
};