Выбор шрифта Twitter Bootstrap, не связанный KnockoutJS

Хорошо, я боролся с этой проблемой уже несколько часов и сузил проблему до очень простой Fiddle.

Проблема в том, что когда я использую плагин twitter bootstrap typeahead для ввода текста и делаю выбор, значение не обновляется в модели представления KnockoutJS. Я знаю, что мог бы взломать его, чтобы он работал, но здесь должно быть что-то, чего мне не хватает.

В основном, что у меня есть:

Связывание нокаута

// 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
                });
    }
};

Отклоняющая модель представления

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.


person Jani Hyytiäinen    schedule 10.07.2013    source источник
comment
Я не автор привязки. Исходная привязка находится здесь: github.com/billpull/knockout-bootstrap Интересно, не пропустил ли я что-то в использовании???   -  person Jani Hyytiäinen    schedule 10.07.2013


Ответы (3)


Одним из решений является изменение вашей функции updater, где вам нужно получить наблюдаемый объект, используемый в привязке value, и обновить его с помощью параметра функции:

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

Демонстрация JSFiddle.

Если вы не являетесь автором привязки, вы можете использовать параметр updater, чтобы указать функцию updater.

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

Поскольку updater должен возвращать выбранный элемент, синтаксис этого не так хорош.

Демонстрация JSFiddle.

person nemesv    schedule 10.07.2013
comment
Хорошо, кажется, это работает нормально. Можете ли вы объяснить, почему это работает по сравнению с предыдущей реализацией? Я не автор нокаутирующей привязки. Я хотел бы понять, что думал первоначальный автор, когда он помещал allBindings.updater в апдейтер, и есть ли «правильный» способ использования привязки. Может быть, я что-то упускаю в HTML? Оригинальный плагин находится здесь: github.com/billpull/knockout-bootstrap - person Jani Hyytiäinen; 10.07.2013
comment
Я не знал, что эта привязка находится в библиотеке. Я изменил свой ответ, чтобы он работал с исходным кодом. Что касается того, почему автор разработал API таким образом, я понятия не имею. Мне не удалось найти на github ни одного примера, в котором используется параметр updater. Поэтому я бы сказал, что это ошибка дизайна API в библиотеке, потому что мы говорим о библиотеке ko-bootstrap, которая должна более идиоматически использовать KO. Таким образом, в качестве правильной реализации можно представить что-то вроде этого: jsfiddle.net/wTRhF - person nemesv; 10.07.2013
comment
Я думаю, что исходная реализация хорошо работает с функциями, а не с простой привязкой значений. В основном функция обновления делает то, что ей нужно, с выбранным значением, здесь она назначается наблюдаемому ko. решение nemesv - это короткий путь. - person Dhana Krishnasamy; 10.07.2013
comment
Здорово! Теперь я понял! Мне действительно нравится понимать, почему вещи не работают, а не просто заставлять их работать. Однако мне не нравится идея функций. Предположим, мне нужно найти способ заставить эту привязку работать более автоматически. - person Jani Hyytiäinen; 10.07.2013

Я предпочитаю, чтобы мой список предложений автозаполнения был полностью отделен от Knockout. Я хочу, чтобы Knockout знал только тогда, когда пользователь действительно ввел значение.

Это ближе к методике user2576666, поскольку использует Typeahead. настраиваемые события для принудительного обновления модели Knockout при выборе или автозаполнении. Однако для этого не требуется настраиваемая привязка с опережением ввода и не требуется, чтобы значения сохранялись в Knockout ViewModel. Это открывает возможности для более настраиваемого завершения в будущем (например, с использованием Bloodhound), что было бы излишне громоздко, если бы мы попытались сохранить его в модели Knockout. Моя ViewModel определенно не является подходящим местом для хранения параметров автозаполнения для моего варианта использования (и, я бы посоветовал, многих других, особенно если у вас есть потенциально большой список, который требует динамического заполнения). ИМО эту версию также легче понять:

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 

Я, конечно, сохранил это как JSFiddle:

person Andy MacKinlay    schedule 28.07.2014

Это средство обновления не сработало для меня, вот что помогло

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);
                });
    }
};
person user2576666    schedule 14.10.2013