Подтвердить что ты не робот

Как создать автоматическое заполнение combobox?

Кто-нибудь знает, как создать автообновление автозаполнения с шаблонами Jock для новичков?

У меня есть следующий шаблон:

<script type="text/html" id="row-template">
<tr>
...
    <td>         
        <select class="list" data-bind="options: SomeViewModelArray, 
                                        value: SelectedItem">
        </select>
    </td>
...        
<tr>
</script>

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

Кроме того, jQuery.Autocomplete требует поля ввода. Любые идеи?

4b9b3361

Ответ 1

Вот привязка автозаполнения пользовательского интерфейса jQuery, которую я написал. Он предназначен для зеркального отображения парадигмы привязки options, optionsText, optionsValue, value, используемой с элементами выбора, с несколькими дополнениями (вы можете запросить параметры через AJAX, и вы можете отличить то, что отображается на входе в поле, которое отображается в появившемся окне выбора.

Вам не нужно предоставлять все параметры. Он выберет для вас значения по умолчанию.

Вот пример без функции AJAX: http://jsfiddle.net/rniemeyer/YNCTY/

Вот тот же пример с кнопкой, которая делает его более похожим на поле со списком: http://jsfiddle.net/rniemeyer/PPsRC/

Вот пример с параметрами, полученными через AJAX: http://jsfiddle.net/rniemeyer/MJQ6g/

//jqAuto -- main binding (should contain additional options to pass to autocomplete)
//jqAutoSource -- the array to populate with choices (needs to be an observableArray)
//jqAutoQuery -- function to return choices (if you need to return via AJAX)
//jqAutoValue -- where to write the selected value
//jqAutoSourceLabel -- the property that should be displayed in the possible choices
//jqAutoSourceInputValue -- the property that should be displayed in the input box
//jqAutoSourceValue -- the property to use for the value
ko.bindingHandlers.jqAuto = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
        var options = valueAccessor() || {},
            allBindings = allBindingsAccessor(),
            unwrap = ko.utils.unwrapObservable,
            modelValue = allBindings.jqAutoValue,
            source = allBindings.jqAutoSource,
            query = allBindings.jqAutoQuery,
            valueProp = allBindings.jqAutoSourceValue,
            inputValueProp = allBindings.jqAutoSourceInputValue || valueProp,
            labelProp = allBindings.jqAutoSourceLabel || inputValueProp;

        //function that is shared by both select and change event handlers
        function writeValueToModel(valueToWrite) {
            if (ko.isWriteableObservable(modelValue)) {
               modelValue(valueToWrite );  
            } else {  //write to non-observable
               if (allBindings['_ko_property_writers'] && allBindings['_ko_property_writers']['jqAutoValue'])
                        allBindings['_ko_property_writers']['jqAutoValue'](valueToWrite );    
            }
        }

        //on a selection write the proper value to the model
        options.select = function(event, ui) {
            writeValueToModel(ui.item ? ui.item.actualValue : null);
        };

        //on a change, make sure that it is a valid value or clear out the model value
        options.change = function(event, ui) {
            var currentValue = $(element).val();
            var matchingItem =  ko.utils.arrayFirst(unwrap(source), function(item) {
               return unwrap(item[inputValueProp]) === currentValue;  
            });

            if (!matchingItem) {
               writeValueToModel(null);
            }    
        }

        //hold the autocomplete current response
        var currentResponse = null;

        //handle the choices being updated in a DO, to decouple value updates from source (options) updates
        var mappedSource = ko.dependentObservable({
            read: function() {
                    mapped = ko.utils.arrayMap(unwrap(source), function(item) {
                        var result = {};
                        result.label = labelProp ? unwrap(item[labelProp]) : unwrap(item).toString();  //show in pop-up choices
                        result.value = inputValueProp ? unwrap(item[inputValueProp]) : unwrap(item).toString();  //show in input box
                        result.actualValue = valueProp ? unwrap(item[valueProp]) : item;  //store in model
                        return result;
                });
                return mapped;                
            },
            write: function(newValue) {
                source(newValue);  //update the source observableArray, so our mapped value (above) is correct
                if (currentResponse) {
                    currentResponse(mappedSource());
                }
            }
        });

        if (query) {
            options.source = function(request, response) {  
                currentResponse = response;
                query.call(this, request.term, mappedSource);
            }
        } else {
            //whenever the items that make up the source are updated, make sure that autocomplete knows it
            mappedSource.subscribe(function(newValue) {
               $(element).autocomplete("option", "source", newValue); 
            });

            options.source = mappedSource();
        }

        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            $(element).autocomplete("destroy");
        });


        //initialize autocomplete
        $(element).autocomplete(options);
    },
    update: function(element, valueAccessor, allBindingsAccessor, viewModel) {
       //update value based on a model change
       var allBindings = allBindingsAccessor(),
           unwrap = ko.utils.unwrapObservable,
           modelValue = unwrap(allBindings.jqAutoValue) || '', 
           valueProp = allBindings.jqAutoSourceValue,
           inputValueProp = allBindings.jqAutoSourceInputValue || valueProp;

       //if we are writing a different property to the input than we are writing to the model, then locate the object
       if (valueProp && inputValueProp !== valueProp) {
           var source = unwrap(allBindings.jqAutoSource) || [];
           var modelValue = ko.utils.arrayFirst(source, function(item) {
                 return unwrap(item[valueProp]) === modelValue;
           }) || {};             
       } 

       //update the element with the value that should be shown in the input
       $(element).val(modelValue && inputValueProp !== valueProp ? unwrap(modelValue[inputValueProp]) : modelValue.toString());    
    }
};

