Callum Linington
Callum Linington

Reputation: 14417

Knockout Bindings what is required in this function

EDIT

So as it stands, this method doesn't actually work as expected.

This issue was due to my changeUrl method, I fixed it and now this "does work". I have it in quotes because, I'm not sure what is and isn't needed to have a proper working binding.


Here is the binding handler:

ko.bindingHandlers.formatUrl = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, context) {
        var data = valueAccessor();

        var href = appStrap.changeUrl(ko.unwrap(data.url), ko.unwrap(data.id), data.replace);

        $(element).attr("href", href);

        ko.applyBindingsToNode(element, { attr: { href:  href } }, context);

        return { controlsDescendantBindings: true };
    },
    update: function (element, valueAccessor) {
        var data = valueAccessor();

        var href = appStrap.changeUrl(ko.unwrap(data.url), ko.unwrap(data.id), data.replace);

        $(element).attr("href", href);
    }
};

Here is it's usage:

<tr>
<td><a data-bind="formatUrl: { url: $root.GenericUrl, id: id, replace: '\\[ID\\]' }">Details</a>
</tr>

So generic url is something like this:

http://www.somehost.com/SomeController/SomeAction/[ID]

And the url will need to look like this:

<a href="http://www.somehost.com/SomeController/SomeAction/9fedb631-67ec-484c-9a7d-5f6bf62fb733"></a>

What do I need, and what shouldn't been in my binding handler. For example, should ko.applyBindingsToNode be there? is it currently being used incorrectly? etc. etc. etc.

Upvotes: 0

Views: 92

Answers (2)

Jarga
Jarga

Reputation: 391

I find it easier to just call back into the original binding handlers rather than using ko.applyBindingsToNode and controlsDescendantBindings: true

So something like this would be my suggestion:

ko.bindingHandlers.formatUrl = {
    changeUrl: function(data) {
          return appStrap.changeUrl(ko.unwrap(data.url), ko.unwrap(data.id), data.replace);
    },
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, context) {

        var href = ko.bindingHandlers.formatUrl.changeUrl(valueAccessor());

        valueAccessor = function(){
            return { href: href };  
        }

        return ko.bindingHandlers.attr.init(element, valueAccessor, allBindingsAccessor, viewModel, context);
    },
    update: function (element, valueAccessor, allBindingsAccessor, viewModel, context) {

        var href = ko.bindingHandlers.formatUrl.changeUrl(valueAccessor());

        valueAccessor = function(){
            return { href: href };  
        }

        return ko.bindingHandlers.attr.update(element, valueAccessor, allBindingsAccessor, viewModel, context);
    }
};

Upvotes: 1

Jeroen
Jeroen

Reputation: 63699

There is no need to do this in a binding handler. You can just use the attr binding: the docs even use href as a typical example. In addition, View Model logic is more easily unit tested.

For example:

var Item = function(urlProvider, id, name) {
    var self = this;
    self.id = ko.observable(id);
    self.name = ko.observable(name);
    self.url = ko.computed(function() {
        return urlProvider.GenericUrl.replace("[ID]", self.id());
    });
};

var RootViewModel = function() {
    var self = this;
    self.GenericUrl = "http://test/controller/action/[ID]";
    self.Item = new Item(self, 42, "Something something");
};

ko.applyBindings(new RootViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<table data-bind="with: Item">
    <tr>
        <td><a data-bind="attr: { href: url }">Details</a></td>
    </tr>
</table>

Upvotes: 0

Related Questions