Bootstrap-multiselect: Ligação KnockoutJS ao valor do objeto (não um valor simples) ... Correção de hack incluída

Criado em 2 mai. 2015  ·  3Comentários  ·  Fonte: davidstutz/bootstrap-multiselect

Estou usando a ligação knockout select onde um objeto inteiro é ligado como o valor ... não apenas uma string.

bootstrap-multiselect não gostou disso porque usa o "valor" para relacionar seus itens da lista suspensa de volta à opção de seleção correspondente. Para fazer isso, você deve definir o atributo "optionsValue" em sua ligação knockout para que a opção tenha um atributo "value". No entanto, definir o atributo "optionsValue" tem o resultado esperado de definir apenas esse valor em meu selectedOptions observableArray ... não no objeto inteiro.

É bem possível que eu seja a única pessoa que usa a funcionalidade do knockout que permite vincular as opções a objetos inteiros, mas caso eu não seja ... aqui está a correção do hack:

Criou duas novas opções de bootstrap-multiselect chamadas "optionsKey" e "observableKey" para saber onde ir para obter um valor com o qual possa se relacionar:

<select id="mySelect" data-bind="options: myOptions, selectedOptions: mySelectedOptions, optionsText: 'myDisplayText', optionsAfterRender: SetOptionKey, multiselect: { optionsKey: 'data-key', observableKey: 'Key' }" multiple="multiple"></select>

Em meu modelo de visão nocauteada, defino o atributo referenciado por "optionsKey" em cada opção:

myVM.SetOptionKey = function ( option, item ) {
    ko.applyBindingsToNode( option, { attr: { "data-key": item.Key } }, item );
};

Agora mergulho no arquivo bootstrap-multiselect.js. A primeira coisa que tive que fazer aqui foi na parte superior, onde está adicionando o manipulador de inicialização do knockout.

init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
    ...
    if (allBindings.has('selectedOptions')) {
        var selectedOptions = allBindings.get('selectedOptions');
        if (ko.isObservable(selectedOptions)) {
            ko.computed({
                read: function () {
                    selectedOptions();

                    // Added to handle knockout binding to an object...not just a value
                    // multiselect needs the selected property on the options, else it wont show them on refresh
                    if ((config.optionsKey !== undefined) &&
                        (config.optionsKey !== null) &&
                        (config.optionsKey.length > 0) &&
                        (config.observableKey !== undefined) &&
                        (config.observableKey !== null) &&
                        (config.observableKey.length > 0)) {
                        var _optionsKey = config.optionsKey;
                        var _observableKey = config.observableKey;
                        ko.utils.arrayForEach(selectedOptions(), function (selectedOption) {
                            $("option[" + _optionsKey + "='" + selectedOption[_observableKey]() + "']", $element).prop('selected', true);
                        })
                    }

                    setTimeout(function () {
                        $element.multiselect('refresh');
                    }, 1);
                },
                disposeWhenNodeIsRemoved: element
            }).extend({ rateLimit: 100, notifyWhenChangesStop: true });
        }
    }
    ...

Em seguida, tive que modificar a função createOptionValue em bootstrap-multiselect.js para primeiro usar essa chave como o valor que ela define em seus itens de lista:

createOptionValue: function (element) {
    ...

    var value = $element.val();
    // Added to handle knockout binding to an object...not just a value
    if ((this.options.optionsKey !== undefined) && (this.options.optionsKey !== null) && (this.options.optionsKey.length > 0)) {
        var key = $element.attr(this.options.optionsKey);
        if ((key !== undefined) && (key !== null) && (key.length > 0)) {
            value = key;
        }
    }

    ...
}

Por último, tive que modificar a função getOptionByValue em bootstrap-multiselect.js para encontrar elementos de opção com base neste atributo em vez de valor:

getOptionByValue: function (value) {
    var valueToCompare = value.toString();

    // Added to handle knockout binding to an object...not just a value
    if ((this.options.optionsKey !== undefined) && (this.options.optionsKey !== null) && (this.options.optionsKey.length > 0)) {
        var opt = $("option[" + this.options.optionsKey + "='" + valueToCompare + "']", this.$select).first();
        if( (opt !== undefined) && (opt !== null) ) {
            return $(opt);
        }
    }

    var options = $('option', this.$select);

    for (var i = 0; i < options.length; i = i + 1) {
        var option = options[i];
        if (option.value === valueToCompare) {
            return $(option);
        }
    }
},
question

Todos 3 comentários

Obrigado, incluirá isso no FAQ! Tenho certeza de que isso também é útil para outros usuários.

Obrigado por esta dica - eu estava tentando realizar a ligação para selecionar objetos também e este artigo foi muito útil. A única coisa que tive que fazer além do código de @ APM3 também foi preencher o atributo "valor" das minhas opções na função SetOptionKey:

self.SetOptionKey = function (option, item) { ko.applyBindingsToNode(option, { attr: { "data-key": item.Value } }, item); ko.applyBindingsToNode(option, { attr: { "value": item.Value } }, item); }

Obrigado novamente por este artigo.

Consulte a essência para vinculação nocaute desse objeto com suporte. Basta substituir a ligação real do arquivo bootstrap-multiselect.js. Neste código, você não precisa colocar em sua VM para definir a opção.

//Just like this one
myVM.SetOptionKey = function ( option, item ) {
    ko.applyBindingsToNode( option, { attr: { "data-key": item.Key } }, item );
};

Eu adicionei o pré-processo às ligações para adicionar implicitamente optionsAfterRender .

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

donbonifacio picture donbonifacio  ·  7Comentários

fitucated picture fitucated  ·  6Comentários

andriijas picture andriijas  ·  8Comentários

mirroras picture mirroras  ·  3Comentários

echan00 picture echan00  ·  6Comentários