Using Knockout with jQuery UI Widgets

16171 views
Skip to first unread message

Mike Edmunds

unread,
Mar 27, 2011, 2:39:54 PM3/27/11
to knock...@googlegroups.com
I recently joined a project that makes extensive use of jQuery UI Widgets -- not just the standard ones, but also its own custom ones as a general architecture for holding view-layer code.

We're adding Knockout to the mix and wanted to create a single, reusable binding for UI widgets. Since several other people here seem to be working with jQuery UI, I thought I'd share what we've come up with. Comments and feedback are very much welcome, and please feel free to use this in your own projects.

Here's the Knockout custom binding code: https://gist.github.com/889400.
Here's a fiddle with some demonstrations: http://jsfiddle.net/medmunds/4Xjtq/

This simple example attaches the jQuery UI datepicker widget to an input field, with all of datepicker's default options:

<input id='deadline' type='text' data-bind='jqueryui: "datepicker"' />

You can also pass jQuery UI options in the binding -- and the real power comes from using Knockout observables in those options. Here's a slightly-more complex example that does just that (and also uses KO's 'value' binding for the contents of the input field):

<input id='search' 
     data-bind='jqueryui: { widget: "autocomplete", 
                              options: { source: searchCompletions(),
                                           delay: 500 } },
                     value: searchString' />

In this example, searchCompletions would be a ko.observableArray in your viewModel, used to provide the possibilities for jQuery UI's autocomplete widget. The options are not only passed to the UI widget at creation time, but they're also updated on the widget whenever they change. So long as searchPossibilities is a Knockout observable, whenever you update searchPossibilities in your viewModel, they're automatically updated in the autocomplete widget. (The fiddle above includes a version of this example: switching the 'category' will change the autocompletions for the 'product' field using a ko.dependentObservable.)


