Simon
Simon

Reputation: 34830

How to get the binding expression inside a BindingHandler in Knockout JS

Lets say I have a bound span

<span data-bind="MyBinding: Name"></span>

And I have a custom binding

ko.bindingHandlers.MyBinding = {
   init: function (element, valueAccessor, allBindings, viewModel, context) {
        // I want to get the string "Name" here. NOT the value of Name.
   },
};

How do i get a string with the value of the binding expression inside the handler? ie how do i get "Name" not "Value of name".

I also need the expression so passing the string "Name" is not feasible.

<span data-bind="MyBinding: 'Name'"></span>

Upvotes: 5

Views: 1580

Answers (6)

webketje
webketje

Reputation: 10966

Actually, Knockout does have a built-in way to do this. You do need to adapt your custom binding a little (namely, it becomes an object instead of a string value). Knockout allows you, along with the init and update properties, to specify a preprocess property. For example, suppose we have the following viewModel:

var app = { data: ko.observable('Hello World') };

And a simple binding that would convert the string passed to it fully to lowercase, and output both the binding's name and value (the property passed in the view obviously being data):

ko.bindingHandlers.lower = {
  update: function(elem, value) {
    var obj = ko.unwrap(value());
    elem.textContent = 'Name: ' + ko.unwrap(obj.name) + 
      ' - Value: ' + ko.unwrap(obj.data).toLowerCase();
  },
  preprocess: function(value, bindingName) {
    return '{data:' + value + ', name:\'' + bindingName + '\'}';
  }
};

It's as simple as stringifying the value property passed to it & transforming the binding's value into an object with 2 keys (name & value here). Have a look. See Binding preprocessing for more info.

Upvotes: 2

codeMonkey
codeMonkey

Reputation: 4805

I like what pavel-chervov said above. In a similar vein if you don't mind using a bit of jQuery:

<span data-binding-name="Name" data-bind="MyBinding: Name"></span>

ko.bindingHandlers.MyBinding = {
    init: function (element, valueAccessor, allBindings, viewModel, context) {
        var nameOfProp = $(element).data('binding-name');
    },
};

Upvotes: 1

user4069489
user4069489

Reputation:

I propose to transfer the name of the property with use another binding.

<div data-bind="myBinding: content, nameOfProp:'content'"></div>

ko.bindingHandlers.myBinding = {
    init: function(element, valueAccessor, allBindings, viewModel, context) {
      var nameOfProp = allBindings.get("nameOfProp");
    };
  }

JSFiddle

Upvotes: 1

Anj
Anj

Reputation: 1024

I'm a bit late to the party on this, but here's what I did, and it works like a charm:

JS:

ko.bindingHandlers.myHandler = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var regex = /return ([^\s]+)\s*}/;

        //accessor will contain whatever expression was used in data-bind
        //so in this example it will be "myProp.childProp"
        var accessor = regex.exec(valueAccessor.toString())[1];

        //...
},
//...

HTML:

<div data-bind="myHandler: myProp.childProp"></div>

So the value of accessor (in case you missed the comment) will be "myProp.childProp"

Upvotes: 0

gkempkens
gkempkens

Reputation: 460

You could do the following, so you won't need the eval.

<span data-bind="MyBinding: 'Name.More.AndMore'"></span>

ko.bindingHandlers.MyBinding = {
    init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        var value = valueAccessor();

        var sp = value.split('.');
        var current = viewModel;
        for(var i=0;i<sp.length;i++) {
            var nNode = current[sp[i]];
            if ( nNode ) {
                current = nNode;
            }
        }
        $(element).html(current());
    }
};

As already explained, you need to give the Name as string value in your binding. After that you can use this to step through your viewmodel to retrieve the value. To display the value in the element I've added $(element).html(current()); Here the current() is at the deepest level in my viewmodel so it can be called and the return value can be used to update the html.

Upvotes: 1

nemesv
nemesv

Reputation: 139748

You need to pass in Name as string and not as a reference:

<span data-bind="MyBinding: 'Name'"></span>

ko.bindingHandlers.MyBinding = {
   init: function (element, valueAccessor, allBindings, viewModel, context) {
        var myBinding = valueAccessor(); // will contain 'Name';
        var valueOfmyBinding = viewModel[myBinding]; //value of the Name property
   },
};

You can still use your expression as a string because in Javascript you can eval altough eval() is a dangerous function: here is a sample JSFiddle.

Or if you only need to support simple property expression like Child.Name you can write your own parser where you split the string on the dots and foreach on the accessors.

Upvotes: 2

Related Questions