Come posso convincere Knockout JS a legare i dati con la pressione dei tasti invece di perdere il focus?


191

Questo esempio di knockout js funziona così quando si modifica un campo e si preme TAB, i dati del modello di visualizzazione e quindi il testo sotto i campi vengono aggiornati.

Come posso modificare questo codice in modo che i dati del modello di visualizzazione vengano aggiornati ad ogni pressione di tasti?

testo alternativo

<!doctype html>
<html>
    <title>knockout js</title>
    <head>
        <script type="text/javascript" src="js/knockout-1.1.1.debug.js"></script>
        <script type="text/javascript">
        window.onload= function() {

            var viewModel = {
                firstName : ko.observable("Jim"),
                lastName : ko.observable("Smith")
            };
            viewModel.fullName = ko.dependentObservable(function () {
                return viewModel.firstName() + " " + viewModel.lastName();
            });

            ko.applyBindings(viewModel);
       }
        </script>
    </head>
    <body>
        <p>First name: <input data-bind="value: firstName" /></p>
        <p>Last name: <input data-bind="value: lastName" /></p>
        <h2>Hello, <span data-bind="text: fullName"> </span>!</h2>
    </body>
</html>

9
In KO 3.2 non è necessario eseguire tutte queste soluzioni alternative: stackoverflow.com/a/25493265/1090562
Salvador Dali,

Salvador ha ragione: lo fa automaticamente nell'ultima versione.
vapcguy,

Risposte:


299
<body>
        <p>First name: <input data-bind="value: firstName, valueUpdate: 'afterkeydown'" /></p>
        <p>Last name: <input data-bind="value: lastName, valueUpdate: 'afterkeydown'" /></p>
        <h2>Hello, <span data-bind="text: fullName"> </span>!</h2>
</body>

Dalla documentazione

Parametri aggiuntivi

  • valueUpdate

    Se l'associazione include anche un parametro chiamato valueUpdate, definisce quale evento del browser KO deve utilizzare per rilevare le modifiche. I seguenti valori di stringa sono le opzioni più comunemente utili:

    • "modifica" (impostazione predefinita): aggiorna il modello di visualizzazione quando l'utente sposta lo stato attivo su un controllo diverso o, nel caso di elementi, immediatamente dopo qualsiasi modifica

    • "keyup": aggiorna il modello di visualizzazione quando l'utente rilascia una chiave

    • "keypress": aggiorna il modello di visualizzazione quando l'utente ha digitato una chiave. A differenza del keyup, questo si aggiorna ripetutamente mentre l'utente tiene premuto un tasto

    • "afterkeydown": aggiorna il modello di visualizzazione non appena l'utente inizia a digitare un carattere. Funziona rilevando l'evento keydown del browser e gestendo l'evento in modo asincrono.

Di queste opzioni, "afterkeydown" è la scelta migliore se si desidera mantenere aggiornato il modello di visualizzazione in tempo reale.


9
dai documenti in knockout.js: // La sintassi "after <eventname>" significa "esegui il gestore in modo asincrono dopo l'evento" // Questo è utile, ad esempio, per catturare eventi "keydown" dopo che il browser ha aggiornato il controllo
John Wright,

4
Esiste un modo per ottenere valueUpdate: 'afterkeydown' come predefinito ?
Aaron Shafovaloff,

2
per alcune caselle di input il browser mostra i valori di "compilazione automatica" (come se il campo di input sia denominato "firstname"). la selezione dal popolamento automatico non funziona con "afterkeydown". c'è un modo per catturarlo?
Anton,

2
@Anton Oltre a popolare automaticamente i valori, ci sono anche clic sullo schermo della tastiera ed eventi incolla del mouse che dovrebbero essere catturati. L'uso afterkeydownè una cattiva soluzione.
John Kurlak,

2
Autore di questa risposta, si prega di aggiornare per includere anche la risposta di Salvador Dalì, textInput aggiunto da 3.2 è una soluzione migliore in quanto ha una migliore compatibilità con i browser mobili (cose come il completamento automatico funzionano meglio)
Michael Crook

85

Nella versione 3.2 puoi semplicemente usare l'associazione textinput. :

<input data-bind="textInput: userName" />

Fa due cose importanti:

  • effettuare aggiornamenti immediati
  • gestisce le differenze del browser per tagliare, trascinare, completare automaticamente ...

Quindi non sono necessari moduli aggiuntivi, controlli personalizzati e altre cose.


Funziona davvero bene ed è esattamente ciò di cui avevo bisogno per sostituire il valore: field, valueUpdate: 'input change' che avevo avuto attraverso la mia app ... :) grazie.
Tod Thomson,