We've been working with this for a couple of weeks now. Some observations:
  • This approach seems to work well with jQuery UI Widgets that are designed to enhance existing HTML with little additional code (e.g., button, buttonset, datepicker, autocomplete).
  • I think it's less attractive for some of the other jQuery UI Widgets. E.g., the example fiddle includes a slider bound to a ko.observable value, but I think the code in that binding is particularly ugly and really doesn't belong in the HTML. For real-world use, I'd probably want to create a custom KO binding specifically for the slider to hide all of this.
  • The code hasn't been extensively tested (and we haven't tried all of the jQuery UI Widgets), so I'm sure there are bugs lurking. E.g., autocomplete seems to have a weird interaction with KO's value binding if you use the mouse in the autocomplete menu -- but it works fine if you use the keyboard (and, oddly, datepicker doesn't have the same problem). Draggable seems to work, but resizable doesn't. Etc.
Also, one big caveat: the jQuery UI Widgets created this way will never get destroyed. This may or may not be a problem for you. (It is for us, as we're using them inside some deeply-nested foreach templates that get updated frequently, meaning Knockout is constantly creating and deleting DOM elements with these widgets attached. Our current solution involves modifying Knockout to call a new 'destroy' method in custom bindings, so that code's not included here. Perhaps some of you will have ideas for a better approach.)

We're really impressed with both Knockout and the community around it. I hope some of you find this helpful.

- Mike

rpn

unread,
Mar 27, 2011, 10:22:39 PM3/27/11
to knock...@googlegroups.com
Nice work!  Looks like a very useful binding.

nevf

unread,
Apr 6, 2011, 6:42:56 PM4/6/11
to KnockoutJS
Mark, thanks for your jQuery UI KO binding. I've just put it into an
app I'm working on and it is working a treat.

- Neville http://www.surfulater.com
>    - This approach seems to work well with jQuery UI Widgets that are
>    designed to enhance existing HTML with little additional code (e.g., button,
>    buttonset, datepicker, autocomplete).
>    - I think it's less attractive for some of the other jQuery UI Widgets.
>    E.g., the example fiddle includes a slider bound to a ko.observable value,
>    but I think the code in that binding is particularly ugly and really doesn't
>    belong in the HTML. For real-world use, I'd probably want to create a custom
>    KO binding specifically for the slider to hide all of this.
>    - The code hasn't been extensively tested (and we haven't tried all of

Sergey

unread,
Apr 7, 2011, 10:10:25 PM4/7/11
to KnockoutJS
Mike,
I think that this is amazing idea to add the posibility to define the
plugin in data-bind.
I would like to use it my project. How do you plan to license the
plugin you created?
>    - This approach seems to work well with jQuery UI Widgets that are
>    designed to enhance existing HTML with little additional code (e.g., button,
>    buttonset, datepicker, autocomplete).
>    - I think it's less attractive for some of the other jQuery UI Widgets.
>    E.g., the example fiddle includes a slider bound to a ko.observable value,
>    but I think the code in that binding is particularly ugly and really doesn't
>    belong in the HTML. For real-world use, I'd probably want to create a custom
>    KO binding specifically for the slider to hide all of this.
>    - The code hasn't been extensively tested (and we haven't tried all of

Mike Edmunds

unread,
Apr 11, 2011, 4:29:17 PM4/11/11
to knock...@googlegroups.com
Sergey,

We've released it under the MIT license (http://www.opensource.org/licenses/mit-license.php), which matches what Knockout 1.2 will use. The code above should already have that license statement in place, so you should be good to go.

Best,
Mike

Mike Edmunds

unread,
Apr 11, 2011, 4:29:53 PM4/11/11
to knock...@googlegroups.com
Neville,

Glad to hear it. Let me know if you run into any trouble.

Cheers,
Mike

Johan Nordberg

unread,
Apr 12, 2011, 6:02:26 AM4/12/11
to knock...@googlegroups.com
I've used this in a project recently and it worked great. One thing that's missing though is to set initial value of widget and update view model value when widget changes. I've mainly used the slider. 

It was easy to add to the plugin, but still something that would make the plugin more complete.

Thanks for a great plugin!

Jehu

unread,
Apr 14, 2011, 2:48:30 AM4/14/11
to KnockoutJS
Great stuff! Thank you for sharing with us.

Have you used it - say - with a 'sortable' unordered list? can't
firure out, how to change the sorting in the viewModel after dropping
the item.
>    - This approach seems to work well with jQuery UI Widgets that are
>    designed to enhance existing HTML with little additional code (e.g., button,
>    buttonset, datepicker, autocomplete).
>    - I think it's less attractive for some of the other jQuery UI Widgets.
>    E.g., the example fiddle includes a slider bound to a ko.observable value,
>    but I think the code in that binding is particularly ugly and really doesn't
>    belong in the HTML. For real-world use, I'd probably want to create a custom
>    KO binding specifically for the slider to hide all of this.
>    - The code hasn't been extensively tested (and we haven't tried all of

Sleighter

unread,
Apr 14, 2011, 1:52:00 PM4/14/11
to KnockoutJS
Thanks for sharing this. It wasn't sufficient for a recent problem I
had, so I thought I'd share a work-around that I used for the jQueryUI
problem and hopefully I can get some suggestions for improvements from
you.

I wanted to use jqui tabs/accordion widgets on an observable
collection. However, jquery wasn't automatically making new elements
part of the tabs/accordion widget. My first thought was to subscribe
to the collection changed event and re-trigger the widget with $
().accordion(). The problem is that the subscription event fires
before the template is rendered, so this still doesn't make the new
element part of the widget. Then, I found the livequery plug-in
(http://docs.jquery.com/Plugins/livequery) which triggers an event
when any new element matching your selector is rendered.

I set up the call as :
$("#accordion-list > DIV").livequery( function() { $("#accordion-
list").accordion('destroy').accordion(); } );

It seems criminally inefficient, but when KO adds an element to the
accordion list, livequery will trigger recreation of the accordion
widget on the, now larger, collection. It still has a problem of
showing the unstyled element for a moment before the accordion is
rebuilt.

Fiddles, For your pleasure:
Without the live-query fix (new elements are not part of the widget):
http://jsfiddle.net/sleighter/q9ZGE/
With the livequery fix (new elements are added to the widget):
http://jsfiddle.net/sleighter/s5tEG/

Cheers,
Sleighter

Ω Alisson

unread,
Apr 14, 2011, 2:02:52 PM4/14/11
to knock...@googlegroups.com

Daz Wilkin

unread,
Apr 20, 2011, 4:12:37 PM4/20/11
to knock...@googlegroups.com
Very useful! Thanks very much.

I'm struggling with what I think is a common problem with the jQuery autocomplete and wonder whether anyone had a solution using Knockout and this custom binder? I want the value returned from the autocomplete to be an ID rather than the searched text.

In my case, the source for the autocomplete widget is an AJAX call that I'm transforming to an array "businesses" of id*name pairs. Everything works well except the data binding contains the typed text, e.g. "micro", not the selected text, e.g. "Microsoft Corporation" and I'm unable to find an elegant way to actually return the associated ID value, e.g. 5.

Would appreciate your suggestions.

Ω Alisson

unread,
Apr 20, 2011, 4:15:04 PM4/20/11
to knock...@googlegroups.com
Please show us the code so we can help you :)

Daz Wilkin

unread,
Apr 20, 2011, 4:46:09 PM4/20/11
to knock...@googlegroups.com
Thanks thelinuxlich! I've edited this down to show the core of what I'm trying to achieve and hope it makes sense. Ultimately, I'd like the each customer's SelectedLookup to be bound to the id of the selected element of businesses/businessLookup.

<fieldset id="customers">
    <table id="customersContainer" data-bind="template: { name: 'customersTemplate', foreach: customers, templateOptions: { businessLookup: businesses() } }"></table>
</fieldset>

<script id="customersTemplate" type="x-jquery-tmpl">
<tr>
    <td>${ID}</td>
    <td>${Name}</td>
    <td>
        <input data-bind="
            value: SelectedLookup,
            jqueryui: {
                widget: 'autocomplete',
                options: {
                    source: $item.businessLookup,
                    select: function (event, ui) {},
                    minLength: 0
                }
            }" type="text">
        </input>
    </td>
</tr>
</script>

<script type="text/javascript">

var customers = $.map(json1, function(customer) {
    return {
        ID: customer.ID,
        Name: customer.Name,
        SelectedLookup: ko.observable(),
     };
});

var businesses = $.map(json2, function(business) {
    return {
        label: business.Name,
        id: business.Id
    }
});

var viewModel = {
    customers = ko.observableArray(customers),
    businesses = ko.observableArray(businesses)
};

ko.applyBindings(viewModel);

</script>

Ω Alisson

unread,
Apr 20, 2011, 4:54:07 PM4/20/11
to knock...@googlegroups.com
You need to add custom focus and select options to filter data for your template, see this http://jqueryui.com/demos/autocomplete/#custom-data

Sleighter

unread,
Apr 20, 2011, 5:30:50 PM4/20/11
to KnockoutJS
Thanks Alisson, you're bindings are kickass.

Cheers,
Sleighter

On Apr 20, 4:54 pm, Ω Alisson <thelinuxl...@gmail.com> wrote:
> You need to add custom focus and select options to filter data for your
> template, see thishttp://jqueryui.com/demos/autocomplete/#custom-data

Daz Wilkin

unread,
Apr 20, 2011, 6:41:30 PM4/20/11
to knock...@googlegroups.com
Thanks. The link you included does mirror what I'm trying to do. However, I'm unable to get it to work.

I added an input field to persist the Id value from the lookup. On the select event, I update $(this).siblings().val with the Id, check it before/after setting it. It appears to get set correctly. But, when I inspect the viewModel element, the observable property is undefined.

Clearly doing something wrong but unable to understand what.

 <input data-bind="
    value: null,
    jqueryui: {
        widget: 'autocomplete',
        options: {
            source: $item.businessLookup,
            focus: function(event, ui) {return false;},
            select: function (event, ui) {
                console.log(ui.item.id);
                console.log($(this).siblings().val());
                $(this).siblings().val(ui.item.id);
                console.log($(this).siblings().val());
            },
            minLength: 0
        }
    }"
    type="text">
</input>
<input data-bind="
    value: SelectedSearchBox"
    class="hidden"
    type="text">
</input>



the input field is bound to the viewModel.

Proto collie://

unread,
Apr 21, 2011, 9:35:44 AM4/21/11
to KnockoutJS
Could you perhaps share the code that you used to implement this?
We're having a similar problem here and I'm not sure of the best way
to approach this in a knockout plugin - i'm new to KO.

Daz Wilkin

unread,
Apr 21, 2011, 11:32:47 PM4/21/11
to knock...@googlegroups.com
I was unable to get it working, gave up and reverted to using a regular select :-(

Vishwa Kiran

unread,
Apr 29, 2011, 12:40:02 PM4/29/11
to KnockoutJS
This rocks, nice work..thought it would choke on nested templates, but
runs like a charm.

regards

Simone Basso

unread,
Jun 1, 2011, 12:03:22 PM6/1/11
to knock...@googlegroups.com
I'm playing with the datepicker. it looks like the binding is mono directional between the datepicker and the viewmodel
so the only way is to force the calendar to reset the setDate
http://jsfiddle.net/uDPGH/

there's also another problem, for a strange reason, 19/05/2009 become 12/24/2016 when I set it manually :(

Jérémie

unread,
Jul 7, 2011, 7:19:47 AM7/7/11
to KnockoutJS
@Mike Edmunds : Very good work thank you for sharing !

@Simone Basso : I guess it's because datepicker default date format is
'mm/dd/yy' and the date you gave it (19/05/2009) is obviously
formatted as 'dd/mm/yy'. You may want to define the date format when
calling the datepicker widget like <input id='deadline' type='text'
data-bind='jqueryui: {widget: "datepicker", options: {dateFormat: "dd/
mm/yy"} }, value: deadline' />

I tried to play with daterangepicker with 2 input fields but can't
find how to call it on both (1 daterangepicker for the 2 inputs, not 1
drp for each).

On 1 juin, 12:03, Simone Basso <smn...@gmail.com> wrote:
> I'm playing with the datepicker. it looks like the binding is mono
> directional between the datepicker and the viewmodel
> so the only way is to force the calendar to reset the setDatehttp://jsfiddle.net/uDPGH/

gaffe

unread,
Jul 11, 2011, 5:26:46 PM7/11/11
to knock...@googlegroups.com
I am using your binding as well. This is really useful and I found it just buried in the forums if you happen to search in here for it. 

Most folks use knockout and jquery together, so imagine my shock when I found out jQueryUI doesn't work with knockout out of the box, you need this custom binding. This is worth a mention in the main documentation I think.

GrailsAndy

unread,
Jul 19, 2011, 8:02:39 AM7/19/11
to KnockoutJS
+1 for the doc reference. This is a really useful binding.

david.s...@gmail.com

unread,
Jul 21, 2011, 6:08:26 PM7/21/11
to knock...@googlegroups.com
By the way in jQuery UI 1.9 they will be adding a new refresh method to .tabs so instead of destroying and recreating you could just call .tabs('refresh') should help your flickering somewhat.

chrisj...@gmail.com

unread,
Nov 2, 2011, 7:40:53 AM11/2/11
to knock...@googlegroups.com
Is it possible to bind two widgets to the same div, for example drag and resize?

nevf

unread,
Jan 11, 2012, 8:09:25 PM1/11/12
to knock...@googlegroups.com
Hi Mike,
Your original fiddle http://jsfiddle.net/medmunds/4Xjtq/ is updating product with the value the user enters, not the value selected from the auto-suggest list. I have the same issue using autosuggest in my code.

jose....@gmail.com

unread,
May 10, 2012, 11:17:08 AM5/10/12
to knock...@googlegroups.com
Very nice! I was about to solve a problem in our data grid in a much less elegant manner before I ran into your custom binding.

protactinium

unread,
Jun 12, 2012, 9:03:03 AM6/12/12
to knock...@googlegroups.com
Hey,

I'm having the same trouble !!

Could not figure out how to deal with it ?

omar.a...@gmail.com

unread,
Jul 4, 2012, 10:16:19 AM7/4/12
to knock...@googlegroups.com
Hi
Recently I worked in a project but we made the combination in different way

(function( $ ) {
  $.widget( "myWidget", {
 
    // These options will be used as defaults
    options: { 
      clear: null
    },
 
    // Set up the widget
    _create: function() {
       this._bindViewModel();
       ko.applyBindings(this, this.element.get(0));
    },
 
    
    _bindViewModel:function(){
      var countryViewModel= ko.observable({
             name:ko.observable(),
             code:ko.observable(),
             flag:ko.observable()
      });
     }
  });
}( jQuery ) );

         <input id='countryName' type='text' data-bind='text:countryViewModel().name/> 

On Sunday, March 27, 2011 10:39:54 PM UTC+4, Mike Edmunds wrote:
I recently joined a project that makes extensive use of jQuery UI Widgets -- not just the standard ones, but also its own custom ones as a general architecture for holding view-layer code.

We're adding Knockout to the mix and wanted to create a single, reusable binding for UI widgets. Since several other people here seem to be working with jQuery UI, I thought I'd share what we've come up with. Comments and feedback are very much welcome, and please feel free to use this in your own projects.

Here's the Knockout custom binding code: https://gist.github.com/889400.
Here's a fiddle with some demonstrations: http://jsfiddle.net/medmunds/4Xjtq/

This simple example attaches the jQuery UI datepicker widget to an input field, with all of datepicker's default options:

<input id='deadline' type='text' data-bind='jqueryui: "datepicker"' />

You can also pass jQuery UI options in the binding -- and the real power comes from using Knockout observables in those options. Here's a slightly-more complex example that does just that (and also uses KO's 'value' binding for the contents of the input field):

<input id='search' 
     data-bind='jqueryui: { widget: "autocomplete", 
                              options: { source: searchCompletions(),
                                           delay: 500 } },
                     value: searchString' />

In this example, searchCompletions would be a ko.observableArray in your viewModel, used to provide the possibilities for jQuery UI's autocomplete widget. The options are not only passed to the UI widget at creation time, but they're also updated on the widget whenever they change. So long as searchPossibilities is a Knockout observable, whenever you update searchPossibilities in your viewModel, they're automatically updated in the autocomplete widget. (The fiddle above includes a version of this example: switching the 'category' will change the autocompletions for the 'product' field using a ko.dependentObservable.)


We've been working with this for a couple of weeks now. Some observations:
  • This approach seems to work well with jQuery UI Widgets that are designed to enhance existing HTML with little additional code (e.g., button, buttonset, datepicker, autocomplete).
  • I think it's less attractive for some of the other jQuery UI Widgets. E.g., the example fiddle includes a slider bound to a ko.observable value, but I think the code in that binding is particularly ugly and really doesn't belong in the HTML. For real-world use, I'd probably want to create a custom KO binding specifically for the slider to hide all of this.
  • The code hasn't been extensively tested (and we haven't tried all of the jQuery UI Widgets), so I'm sure there are bugs lurking. E.g., autocomplete seems to have a weird interaction with KO's value binding if you use the mouse in the autocomplete menu -- but it works fine if you use the keyboard (and, oddly, datepicker doesn't have the same problem). Draggable seems to work, but resizable doesn't. Etc.
Also, one big caveat: the jQuery UI Widgets created this way will never get destroyed. This may or may not be a problem for you. (It is for us, as we're using them inside some deeply-nested foreach templates that get updated frequently, meaning Knockout is constantly creating and deleting DOM elements with these widgets attached. Our current solution involves modifying Knockout to call a new 'destroy' method in custom bindings, so that code's not included here. Perhaps some of you will have ideas for a better approach.)

rrik...@gmail.com

unread,
Nov 15, 2012, 9:03:25 AM11/15/12
to knock...@googlegroups.com
I'm trying this binding with the slider and i want to use it with the range property, but i'm having same problems, the example: http://jsfiddle.net/rrikkers/r4cpp/
Only the min slider is showing and not the max slider, any idea why?

rrik...@gmail.com

unread,
Nov 15, 2012, 10:28:08 AM11/15/12
to knock...@googlegroups.com, rrik...@gmail.com
Oke I just had to take a break  I think ;)
Forgot the values property to call the viewmodel value.

John Doe

unread,
Apr 2, 2013, 8:32:43 AM4/2/13
to knock...@googlegroups.com, chrisj...@gmail.com
Op woensdag 2 november 2011 12:40:53 UTC+1 schreef chrisj...@gmail.com het volgende:
Is it possible to bind two widgets to the same div, for example drag and resize?

I am wondering the same thing. Does anyone know how to do this?

I'm trying to bind the Datepicker widget and Masked Input Plugin at the same time:

This doesn't work:

<input data-bind="value: my_date, jqueryui: { widget: 'mask', options: '99-99-9999' }, jqueryui: 'datepicker'" />

This works, but is not KnockOutJS compliant:

$('.datepicker').mask("99-99-9999").datepicker();
Reply all
Reply to author
Forward
0 new messages