First things first. Your example code uses a text
binding on an input
node, which isn't all that useful (it would mean a one-way binding from view model to DOM, the "text" content of that input
which is not meaningful). Given the valueUpdate
binding present you probably meant the value
binding? Note by the way that the textInput
binding is the way to combine those two in modern versions of KnockoutJS.
As to your actual question, important context is missing: why do you (think you) want to do this? Depending on the context, the solution will be different, or you might even turn out to have an XY-problem.
In any case, to the answers.
Option 1
Trying to answer the specific question nonetheless, I second @JohnnyHK's answer of writing a .subscribe
on your observable as one possible solution.
Option 2
Another possible solution closely resembling that one, but trying to satisfy "I'm not interested in the input's value" part, I suggest using a read only computed observable:
function Root() {
var self = this;
self.filter = ko.computed({
read: function() { return ""; }, // <-- not recommended, read context!
write: function(newValue) {
// You're free to discard the newValue <-- not recommended, read context!
alert("Input has been changed.");
}
});
}
ko.applyBindings(new Root());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<input data-bind="textInput: filter">
Now this explicitly discards the newValue
in the write bit (as per your request), and thus also has a weird-looking read
function. I don't recommend this, and instead would recommend using a private variable with a backing observable for reading and writing. Effectively though, that would make this option equivalent to the first option from the other answer.
An interesting note though, your question's bit about "the collectin" and filtering input: if you look at the docs from writeable computed
s you'll find that accepting users input or not if it satisfies a certain condition is in fact a use case for this.
Option 3
What I'd recommend though, if I rephrase your request a bit:
How do you react to input
value
changes with idiomatic KnockoutJS when your action won't care about the actual value
?
For writing custom interaction with the DOM, there's custom binding handlers. You could either have one that does not use the ViewModel at all, or one that has an observable.
Option 3.A
ko.bindingHandlers["opacitor"] = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
$(element).on("keydown", function() {
$(this).animate({ opacity: 0.05 }, 1000, function() { $(this).animate({ opacity: 1.0 }, 1000); });
});
}
}
ko.applyBindings({});
input { background-color: gold; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<input data-bind="opacitor">
Option 3.B
Or a second option that does use the view model:
ko.bindingHandlers["opacitor"] = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
$(element).on("keydown", function() {
var speed = ko.utils.unwrapObservable(valueAccessor)();
$(this).animate({ opacity: 0.05 }, speed, function() { $(this).animate({ opacity: 1.0 }, speed); });
});
}
}
// speed1 and speed2 could also be observables
ko.applyBindings({ speed1: 500, speed2: 2000 });
input { background-color: gold; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<input data-bind="opacitor: speed1">
<input data-bind="opacitor: speed2">
The downside of both option 3A and 3B is that you need to write custom event handling logic. If you have a peek at the textInput
source from KO you'll see that it's not trivial to handle these things correctly cross-browser. So using Option 1 or 2 might still be better (even though you'll have some view model interaction even though "you're not interested").
Option 4
Use the event
binding. This suffers from the same drawbacks as option 3, but might be a straightforward solution nonetheless. Here's an example:
function Root(){
var self = this;
self.myFn = function(data, element) {
console.log(element.target.value);
return true;
};
}
ko.applyBindings(new Root());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<input data-bind="event: { keyup: myFn }">
One subtle change you'll see in that last one, and a question I want to end with: did you in fact not have this issue, where you actually need the new value and thus should not be looking at afterkeydown
but for a different event?