Вы бы использовали его как:

<input data-bind="jqAuto: { autoFocus: true }, jqAutoSource: myPeople, jqAutoValue: mySelectedGuid, jqAutoSourceLabel: 'displayName', jqAutoSourceInputValue: 'name', jqAutoSourceValue: 'guid'" />

UPDATE: я поддерживаю версию этого связывания здесь: https://github.com/rniemeyer/knockout-jqAutocomplete

Ответ 2

Вот мое решение:

ko.bindingHandlers.ko_autocomplete = {
    init: function (element, params) {
        $(element).autocomplete(params());
    },
    update: function (element, params) {
        $(element).autocomplete("option", "source", params().source);
    }
};

Использование:

<input type="text" id="name-search" data-bind="value: langName, 
ko_autocomplete: { source: getLangs(), select: addLang }"/>

http://jsfiddle.net/7bRVH/214/ По сравнению с RP это очень простой, но, возможно, полный объем ваших потребностей.

Ответ 3

Требуется утилизация....

Оба этих решения великолепны (с Niemeyer гораздо более мелкозернистым), но они оба забывают об утилизации!

Они должны обрабатывать выбытия путем уничтожения автозаполнения jquery (предотвращения утечек памяти) следующим образом:

init: function (element, valueAccessor, allBindingsAccessor) {  
....  
    //handle disposal (if KO removes by the template binding)
    ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
        $(element).autocomplete("destroy");
    });
}

Ответ 4

Незначительные улучшения,

Прежде всего, это очень полезные советы, спасибо вам всем за обмен.

Я использую версию, опубликованную Epstone, со следующими улучшениями:

  • Отображайте метку (вместо значения) при нажатии вверх или вниз - видимо, это можно сделать, обработав событие фокусировки

  • Использование наблюдаемого массива в качестве источника данных (вместо массива)

  • Добавлен одноразовый обработчик, предложенный Джордж

http://jsfiddle.net/PpSfR/

...
conf.focus = function (event, ui) {
  $(element).val(ui.item.label);
  return false;
}
...

Btw, указав minLength, поскольку 0 позволяет отображать альтернативы, просто перемещая клавиши со стрелками без ввода какого-либо текста.

