开发者

How to add or remove jQueryUI tabs using knockout JS?

开发者 https://www.devze.com 2023-04-04 07:58 出处:网络
I\'ve been trying to use knockout to add/remove jQueryUI tabs but haven\'t开发者_高级运维 had any luck.

I've been trying to use knockout to add/remove jQueryUI tabs but haven't开发者_高级运维 had any luck. My view model is an array of Effect objects. I'd like a tab added/removed from the tab control as objects are added/removed from the view model.

Here's a JSFiddle someone started and I updated which shows what I'm looking to do JSFiddle example

It will break when you try to add a tab. I think I need to combine the template binding w/ a new custom binding that can destroy/recreate the tab control I think. I'd greatly appreciate any help. Thanks!


I thought I'd update this question with a new take on a solution that I started using. I had previously been using RP Niemeyer's fiddle http://jsfiddle.net/rniemeyer/dsKbH/ as the basis for dynamically adding/removing jQuery UI tabs bound to a KO observableArray.

Over the last few months I've bumped up against a few problems in my app related to A) The setTimeout() deferral, and B) The destroy and recreate of the tabs widget every time an update is triggered. So I came up with a different approach that avoids these issues, and IMHO, is a more elegant technique.

http://jsfiddle.net/LatencyMachine/XJPJZ/

The key idea is to introduce a very simple custom binding called "tabPanel" and a corresponding widget that you bind to your tab panel content divs. As KO creates and removes these divs based on your observableArray, the tabPanel binding makes sure to update the jQueryUI.tabs using it's "refresh" method. This works a lot smoother I think than trying to get the tabs to update (and at the right time) up in the container element's bindings.

Relevant code from fiddle

/**
KO Binding handler for a tabPanel div.  Use this on divs that can appear/disappear and/or have their id change 
depending upon an observable, usually an observableArray.
*/
ko.bindingHandlers.tabPanel = {
    update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {        
        $(element).tabPanel(ko.toJS(valueAccessor()));
    }
};

/**
This widget facilitates jQuery UI tabs that appear and disappear dynamically, usually as a result of MVVM like Knockout
Whenever this widget is created, the containing jQuery UI 'tabs' widget is refreshed so that it picks up the new tab
or drops the removed one.
This also facilitates dealing with id rename 'ripple' that occurs whenever a tab is removed due to the splice of an
observable array.
*/
$.widget("bw.tabPanel", {
    options: {
        id: null
    },

    _create: function() {
        this.element.hide();
        this.tabsElement = this.element.closest(".ui-tabs");        

        if(this.options.id) {
            this.element.attr({id: this.options.id});
        }
        this.refreshTabs();
    },

    _destroy: function() {
        if(this.options.id) {
            this.element.attr({id: ""});
        }
        this.refreshTabs();
    },

    _setOption: function(key, value) {
        var previousValue = this.options[key];
        if(previousValue == value) return;

        this.options[key] = value;

        switch(key) {
            case "id":
                this.element.attr({id: this.options.id});
                this.refreshTabs();
                break;
        }
    },

    /**
    Invoke refresh on the parent tab to let it know that something has changed.
    This also preserves the active index by setting it back to what it was before the refresh, which
    may correspond to a different tab after the refresh.
    */
    refreshTabs: function() {
        var previousActiveIndex = this.tabsElement.tabs("option", "active");
        this.tabsElement.tabs("refresh");
        this.tabsElement.tabs("option", "active", previousActiveIndex);        
    }
});


You will most likely have to re-render the tabs interface each time the model changes.

Also, add a unique id to the tabs <ul>:

<ul id="tabs">

Each time the model changes, call

$("#tabs").tabs();

(I am unsure how to fire off an event with Knockout but I'm sure it's in the documentation.)


I had a very similar issue. I have a deep view model graph where the top level observable array of child view models is represented as tabs, and each tab renders markup for grandchild data. Originally I was using jQuery UI tabs to tabify my markup after Knockout built it.

I have functionality to add, remove and duplicate those tabbed view models / domain objects. With the jQuery UI tabs I ended up having to call destroy on it and then rebuild it, and for cases like Duplicate I had to store the selected index first and then select that index + 1 after recreation, to select the newly created tab.

But now I've come to more fully embrace MVVM as the driver of my UI and have culled the jQuery tabs altogether and bound my own tab headers and tab content elements to an "isSelected" observable property on my Knockout view models. I have found this to be much cleaner and gives me way more flexibility, and the markup / CSS for the tabs turned out to be simple and elegant.

That isSelected property also means I can increase binding efficiency by specifying something like <div class="child" data-bind="if: isSelected"> for each tab. Previously (presumably) Knockout was needlessly working through all of the bindings of the invisible tabs.

This is how I'm trying to approach my UI generally now - avoiding direct procedural UI stuff as much as possible in favour of using bindings.

0

精彩评论

暂无评论...
验证码 换一张
取 消