C'è un modo per catturare l'evento quando un utente fa clic sull'icona 'x' sul nuovo campo di input della ricerca HTML5 e agisce come nel caso in cui l'input sia vuoto?
Codrina Valo,

35

Se si desidera che esegua gli aggiornamenti su afterkeydown"per impostazione predefinita", è possibile iniettare l' valueUpdateassociazione nel valuegestore dell'associazione. Basta fornire un nuovo allBindingsAccessorche l'handler possa usare e che includa afterkeydown.

(function () {
    var valueHandler = ko.bindingHandlers.value;
    var getInjectValueUpdate = function (allBindingsAccessor) {
        var AFTERKEYDOWN = 'afterkeydown';
        return function () {
            var allBindings = ko.utils.extend({}, allBindingsAccessor()),
                valueUpdate = allBindings.valueUpdate;

            if (valueUpdate === undefined) {
                return ko.utils.extend(allBindings, { valueUpdate: AFTERKEYDOWN });
            } else if (typeof valueUpdate === 'string' && valueUpdate !== AFTERKEYDOWN) {
                return ko.utils.extend(allBindings, { valueUpdate: [valueUpdate, AFTERKEYDOWN] });
            } else if (typeof valueUpdate === 'array' && ko.utils.arrayIndexOf(valueUpdate, AFTERKEYDOWN) === -1) {
                valueUpdate = ko.utils.arrayPushAll([AFTERKEYDOWN], valueUpdate);
                return ko.utils.extend(allBindings, {valueUpdate: valueUpdate});
            }
            return allBindings;
        };
    };
    ko.bindingHandlers.value = {
        // only needed for init
        'init': function (element, valueAccessor, allBindingsAccessor) {
            allBindingsAccessor = getInjectValueUpdate(allBindingsAccessor);
            return valueHandler.init(element, valueAccessor, allBindingsAccessor);
        },
        'update': valueHandler.update
    };
} ());

Se non ti senti a tuo agio nel "sovrascrivere" l' valueassociazione, puoi assegnare alla rilegatura un'associazione personalizzata un nome diverso e utilizzare il gestore dell'associazione.

ko.bindingHandlers.realtimeValue = { 'init':..., 'update':... };

dimostrazione

Una soluzione come questa sarebbe adatta per Knockout versione 2.x. Il team di Knockout ha messo a punto un'associazione più completa per input simili a testo tramite l' associazione textInput in Knockout versione 3 e successive. È stato progettato per gestire tutti i metodi di input di testo per input di testo etextarea . Gestirà anche l'aggiornamento in tempo reale che rende effettivamente obsoleto questo approccio.


2
In questo modo il mio markup è un po 'più ordinato. Inoltre, oltre a "afterkeydown", è possibile aggiungere eventi "input" e "incolla" per gestire le azioni incolla / trascina selezione.
bob

Nella soluzione precedente penso che "typeof valueUpdated === 'array'" debba essere sostituito con "Object.prototype.toString.call (valueUpdate) === '[array di oggetti]'". Vedere stackoverflow.com/questions/4775722/...
daveywc

20

La risposta di Jeff Mercado è fantastica, ma purtroppo interrotta con Knockout 3.

Ma ho trovato la risposta suggerita dagli sviluppatori ko mentre lavoravano alle modifiche di Knockout 3. Vedi i commenti in basso su https://github.com/knockout/knockout/pull/932 . Il loro codice:

//automatically add valueUpdate="afterkeydown" on every value binding
(function () {
    var getInjectValueUpdate = function (allBindings) {
        return {
            has: function (bindingKey) {
                return (bindingKey == 'valueUpdate') || allBindings.has(bindingKey);
            },
            get: function (bindingKey) {
                var binding = allBindings.get(bindingKey);
                if (bindingKey == 'valueUpdate') {
                    binding = binding ? [].concat(binding, 'afterkeydown') : 'afterkeydown';
                }
                return binding;
            }
        };
    };

    var valueInitHandler = ko.bindingHandlers.value.init;
    ko.bindingHandlers.value.init = function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        return valueInitHandler(element, valueAccessor, getInjectValueUpdate(allBindings), viewModel, bindingContext);
    };
}());

http://jsfiddle.net/mbest/GKJnt/

Modifica Ko 3.2.0 ora ha una soluzione più completa con la nuova associazione "textInput". Vedi la risposta di SalvidorDali


1
Grazie per aver aggiornato la risposta alla 3.0!
Graham T,

@GrahamT in KO 3.2 non ne hai nemmeno bisogno. È solo una riga
Salvador Dali,

Molto bello per quelli di noi su KO 3.2 che non sapevano di TextInput quando abbiamo iniziato e non possono aggiornare tutti gli input dell'intero sistema!
cscott530,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.