2013-07-10 18 views
8

Ok, ho combattuto con questo problema per ore e che ha limitato la questione ad un semplice FiddleTwitter Bootstrap selezione typeahead non è vincolata da KnockoutJS

problema è che quando sto usando typeahead Twitter di bootstrap plug-in un input di testo e fare una selezione, il valore non si aggiorna nel ViewModel KnockoutJS. So che potrei farlo funzionare, ma ci deve essere qualcosa che mi manca qui.

Fondamentalmente quello che ho è:

Knockout Binding

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

Knockout 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"/> 

Il resto del materiale proviene semplicemente da Twitter Bootstrap.

+1

Io non sono l'autore del legame. L'associazione originale è qui: https://github.com/billpull/knockout-bootstrap Mi chiedo se mi sia sfuggito qualcosa nell'uso ??? –

risposta

4

Una soluzione è quella di modificare la vostra funzione updater in cui è necessario per afferrare il osservabile utilizzato nel value legame e aggiornarlo con la funzione paramter:

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

Demo JSFiddle.

Se non sei autore del binding di quanto tu possa utilizzare l'opzione updater per specificare la funzione updater

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

Poiché lo updater deve restituire l'elemento selezionato, la sintassi di questo non è così buona.

roba Demo JSFiddle.

+0

Ok, sembra funzionare bene. Puoi spiegare perché funziona contro la precedente implementazione? Non sono l'autore del binding a eliminazione diretta. Mi piacerebbe capire cosa ha pensato l'autore originale quando ha inserito allBindings.updater nel programma di aggiornamento e se esiste un modo "corretto" per utilizzare l'associazione. Forse qualcosa che mi manca nell'HTML? Il plug-in originale è qui: https://github.com/billpull/knockout-bootstrap –

+0

Non sapevo che questa associazione fosse in una libreria. Ho modificato la mia risposta per lavorare con il codice originale. Per quanto riguarda il motivo per cui l'autore ha progettato l'API in questo modo, non ne ho idea. Non ero in grado di trovare alcun esempio su github che utilizza l'opzione "updater". Quindi direi che questo è un "bug di progettazione API" nella libreria perché stiamo parlando della libreria ko-bootstrap che dovrebbe essere usata in modo più idiomatico da KO. Quindi potrei immaginare qualcosa del genere come la corretta implementazione: http://jsfiddle.net/wTRhF/ – nemesv

+0

Penso che l'implementazione originale funzioni bene con le funzioni piuttosto che con il binding a valore semplice. Fondamentalmente la funzione di aggiornamento fa quello che deve fare con il valore selezionato, qui è da assegnare a un ko osservabile. la soluzione di nemesv è una scorciatoia di questo. –

3

Questo aggiornamento non ha funzionato per me, ecco cosa ha fatto

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

preferisco mantenere la mia lista suggerimenti di completamento automatico separato da Knockout del tutto. Voglio solo che Knockout sappia quando un utente ha effettivamente inserito un valore.

Questo è più vicino alla tecnica dell'utente2576666, poiché utilizza Typeahead's custom events per forzare un aggiornamento nel modello Knockout quando c'è una selezione o un completamento automatico. Tuttavia, non richiede un binding personalizzato typeahead e non richiede che i valori vengano archiviati nel ViewModel Knockout. Ciò apre la possibilità di completamento più personalizzabile lungo la linea (come l'uso di Bloodhound), che diventerebbe inutilmente faticoso se provassimo a memorizzarlo nel modello Knockout. My ViewModel non è sicuramente il posto giusto dove archiviare le opzioni di autocompletamento per il mio caso d'uso (e, vorrei suggerire, molte altre - in particolare se hai una lista potenzialmente ampia che ha bisogno di una popolazione dinamica).IMO questa versione è anche più facile da capire:

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 

ho, naturalmente, salvato questo come a JSFiddle:

Problemi correlati