I've got a custom binding to handle autocomplete, and when a user selects an item from the autocomplete I talk to the server and replace the text_field with a shortened name. The problem is that this triggers the 'update' function of my custom binding a second time.
Knockout.js code (edit: Note the following is CoffeeScript) :
ko.bindingHandlers.ko_autocomplete =
init: (element, params) ->
$(element).autocomplete(params())
update: (element, valueAccessor, allBindingsAccessor, viewModel) ->
unless task.name() == undefined
$.ajax "/tasks/name",
data: "name=" + task.name(),
success: (data,textStatus, jqXHR) ->
task.name(data.short_name)
Task = ->
@name = ko.observable()
@name_select = (event, ui) ->
task.name(ui.item.name)
false
task = Task.new()
View
= f.text_field :name, "data-bind" => "value: name, ko_autocomplete: { source: '/autocomplete/tasks', select: name_select }"
Is there a way to apply a throttle to a custom binding?
I just want to stop the custom bindings 'update' function from triggering a second time when I set the task.name to the short_name sent back from the server.
In general, I've found a pattern like this to work for me
ko.bindingHandlers.gsExample =
update: (element, valueAccessor, allBindingsAccessor, viewModel) ->
args = valueAccessor()
# Process args here, turn them into local variables
# eg.
span = args['span'] || 10
render = ko.computed ->
# Put your code in here that you want to throttle
# Get variables from things that change very rapidly here
# Note: You can access variables such as span in here (yay: Closures)
some_changing_value = some_observable()
$(element).html(some_changing_value)
# Now, throttle the computed section (I used 0.5 seconds here)
render.extend throttle : 500
# Cause an immediate execution of that section, also establish a dependancy so
# this outer code is re-executed when render is computed.
render()
If you don't want to violate isolation, so you can use autocomplete's delay options and it's select event, throwing update function away.
Modify your init in this way:
var options = $.extend(params(), {
select: function(ev, ui) {
var name = ui.item ? ui.item.short_name : this.value
task.name(name);
}
})
$(element).autocomplete(options)
Your update is called a second time because (for simplification) update is some kind of computed itself. So it subscribes every observable accessed inside it. In this line unless task.name() == undefined
you subscribe update to task.name()
. Then when you update your observable with task.name(data.short_name)
on success of ajax request, update get notified and recomputed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With