Ropez
Ropez

Reputation: 3514

TwoWay focus binding in knockout.js

I have this custom binding that updates an observable with the data for the focused child element of the element that is is applied to:

ko.bindingHandlers.selected =
  init: (element, valueAccessor) ->
    receiver = valueAccessor()
    $(element).focusin((event) ->
      data = ko.dataFor(event.target)
      receiver(data)
    )

This works better than the standard hasFocus binding for the use case where I want some other part of the page to show information about the focused item:

<ul data-bind="foreach: items, selected: selectedItem">
    <li><a href="#" data-bind="text: name"></a></li>
</ul>
<p data-bind="with: selectedItem">
    Selected: <span data-bind="text: name"></span>
</p>

Now I want to make this a two-way binding, so that I can change selectedItem from my script code, and make the correct list element focused with $.focus(). Any ideas how to extend the binding to work in both ways?

I realize that I may need to use the "control descendant bindings" approach, and not be able to apply the binding to the same element as the foreach binding, but that's fine. The problem I have is that the child element doesn't seem to be bound when the update function is called (dataFor returns undefined).

Upvotes: 2

Views: 644

Answers (2)

nemesv
nemesv

Reputation: 139798

You can walk through the descendant elements in your update function and check which element to focus using the ko.dataFor:

update: (element, valueAccessor) ->
    receiver = valueAccessor()
    item = receiver()
    $(element).find("*").each((index, elem) ->      
      if (item == ko.dataFor(elem))
        $(elem).focus()
    )

Demo JSFiddle.

Upvotes: 1

Ali Habibzadeh
Ali Habibzadeh

Reputation: 11577

I am not sure what you have decided to create a custom binding for finding out a selected item in a collection and storing it in an observable?

You can simply capture an event and find out the selected item:

var data = [
    { "Id" : 0, "Name" : "Item0" },
    { "Id" : 1, "Name" : "Item1" },
    { "Id" : 2, "Name" : "Item2" },
    { "Id" : 3, "Name" : "Item3" },
    { "Id" : 4, "Name" : "Item4" },
    { "Id" : 5, "Name" : "Item5" },
    { "Id" : 6, "Name" : "Item6" },
    { "Id" : 7, "Name" : "Item7" }
]

function vm(){
    var self = this;
    self.items = ko.observableArray(data);
    self.selectedItem = ko.observable();
    self.selectItem = function(item){
        self.selectedItem(item);
    }
    self.select3rd = function(){
        self.selectedItem(self.items()[2]);
    }
}

ko.applyBindings(new vm());

Html:

<ul data-bind="foreach: items">
    <li><a href="#" data-bind="text: Name, event: { focus : $parent.selectItem }, attr: { 'tabindex' : $index }"></a></li>
</ul>

<p>Selected item: <span data-bind="text: ko.toJSON(selectedItem)"></span></p>

<button data-bind="click: select3rd">Select 3rd item</button>

And the fiddle is here

Upvotes: 0

Related Questions