由JsonRestStore和jsonp提供支持的延迟加载dojo树

时间:2011-06-16 03:28:04

标签: tree dojo lazy-loading jsonp deferred

我正在尝试使用Dojo工具包实现跨域,延迟加载树。到目前为止,我有顶级节点正确显示,但点击expando我得到一个“延迟已解决”错误,我不知道为什么。我可以通过查看firebug网络选项卡看到fetch方法似乎正在工作。我认为我的问题在我的_processResults方法中,也许与定义其中的_loadObject有关...

我觉得我应该更好地了解Dojo,因为我花了很多时间来理解它。但唉,它是一个野兽...我已经看到一些提到JSONP和延迟加载不能在其中一个sitepen博客(http://www.sitepen.com/blog/2008/06/25/web-service-data-store/),但它没有提到为什么它不可能其他比JSONP是asyncronus。如果Dojo只是将传入的json数据填充到商店中,我不明白为什么这应该重要。

也许它与我的数据格式有关 - 在sitepen(http://www.sitepen.com/blog/2010/01/27/efficient-lazy-loading-of-a-tree/)上的另一个例子,使用jsonreststore在你扩展节点之前不加载项目,而我的格式加载项目但不加载子项节点,直到你扩展它...

不用多说,这里是ta codez ...

<script type="text/javascript">
dojoConfig = {
parseOnLoad: true,
isDebug: true,
usePlainJson: true  
};
</script>
<script type="text/javascript" src="scripts/dojo_16/dojo/dojo.js"></script>

<script type="text/javascript">
    dojo.require("dojo.parser");
    dojo.require("dojo.io.script");
    dojo.require("dojox.rpc.Service");
    dojo.require("dojox.data.ServiceStore");                    
    dojo.require("dijit.tree.ForestStoreModel");                    
    dojo.require("dijit.Tree");         

    dojo.addOnLoad(function(){

        var mySmd = {
                "SMDVersion": "2.0",
                "id": "http://urlbehindfirewall/testtree/", 
                "description": "This is the service to get to the finder app backend data",

                transport: "JSONP",
                envelope: "URL",
                additionalParameters: true,
                target: "http://urlbehindfirewall/testtree/",               

                services: {
                    "getChildrenNodes": {
                    "target": "getChildrenNodes.php",
                        parameters: [
                            { name: "nodeId", type: "integer"}                             
                        ]
                    }
                }

        };

        var myService = new dojox.rpc.Service(mySmd);

        var myStoreParams = {               
            service : myService.getChildrenNodes,
            idAttribute : "Node_Id",
            labelAttribute : "Display_Name",
            _processResults: function(results, def){                                        
                var _thisStore = this;
                for(i in results){
                    results[i]._loadObject = function(callback){
                        _thisStore.fetch({
                        query: { nodeId: this.Node_Id },
                        onItem: callback
                        });
                    delete this._loadObject;
                    };
                }                   
                return {totalCount: results.length, items: results};                                                
            }
        };


        var myStore = new dojox.data.ServiceStore(myStoreParams);

        //create forestTreeModel
        var treeModelParams = {
            store: myStore,
            deferItemLoadingUntilExpand: true,
            childrenAttrs: ["Children_Nodes"],              
            rootId : 1,
            query: 1
            };

        var treeModel = new dijit.tree.ForestStoreModel(treeModelParams);

        var myTree = new dijit.Tree({
            model: treeModel,
            id: "myTree",
            showRoot: false 
            });

        dojo.byId("treeContainer").appendChild(myTree.domNode);
        myTree.startup();                   
    });

</script>

以下是json数据结构的一个示例:遗憾的是,该服务目前在网络防火墙后面......我将努力建立一个公共版本来进行演示。同时,这是在根节点上搜索的响应,节点1:

    [
   {
      "Node_Id":"2",
      "Display_Name":"LeftNode:2",
      "Secondary_Names":"Parent:1",
      "Obi_Id":"10002",
      "Children_Nodes":[

      ],
      "Has_Children":true,
      "Child_Node_Ids":[
         "5",
         "6",
         "7",
         "8"
      ]
   },
   {
      "Node_Id":"3",
      "Display_Name":"Middle Node:3",
      "Secondary_Names":"Parent:1",
      "Obi_Id":"10003",
      "Children_Nodes":[

      ],
      "Has_Children":true,
      "Child_Node_Ids":[
         "9",
         "10"
      ]
   },
   {
      "Node_Id":"4",
      "Display_Name":"Right Node:4",
      "Secondary_Names":"Parent:1",
      "Obi_Id":"10004",
      "Children_Nodes":[

      ],
      "Has_Children":true,
      "Child_Node_Ids":[
         "11",
         "12"
      ]
   }
]

扩展任何上述节点将获得该节点的子节点 - 因此2将获得5,6,7,8的节点数组。 (对于当前的实现,可能没有必要使用Child_Node_Ids和Children_Nodes,但是不应该破坏任何东西吗?)

所以我确定眼睛现在正在上釉,重申问题 - 创造这个“延迟已经解决”的错误是什么?使用JSONP可以在树中延迟加载吗?不同的json结构会解决我的延迟加载问题吗?是否可以在dojo中重新格式化我的数据以使其有效? (我认为这是_processResults方法的重点...)是否有任何可公开访问的树数据服务在那里练习?

谢谢大家!

1 个答案:

答案 0 :(得分:2)

经过大量的实验和挫折,这些都是我的发现:

是的,使用JSONP可以延迟加载树。是的,将我的数据放入不同的格式有助于解决延迟加载问题。我沿途找到了一些绊脚石,我稍后会提到。

以下是工作实现的代码。首先是服务:

<强> pretendService.php

$callback = $_GET["callback"];
$nodeName = $_GET["node"];
$fileName = "pretendServiceJson/".$nodeName.".js";

print($callback . "(" . file_get_contents($fileName) . ")" );

?>

pretendServiceJson / 0.js - 这是初始数据加载注释它是一个数组!

[
{
   "Node_Id":"0",
   "Display_Name":"",
   "Children":[
      {
         "Node_Id":"1",
         "Display_Name":"node 1",
         "Obi_Id":"02",
         "Secondary_Names":"Blah blah secondary name node 1"
      },
      {
         "Node_Id":"2",
         "Display_Name":"node 2",
         "Obi_Id":"o2",
         "Secondary_Names":"Blah blah secondary name node 2"
      },
      {
         "$ref":"3",
         "Display_Name":"node 3",
         "Obi_Id":"o3",
         "Secondary_Names":"Blah blah secondary name node 3",
         "Children":true
      },
      {
         "Node_Id":"4",
         "Display_Name":"node 4",
         "Obi_Id":"o4",
         "Secondary_Names":"Blah blah secondary name node 4"
      },
      {
         "Node_Id":"5",
         "Display_Name":"node 5",
         "Obi_Id":"o5",
         "Secondary_Names":"Blah blah secondary name node 5"
      }
   ]
}
]

pretendServiceJson / 3.js - 这将是第一个延迟加载的项目 - 注意它是一个对象!!!

{
    "Node_Id": "3",
    "Display_Name": "node 3",
    "Obi_Id": "o3",
    "Secondary_Names": "Blah blah secondary name node 3",
    "Children": [
        {
            "$ref": "6",
            "Display_Name": "node 6",
            "Obi_Id": "o6",
            "Secondary_Names": "Blah blah secondary name 06",
            "Children":true
        },
        {
            "Node_Id": "7",
            "Display_Name": "node 7",
            "Obi_Id": "o7",
            "Secondary_Names": "Blah blah secondary name 07"
        }
    ]
}

还有另一个json文件6.js,但我认为你明白了。终于神奇......

            dojo.require("dojo.parser");
        dojo.require("dojo.io.script");
        dojo.require("dojox.rpc.Service");
        dojo.require("dojox.data.JsonRestStore");                   
        dojo.require("dijit.tree.ForestStoreModel");                    
        dojo.require("dijit.Tree");         

        dojo.addOnLoad(function(){

            var mySmd = {
                    "SMDVersion": "2.0",
                    "id": "http://localhost/pretendService.php", 
                    "description": "This is the service to get to the finder app backend data",

                    transport: "JSONP",
                    envelope: "URL",
                    additionalParameters: true,
                    target: "http://localhost/",                

                    services: {
                        "getNode": {
                        "target": "pretendService.php",
                            parameters: [
                                { name: "node", type: "string"}                            
                            ]
                        }
                    }
            };

            var myService = new dojox.rpc.Service(mySmd);                       

            var myStore = new dojox.data.JsonRestStore({                
                service : myService.getNode,
                idAttribute : "Node_Id",
                labelAttribute : "Display_Name"             
            });     

            //create forestTreeModel
            var treeModelParams = {
                store: myStore,
                deferItemLoadingUntilExpand: true,
                childrenAttrs: ["Children"],                
                //rootId : "0",
                query: "0"
                };

            var treeModel = new dijit.tree.ForestStoreModel(treeModelParams);

            var myTree = new dijit.Tree({
                model: treeModel,
                id: "myTree",
                showRoot: false,
                persist: false
                });

            dojo.byId("treeContainer").appendChild(myTree.domNode);
            myTree.startup();

        });

    </script>
</head>

<body class="tundra">

<div id="treeContainer"></div>

</body>
</html>

这里最重要的一点是,有两种独立的方法(尽我所能遵循)将数据放入商店,然后放入树中。第一个dataload将来自一个fetch,它将需要一个项目数组。随后的延迟加载项(假设您已正确设置服务并且您正在获得响应)将通过loadItem并且该方法将期望一个对象。

如果你有初始加载数据作为对象进入,你将看不到页面上的任何树,尽管在firebug中看到了响应。虽然没有错误。

如果你有一个延迟加载的数据作为一个数组进入你得到一个“TypeError:args.item是未定义的” - 似乎loadItem被调用2x而第二次而不是你的结果对象它是一个空对象。 / p>

如果您在创建商店时定义了rootId,它将不会显示树并为您提供“无法在指定的层次结构中插入节点”错误。

使用JsonRestStore找到了上述所有错误。文档说JsonRestStore继承了ServiceStore的读取功能,但如果我切换出两个存储,我会收到错误“节点无法插入指定的层次结构”。

我对Dojo最大的挫折之一就是缺乏文档来指定如何将实际数据成功解析为数据存储的项目。有很多关于数据存储的灵活性和模块性的讨论,但如何在那里获取数据并使其工作仍然是一个谜,当我抓住稻草时,这个解决方案来到我身边。我希望有一篇关于数据如何成为数据存储项目的文章...? :)

我希望这有助于其他人。快乐的编码。