Ответ 5

Я попробовал решение Niemeyer с JQuery UI 1.10.x, но окно автозаполнения просто не появилось, после некоторого поиска я нашел простую обходную схему . Добавление следующего правила в конец файла jquery-ui.css устраняет проблему:

ul.ui-autocomplete.ui-menu {
  z-index: 1000;
}

Я также использовал Knockout-3.1.0, поэтому мне пришлось заменить ko.dependentObservable(...) на ko.computed(...)

Кроме того, если ваша модель KO View содержит некоторое числовое значение, убедитесь, что вы изменили операторы сравнения: от === до == и! == to! =, чтобы преобразование типа выполнялось.

Я надеюсь, что это поможет другим

Ответ 6

Исправлена ​​ошибка очистки входного сигнала для решения RP. Несмотря на то, что это косвенное решение, я изменил это в конце функции:

$(element).val(modelValue && inputValueProp !== valueProp ?
unwrap(modelValue[inputValueProp]) : modelValue.toString());

:

var savedValue = $(element).val();
$(element).val(modelValue && inputValueProp !== valueProp ?  unwrap(modelValue[inputValueProp]) : modelValue.toString());
if ($(element).val() == '') {
   $(element).val(savedValue);
}

Ответ 7

Решение Niemeyer отлично, однако я сталкиваюсь с проблемой при попытке использовать автозаполнение внутри модального. Autocomplete был уничтожен в модальном закрытом событии ( "Ошибка обмана": не удалось вызвать методы на автозаполнении до инициализации, попытался вызвать метод "option" ). Я исправил его, добавив две строки к методу подписки:

mappedSource.subscribe(function (newValue) {
    if (!$(element).hasClass('ui-autocomplete-input'))
         $(element).autocomplete(options);
    $(element).autocomplete("option", "source", newValue);
});

Ответ 8

Я знаю, что этот вопрос старый, но я также искал очень простое решение для нашей команды, используя это в форме, и узнал, что jQuery autocomplete вызывает событие autocompleteselect.

Это дало мне эту идею.

<input data-bind="value: text, valueUpdate:['blur','autocompleteselect'], jqAutocomplete: autocompleteUrl" />

Когда обработчик просто будет:

ko.bindingHandlers.jqAutocomplete = {
   update: function(element, valueAccessor) {
      var value = valueAccessor();

      $(element).autocomplete({
         source: value,
      });
   }    
}

Мне понравился этот подход, потому что он упрощает обработчик, и он не присоединяет события jQuery к моей модели viewmodel. Вот скрипка с массивом вместо URL-адреса в качестве источника. Это работает, если вы нажмете на текстовое поле, а также нажмите Enter.

https://jsfiddle.net/fbt1772L/3/

Ответ 9

Еще одна вариация оригинального решения Epstone.

Я попытался использовать его, но также обнаружил, что модель просмотра обновляется только тогда, когда значение вводилось вручную. Выбор записи автозаполнения оставил модель представления со старым значением, что немного беспокоит, потому что валидация все еще проходит - она ​​только при просмотре в базе данных вы видите проблему!

Используемый мной метод заключается в том, чтобы перехватить обработчик выбора компонента jquery UI в init init привязки нокаута, который просто обновляет модель нокаута при выборе значения. Этот код также включает в себя утилиту сантехника из Джорджа полезный ответ выше.

init: function (element, valueAccessor, allBindingsAccessor) {

        valueAccessor.select = function(event, ui) {
            var va = allBindingsAccessor();
            va.value(ui.item.value);
        }

        $(element).autocomplete(valueAccessor);

        //handle disposal (if KO removes by the template binding)
        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            $(element).autocomplete("destroy");
        });

    }
...
                    <input class="form-control" type="text" data-bind="value: ModelProperty, ko_autocomplete: { source: $root.getAutocompleteValues() }" />