Knockout, Templates, and jQuery UI elements

998 views
Skip to first unread message

Tim

unread,
Jul 28, 2011, 7:10:03 PM7/28/11
to KnockoutJS
Hi All,

I have a nested set of dynamic templates like so:

<script id="outer-template">
<div data-bind="template: {name: 'inner-template', foreach:
model.filterOptions}"
</script>

<script id="inner-template">
<div data-bind="template: {name: $item.templateLookup,
afterRender: subModel.postRender}"
</script>

<script id="dynamic-template-a">
<div class="ui-slider">
</script>

The function subModel.postRender makes the dynamic template a
slider via the jQuery UI library like so:

this.postRender = function(elements) {
$(".range-slider", elements).slider()
}

The problem is that because of the nesting foreach loop, the
elements don't exist in the DOM yet, and so the slider call doesn't
work when I try and initialize this template.

How do I resolve this? I must assume that someone has tried to
attach UI elements in a nested template before, but I can't find a
working example.

Let me know if I need to clarify further!

Thanks!
Tim


rpn

unread,
Jul 28, 2011, 9:18:18 PM7/28/11
to knock...@googlegroups.com
Hi Tim-
A couple options.  You could call your afterRender callback asynchronously like:  

this.postRender = function(elements) { 
    setTimeout(function() {
         $(".range-slider", elements).slider() 
   }, 0)


A more preferable option though would be to create a custom binding.  It could be as simple as:

ko.bindingHandlers.jqSlider = {
  init: function(element, valueAccessor) {
     var options = valueAccessor() || {};
     $(element).slider(options);
  }
}

Now, you would put this on the element inside your template like:

<div data-bind="jqSlider: { max: 100 }"></div>

Tim

unread,
Jul 29, 2011, 12:55:16 PM7/29/11
to KnockoutJS
Ok, I've tried both of these options before, but just wanted to see if
there was something better.

Also, just as a note, I'd seriously avoid doing init bindings with UI
elements in them.

For instance, I've seen something like this:

ko.bindingHandlers.dialog= {
  init: function(element, valueAccessor) {
     var options = valueAccessor() || {};
     $(element).dialog(options);
}

Because dialog is stateful, if you are switching templates in and out
dynamically, init gets called multiple times in the same context and
causes some serious leaking. In fact, with dialog, if you switched
templates in and out dynamically, this would actually spawn a new
dialog every time you loaded the template.

I'm not 100% sure that slider has similar behavior, but I'm still wary
of this approach anyways.

rpn

unread,
Jul 29, 2011, 3:21:52 PM7/29/11
to knock...@googlegroups.com
Hi Tim-
That is a very interesting point that you bring up and one that I had not really thought through for samples.  There is a pretty convenient solution available to us though.   There is a utility function called: ko.utils.domNodeDisposal.addDisposeCallback

This is used by the engine to ensure that dependentObservables from bindings are disposed when KO removes elements.  We can nicely take advantage of this in our binding to do the same.  So, a dialog binding could look like:

ko.bindingHandlers.dialog 

    initfunction(elementvalueAccessor
      var options valueAccessor(|| {}
      $(element).dialog(options)
      
      //handle disposal
      ko.utils.domNodeDisposal.addDisposeCallback(elementfunction({
         $(element).dialog("destroy");
      });    
   }    
}


Here is a sample with jQuery slider: http://jsfiddle.net/rniemeyer/wezk6/

Tim

unread,
Aug 2, 2011, 1:20:33 PM8/2/11
to KnockoutJS
Thanks, this is very helpful.

2 more questions though.

1. Is there a similar utility event for node creation?
(domNodeAdded.addCreationCallback or something)
2. Is there any online documentation for the utility classes for
knockout?

Thanks again!
Tim

rpn

unread,
Aug 2, 2011, 1:41:58 PM8/2/11
to knock...@googlegroups.com
There is not a concept currently for a creation callback.  There are hooks in the template binding to run code on your nodes (afterRender and for foreach there is beforeRemove, afterAdd).  You can also write a custom binding that just does some work in the init.

I have a post about many of the utility functions.  This does not include the domNodeDisposal API.
Reply all
Reply to author
Forward
0 new messages