Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get element an observable is bound to with Knockout?

This isn't an ideal situation, but due to another knockout binding I am using I am in a situation where I am needing to get the element an observable is bound to, if it is indeed bound to anything.

So is there a way to do this?

== Update ==

I didn't want to add any extra context incase it confuses the question, but as it may get an answer more in line with expectations here is the scenario.

I am using the knockout validation binding, which exposes all the errors using the ko.validation.group(model) method. However the problem is that only gives you the textual errors, it does not give you any context as to what part of the model gave you those errors. So I have made a small change to the source to now pass back the observable tied to each error, as this may be useful for a few scenarios, but from here I need to be able to tie this to an element so I can display some in-line validation of some kind.

Knockout Validation provides a very basic in-line validation where it creates a span after your element and you can give it a class, but this is too basic for my needs as currently we are using Qtip and other notification systems to display validation errors, and because of this I need to be able to have a Dom element and an error. So far I have an observable and an error, but I need to tie that observable object (which could be any ko.observable() property from the model) to its given DOM element, if it does have an element binding.

As all I have is an object and I am using validation driven from the model not the UI, the problem is not really going to be solved via a custom binding. Ideally I need to be able to crack open the marry up the observable object (an unknown ko.observable()) to an element.

Not to go too project specific, but my current project abstracts validation where events are fired off (i.e EventSystem.SendEvent(ValidationEvents.ValidationFailed, <element>, <error>)) then a validation system listens for these events and ties the error to the element, be it a tooltip, a growl style notification, an alert box etc. So I am trying to find the best way to keep this abstraction when driving the validation from the models observables not the ui's DOM elements (i.e jquery-ui)

== Edit 2 ==

I was a bit thrown by the way Knockout Validation knows the elements for observables to put in its own validation elements, however they just piggy back off the existing value binding, so I am just going to change that to add the elements for any validation elements based on their isValidatable() method, at least that way for each error I can tie it to an observable, and for any observables with element bindings I can tie them to the elements, and if there are none then it is fine they would just be form wide validation errors. I will give this a try as this should be something like (not tested yet):

if(utils.isValidatable(valueAccessor())) {
    valueAccessor().extend({hasElementalBinding: true, elementalBinding: element});
}
else { 
    valueAccessor().extend({hasElementalBinding: false});
}

At around line 250 in the registerValueBindingHandler, I will leave this question open for a while longer incase someone else has a better solution.

like image 277
Grofit Avatar asked Feb 27 '12 12:02

Grofit


People also ask

What is observable in Knockout?

Knockout. js defines an important role when we want to detect and respond to changes on one object, we uses the observable. An observable is useful in various scenarios where we are displaying or editing multiple values and require repeated sections of the UI to appear and disappear as items are inserted and deleted.

What are Knockout bindings?

Knockout's declarative binding system provides a concise and powerful way to link data to the UI. It's generally easy and obvious to bind to simple data properties or to use a single binding. For more complex bindings, it helps to better understand the behavior and syntax of Knockout's binding system.

What is Ko dataFor?

ko. dataFor(element) - returns the data that was available for binding against the element. ko. contextFor(element) - returns the entire binding context that was available to the DOM element.


2 Answers

I have done something similar to what you mentioned above. My data-bind tag includes a custom binding:

data-bind="... myvalidationbinding: myobservable"

Then in my binding handler I extend the observable

ko.bindingHandlers.myvalidationbinding = {
  init: function (element, valueAccessor, allBindingsAccessor, viewModel) { 
    valueAccessor().extend({element: element });
  }
};

And finally my extension is

ko.extenders.element = function (target, element) {
  target.DOMElement = element;
}

Now I can subscribe to isValid() given by knockout.validation and if not valid, go get the element the observable is bound to and then manipulate it with jQuery.

like image 190
Jon Keto Avatar answered Oct 18 '22 01:10

Jon Keto


This won't be very fast, so I would definitely cache the results, but something using jQuery's attribute selectors:

$('[data-bind*="Property"]')

*= is the attribute contains selector: http://api.jquery.com/attribute-contains-selector/

Obviously this won't catch anything that subscribed manually using the .subscribe method, but I'm not sure how you would extract element's from the functions anyway.

Disclaimer: while this solution will probably work, this sounds like a horrible idea to me, I would instead write a custom binding (as mentioned in the comments) or find some other solution.

like image 36
Paul Tyng Avatar answered Oct 18 '22 00:10

Paul Tyng