Passing the current element into a "visible" or "css" function?

1,008 views
Skip to first unread message

Michael Stum

unread,
May 23, 2011, 8:50:35 PM5/23/11
to KnockoutJS
I have a binding like this:
<div data-viewName="detailsView" data-bind="click: changeView,
css: {disabled: currentChildView() !== 'detailsView'}">

As you see, the css binding is an expression. Now, in order to DRY up
my view I would love to do something like
css: {disabled: currentChildView() !== this.getAttribute('data-
viewName')}

or even better:
css: {disabled: someFunction}

and then in my ViewModel:

someFunction: function(element) {
return this.currentChildView() === element.getAttribute("data-
viewName");
}

However, it seems that Knockout.js does not pass the current element
to a function that is used to evaluate the result, and that "this"
refers to the viewModel and not the DOM element.

I've dug a bit into the code of Knockout 1.2.1 and saw a
"ko.bindingHandlers['css']", but that seems to be the function that
acts on the result of the binding. The actual binding evaluation
happens earlier, but I haven't dug into it yet.

So before I spend a lot more time trying to understand the inner
workings I first wanted to ask if there is a way for css, visible and
pretty much all the other bindings to be able to call a function with
the DOM element as a parameter? Or, alternatively, with a data-
something attribute of my choice as a parameter?

rpn

unread,
May 23, 2011, 9:48:19 PM5/23/11
to knock...@googlegroups.com
Two thoughts on this one:
- I think that you are ultimately going to have the best experience, if you try to keep your viewModel and your UI (DOM elements) as separate as possible.  The view model should have almost no knowledge of the actual UI.  The binding handlers should be the only link between the two.  This makes it so your viewModel can stand-alone, and is portable and testable.  I don't know the structure of your whole viewModel above, but it seems that you should try to get the data-viewName into the data that is the context of the binding rather than in the "data-" attribute.  Maybe we could play with it in a jsFiddle?

-On the other hand, if you really want to do this, then I think that one way is to create a "wrapper" binding to the css binding.

Maybe something like (probably with a better name):

ko.bindingHandlers.cssPlus {
    initfunction(elementvalueAccessorallBindingsAccessorcontext{
        var options valueAccessor();
        var value element.getAttribute(options.paramAttribute);
        var result options.action.call(contextvalue);
        var newValueAccessor function({
            var css {};
            css[options.cssresult;
            return css;
        };
        ko.bindingHandlers.css.update(elementnewValueAccessorallBindingsAccessorcontext);
    }
};


Bind it like:
<div data-viewName="one" data-bind="cssPlus: { paramAttribute: 'data-viewName', action: someFunction, css: 'special' }">One</div>

No real reason that the binding could not be even more specific for your purposes and make more assumptions (assume 'data-viewName').



Michael Stum

unread,
May 24, 2011, 12:01:30 AM5/24/11
to KnockoutJS
Thank you. Let me explain what I have, maybe I'm just doing it wrong.
I have something that mimics a Tab Control. When people click on a
tab, a child div should render a different views and other tabs should
change their css class and/or visibility based on the fact that they
aren't the "active view".

I've created a jsFiddle here: http://jsfiddle.net/wmwbJ/8/
As you see, the CSS Class on the li is dependent on the fact that it
is the active view. And in order to determine that it is the active
view, I wanted to compare the data-viewName attribute to the
currentView().

Maybe I'm doing something conceptually wrong?

rpn

unread,
May 24, 2011, 9:10:23 AM5/24/11
to knock...@googlegroups.com
Hi Michael-
I think that you can make it work with the method that you are using.  I generally don't think that there is always a "right" way to do something, so certainly no reason to say your way is "wrong" at all.

However, the way that I would tackle your example is to represent your views as an array of objects.  These objects would have properties like name and either the actual content or the name of a template.    

Now, you can add/remove tabs by simply modifying the view model and the UI will update accordingly.  You can potentially load your tabs from the server, if necessary too.  

Maybe something more like: http://jsfiddle.net/rniemeyer/vWZQM/.  If some of the other properties are editable or could change, then they could be observable.

Hope this helps.  Would be happy to help further with exploring either method.


Reply all
Reply to author
Forward
0 new messages