extjs4 tree

How To Use ExtJS 4 TreePanels with Both Static Data and Model Stores

by CLINT on SEPTEMBER 26, 2011

Wireframe showing tree with both static and dynamically-loaded items

I recently needed to build an “admin” screen for a webapp using ExtJS 4. The wireframe called for a tree to navigate the various admin settings, including settings for individual users (something like what you see at right). To build a tree like this with ExtJS, you usually use the Ext.tree.Panel class (a.k.a., a TreePanel). Unfortunately I couldn’t find any examples of how to make a TreePanel with a mixture of both static and dynamic nodes, so figuring it out took longer than it should have. With that in mind, I thought I’d share what I learned.

A working example of the solution I came up with is shown below (if the embedded version doesn’t appear for some reason, you can open it here). Note that you can actually run the code in your browser and see the results–just click the play button. Or if you’d like to modify it, click the “+” button to copy the code into your own project on JSFiddle.

 

The first thing the code does is define a basic model class for user data, called UserModel:

  1. Ext.define('demo.UserModel', {  
  2.     extend: 'Ext.data.Model',  
  3.     fields: ['id''name''profile_image_url']  
  4. });  

The next thing it does is set up a TreeStore to load the UserModel instances:

  1. var userTreeStore = Ext.create('Ext.data.TreeStore', {  
  2.   
  3.     model: 'demo.UserModel',  
  4.   
  5.     proxy: {  
  6.         type: 'jsonp'// Because it's a cross-domain request  
  7.   
  8.         url : 'https://api.twitter.com/1/lists/members.json?owner_screen_name=Sencha&slug=sencha-team&skip_status=true',  
  9.   
  10.         reader: {  
  11.             type: 'json',  
  12.             root: 'users' // The returned JSON will have array   
  13.                           // of users under a "users" property  
  14.         },  
  15.   
  16.         // Don't want proxy to include these params in request  
  17.         pageParam: undefined,  
  18.         startParam: undefined,  
  19.         pageParam: undefined,  
  20.         pageParam: undefined  
  21.     },  
  22.     ...  
  23. });  

Note that for this example we’ll be using Twitter as our data source for faux “users”; the UserModel fields are set up to match JSON from the Twitter API (specifically, people who are a member of the “Sencha Team” Twitter list) and we need to use a JSONP proxy to make cross-domain requests (i.e., the demo is hosted at jsfiddle.net but it’s connecting to api.twitter.com). Here’s an easy-to-read sample of what the data looks like:

The JSON user data we're getting from Twitter (edited for easier reading)

The next part requires a little more explanation. First, let’s look at the code:

  1. var userTreeStore = Ext.create('Ext.data.TreeStore', {  
  2.     model: 'demo.UserModel',  
  3.     ...      
  4.     listeners: {  
  5.           
  6.         // Each demo.UserModel instance will be automatically   
  7.         // decorated with methods/properties of Ext.data.NodeInterface   
  8.         // (i.e., a "node"). Whenever a UserModel node is appended  
  9.         // to the tree, this TreeStore will fire an "append" event.  
  10.         append: function( thisNode, newChildNode, index, eOpts ) {  
  11.               
  12.             // If the node that's being appended isn't a root node, then we can   
  13.             // assume it's one of our UserModel instances that's been "dressed   
  14.             // up" as a node  
  15.             if( !newChildNode.isRoot() ) {  
  16.                   
  17.                 // The node is a UserModel instance with NodeInterface  
  18.                 // properties and methods added. We want to customize those   
  19.                 // node properties  to control how it appears in the TreePanel.  
  20.                   
  21.                 // A user "item" shouldn't be expandable in the tree  
  22.                 newChildNode.set('leaf'true);  
  23.                   
  24.                 // Use the model's "name" value as the text for each tree item  
  25.                 newChildNode.set('text', newChildNode.get('name'));  
  26.                   
  27.                 // Use the model's profile url as the icon for each tree item  
  28.                 newChildNode.set('icon', newChildNode.get('profile_image_url'));  
  29.                 newChildNode.set('cls''demo-userNode');  
  30.                 newChildNode.set('iconCls''demo-userNodeIcon');  
  31.             }  
  32.         }  
  33.     }  
  34. });  
  35.   
  36. userTreeStore.setRootNode({  
  37.     text: 'Users',  
  38.     leaf: false,  
  39.     expanded: false // If this were true, the store would load itself   
  40.                     // immediately; we do NOT want that to happen  
  41. });  

This doesn’t look like a “normal” Ext.data.Store. For one thing, it has an “append” event handler that receives “nodes”–objects that have methods and properties from the Ext.data.NodeInterface class. Secondly, the store has a setRootNode() method that we’re calling with a config object for NodeInterface. What’s going on?

The important thing to understand here is that a TreeStore manages Ext.data.Model instances–just like any other Store–but it copies NodeInterface methods/properties into every model so that they can be linked together into a hierarchy (i.e., a tree). In this case, every instance of demo.UserModel now has NodeInterface properties like “leaf” and “text” (which indicate if the item should be expandable when it’s displayed in a TreePanel, and what text should be shown).

Next, understand that when we call setRootNode({...}), the TreeStore implicitly creates a generic Ext.data.Model instance for us, adds the NodeInterface method/properties, and then makes it the root node; when the UserModels are loaded, they will be added as “children” to this node. What we end up with is a TreeStore with models organized into a hierarchy, each one having properties that a TreePanel can use for displaying it:

The "userTreeStore" builds a data structure like this

The next thing the code does is create a separate TreeStore with some “static” nodes (again, using NodeInterface config properties).

  1. var settingsTreeStore = Ext.create('Ext.data.TreeStore', {  
  2.     root: {  
  3.         expanded: true,  
  4.         children: [  
  5.             {  
  6.                 text: 'Settings',  
  7.                 leaf: false,  
  8.                 expanded: true,  
  9.                 children: [  
  10.                     {  
  11.                         text: 'System Settings',  
  12.                         leaf: true  
  13.                     },  
  14.                     {  
  15.                         text: 'Appearance',  
  16.                         leaf: true  
  17.                     }   
  18.                 ]  
  19.             }  
  20.         ]  
  21.     }  
  22. });  

Finally, we get the root of our userTreeStore and append it as a child to the “static” TreeStore. Then it’s just a matter of creating the TreePanel.

  1. // Graft our userTreeStore into the settingsTreeStore. Note that the call  
  2. // to .expand() is what triggers the userTreeStore to load its data.  
  3. settingsTreeStore.getRootNode().appendChild(userTreeStore.getRootNode()).expand();  
  4.   
  5. Ext.create('Ext.tree.Panel', {  
  6.     id: 'usersTreePanel',  
  7.     title: 'Admin Control Panel',  
  8.     renderTo: Ext.getBody(),  
  9.     height: 300,  
  10.     width: 300,  
  11.     store: settingsTreeStore,  
  12.     rootVisible: false  
  13. });  

In other words, you can “graft” TreeStores onto other TreeStores–this is the key to creating tree panels with a mixture of different data sources and underlying Models. The prerequisite, however, is understanding the NodeInterface. Hopefully this post will help others learn how to get started with TreePanels and TreeStores more quickly.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值