开发者

Persisting json data in jstree through postback via asp:hiddenfield

I've been pouring over this for hours and I've yet to make much headway so I was hoping one of the wonderful denizens of SO could help me out. Here's the problem...

I'm implementing a tree via the jstree plugin for jQuery. I'm pulling the data with which I populate the tree programatically from our webapp via json dumped into an asp:HiddenField, basically like this:

JavaScriptSerializer serializer = new JavaScriptSerializer();
string json = serializer.Serialize(Items);

json = json.ToLower();
data.Value = json;    

Then, the tree pulls the json from the hidden field to build itself. This works perfectly fine up until I try to persist data for which nodes are selected/opened. To simplify my problem I've hardcoded some json data into the tree and attempted to use the cookie plugin to persist the tree state data. This does not work for whatever reason. I've seen other issues where people need to load the plugins in a specific order, etc, this did not solve my issue. I tried the same setup with html_data and it works perfectly. With this working persistence I converted the cookie plugin to persist the data in a different asp:hiddenfield (we can't use cookies for this type of thing in our application.)

essentially the cookie operations are identical, it just saves the array of nodes as the value of a hidden field. This works with the html_data, still not with the json and I have yet to be able to put my finger on where it's failing.

This is the jQuery.cookie.js replacement:

jQuery.persist = function(name, value) {
    if (typeof value != 'undefined') { // name and value given, set persist
        if (value === null) {
            value = '';
        }
        jQuery('#' + name).attr('value', value);
    } else { // only name given, get value
        var persistValue = null;
        persistValue = jQuery('#' + name).attr('value');
        return persistValue;
    }
};

The jstree.cookie.js code is identical save for a few variable name changes. And this is my tree:

$(function() {
                $("#demo1").jstree({
                    "json_data": {
                        "data" : [
                        {
                            "data" : "A node",
                            "children" : [ "Child 1", "Child 2" ]
          开发者_如何学Python              },
                        {
                            "attr": { "id": "li.node.id" },
                            "data" : {
                                "title": "li.node.id",
                                "attr": { "href": "#" }
                            },
                            "children": ["Child 1", "Child 2"]
                        }
                    ]
                    },
                    "persistence": {
                        "save_opened": "<%= open.ClientID %>",
                        "save_selected": "<%= select.ClientID %>",
                        "auto_save": true
                    },
                   "plugins": ["themes", "ui", "persistence", "json_data"]
                });
            });

The data -is- being stored appropriately in the hiddenfields, the problem occurs on a postback, it does not reopen the nodes. Any help would be greatly appreciated.

After looking through this some more, I just wanted to explain that it appears to me that the issue is that the tree has not yet been built from the JSON_data when the persistence operations are being attempted. Is there any way to postpone these actions until after the tree is fully loaded?


If anyone is still attempting to perform the same type of operation on a jsTree version 3.0+ there is an easier way to accomplish the same type of functionality, without editing any of the jsTree's core JavaScript, and without relying on the "state" plugin (Version 1.0 - "Persistence"):

var jsTreeControl = $("#jsTreeControl");
//Can be a "asp:HiddenField"
var stateJSONControl = $("#stateJSONControl");
var url = "exampleURL";

jsTreeControl.jstree({
    'core': {
        "data": function (node, cb) {
            var thisVar = this;

            //On the initial load, if the "state" already exists in the hidden value 
            //then simply use that rather than make a AJAX call
            if (stateJSONControl.val() !== "" && node.id === "#") {
                cb.call(thisVar, { d: JSON.parse(stateJSONControl.val()) });
            }
            else {
                $.ajax({
                    type: "POST",
                    url: url,
                    async: true,
                    success: function (json) {
                        cb.call(thisVar, json);
                    },
                    contentType: "application/json; charset=utf-8",
                    dataType: "json"
                }).responseText;
            }
        }
    }
});

//If the user changes the jsTree, save the full JSON of the jsTree into the hidden value, 
//this will then be restored on postback by the "data" function in the jsTree decleration
jsTreeControl.on("changed.jstree", function (e, data) {
    if (typeof (data.node) != 'undefined') {
        stateJSONControl.val(JSON.stringify(jsTreeControl.jstree(true).get_json()));
    }
});

This code will create a jsTree and save it's "state" into a hidden value, then upon postback when the jsTree is recreated, it will use its old "state" restored from the "HiddenField" rather than make a new AJAX call and lose the expansions/selections that the user has made.


Got it working properly with JSON data. I had to edit the "reopen" and "reselect" functions inside jstree itself.

Here's the new functioning reopen function for anyone who needs it.

reopen: function(is_callback) {
                var _this = this,
                    done = true,
                    current = [],
                    remaining = [];
                if (!is_callback) { this.data.core.reopen = false; this.data.core.refreshing = true; }
                if (this.data.core.to_open.length) {
                    $.each(this.data.core.to_open, function(i, val) {
                        val = val.replace(/^#/, "")
                        if (val == "#") { return true; }
                        if ($(("li[id=" + val + "]")).length && $(("li[id=" + val + "]")).is(".jstree-closed")) { current.push($(("li[id=" + val + "]"))); }
                        else { remaining.push(val); }
                    });
                    if (current.length) {
                        this.data.core.to_open = remaining;
                        $.each(current, function(i, val) {
                            _this.open_node(val, function() { _this.reopen(true); }, true);
                        });
                        done = false;
                    }
                }
                if (done) {
                    // TODO: find a more elegant approach to syncronizing returning requests
                    if (this.data.core.reopen) { clearTimeout(this.data.core.reopen); }
                    this.data.core.reopen = setTimeout(function() { _this.__callback({}, _this); }, 50);
                    this.data.core.refreshing = false;
                }
            },

The problem was that it was trying to find the

  • element by a custom attribute. It was just pushing these strings into the array to search when it was expecting node objects. Using this line

    if ($(("li[id=" + val + "]")).length && $(("li[id=" + val + "]")).is(".jstree-closed")) { current.push($(("li[id=" + val + "]"))); }
    

    instead of

    if ($(val).length && $(val).is(".jstree-closed")) { current.push(val); }
    

    was all it took. Using a similar process I was able to persist the selected nodes this way as well.

    Hope this is of help to someone.

  • 0

    上一篇:

    下一篇:

    精彩评论

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

    最新问答

    问答排行榜