Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / Panel5.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-tree-Panel'>/**
19 </span> * The TreePanel provides tree-structured UI representation of tree-structured data.
20  * A TreePanel must be bound to a {@link Ext.data.TreeStore}. TreePanel's support
21  * multiple columns through the {@link #columns} configuration.
22  *
23  * Simple TreePanel using inline data:
24  *
25  *     @example
26  *     var store = Ext.create('Ext.data.TreeStore', {
27  *         root: {
28  *             expanded: true,
29  *             children: [
30  *                 { text: &quot;detention&quot;, leaf: true },
31  *                 { text: &quot;homework&quot;, expanded: true, children: [
32  *                     { text: &quot;book report&quot;, leaf: true },
33  *                     { text: &quot;alegrbra&quot;, leaf: true}
34  *                 ] },
35  *                 { text: &quot;buy lottery tickets&quot;, leaf: true }
36  *             ]
37  *         }
38  *     });
39  *
40  *     Ext.create('Ext.tree.Panel', {
41  *         title: 'Simple Tree',
42  *         width: 200,
43  *         height: 150,
44  *         store: store,
45  *         rootVisible: false,
46  *         renderTo: Ext.getBody()
47  *     });
48  *
49  * For the tree node config options (like `text`, `leaf`, `expanded`), see the documentation of
50  * {@link Ext.data.NodeInterface NodeInterface} config options.
51  */
52 Ext.define('Ext.tree.Panel', {
53     extend: 'Ext.panel.Table',
54     alias: 'widget.treepanel',
55     alternateClassName: ['Ext.tree.TreePanel', 'Ext.TreePanel'],
56     requires: ['Ext.tree.View', 'Ext.selection.TreeModel', 'Ext.tree.Column'],
57     viewType: 'treeview',
58     selType: 'treemodel',
59
60     treeCls: Ext.baseCSSPrefix + 'tree-panel',
61
62     deferRowRender: false,
63
64 <span id='Ext-tree-Panel-cfg-lines'>    /**
65 </span>     * @cfg {Boolean} lines False to disable tree lines.
66      */
67     lines: true,
68
69 <span id='Ext-tree-Panel-cfg-useArrows'>    /**
70 </span>     * @cfg {Boolean} useArrows True to use Vista-style arrows in the tree.
71      */
72     useArrows: false,
73
74 <span id='Ext-tree-Panel-cfg-singleExpand'>    /**
75 </span>     * @cfg {Boolean} singleExpand True if only 1 node per branch may be expanded.
76      */
77     singleExpand: false,
78
79     ddConfig: {
80         enableDrag: true,
81         enableDrop: true
82     },
83
84 <span id='Ext-tree-Panel-cfg-animate'>    /**
85 </span>     * @cfg {Boolean} animate True to enable animated expand/collapse. Defaults to the value of {@link Ext#enableFx}.
86      */
87
88 <span id='Ext-tree-Panel-cfg-rootVisible'>    /**
89 </span>     * @cfg {Boolean} rootVisible False to hide the root node.
90      */
91     rootVisible: true,
92
93 <span id='Ext-tree-Panel-cfg-displayField'>    /**
94 </span>     * @cfg {Boolean} displayField The field inside the model that will be used as the node's text.
95      */
96     displayField: 'text',
97
98 <span id='Ext-tree-Panel-cfg-root'>    /**
99 </span>     * @cfg {Ext.data.Model/Ext.data.NodeInterface/Object} root
100      * Allows you to not specify a store on this TreePanel. This is useful for creating a simple tree with preloaded
101      * data without having to specify a TreeStore and Model. A store and model will be created and root will be passed
102      * to that store. For example:
103      *
104      *     Ext.create('Ext.tree.Panel', {
105      *         title: 'Simple Tree',
106      *         root: {
107      *             text: &quot;Root node&quot;,
108      *             expanded: true,
109      *             children: [
110      *                 { text: &quot;Child 1&quot;, leaf: true },
111      *                 { text: &quot;Child 2&quot;, leaf: true }
112      *             ]
113      *         },
114      *         renderTo: Ext.getBody()
115      *     });
116      */
117     root: null,
118
119     // Required for the Lockable Mixin. These are the configurations which will be copied to the
120     // normal and locked sub tablepanels
121     normalCfgCopy: ['displayField', 'root', 'singleExpand', 'useArrows', 'lines', 'rootVisible', 'scroll'],
122     lockedCfgCopy: ['displayField', 'root', 'singleExpand', 'useArrows', 'lines', 'rootVisible'],
123
124 <span id='Ext-tree-Panel-cfg-hideHeaders'>    /**
125 </span>     * @cfg {Boolean} hideHeaders True to hide the headers. Defaults to `undefined`.
126      */
127
128 <span id='Ext-tree-Panel-cfg-folderSort'>    /**
129 </span>     * @cfg {Boolean} folderSort True to automatically prepend a leaf sorter to the store. Defaults to `undefined`.
130      */
131
132     constructor: function(config) {
133         config = config || {};
134         if (config.animate === undefined) {
135             config.animate = Ext.enableFx;
136         }
137         this.enableAnimations = config.animate;
138         delete config.animate;
139
140         this.callParent([config]);
141     },
142
143     initComponent: function() {
144         var me = this,
145             cls = [me.treeCls];
146
147         if (me.useArrows) {
148             cls.push(Ext.baseCSSPrefix + 'tree-arrows');
149             me.lines = false;
150         }
151
152         if (me.lines) {
153             cls.push(Ext.baseCSSPrefix + 'tree-lines');
154         } else if (!me.useArrows) {
155             cls.push(Ext.baseCSSPrefix + 'tree-no-lines');
156         }
157
158         if (Ext.isString(me.store)) {
159             me.store = Ext.StoreMgr.lookup(me.store);
160         } else if (!me.store || Ext.isObject(me.store) &amp;&amp; !me.store.isStore) {
161             me.store = Ext.create('Ext.data.TreeStore', Ext.apply({}, me.store || {}, {
162                 root: me.root,
163                 fields: me.fields,
164                 model: me.model,
165                 folderSort: me.folderSort
166             }));
167         } else if (me.root) {
168             me.store = Ext.data.StoreManager.lookup(me.store);
169             me.store.setRootNode(me.root);
170             if (me.folderSort !== undefined) {
171                 me.store.folderSort = me.folderSort;
172                 me.store.sort();
173             }
174         }
175
176         // I'm not sure if we want to this. It might be confusing
177         // if (me.initialConfig.rootVisible === undefined &amp;&amp; !me.getRootNode()) {
178         //     me.rootVisible = false;
179         // }
180
181         me.viewConfig = Ext.applyIf(me.viewConfig || {}, {
182             rootVisible: me.rootVisible,
183             animate: me.enableAnimations,
184             singleExpand: me.singleExpand,
185             node: me.store.getRootNode(),
186             hideHeaders: me.hideHeaders
187         });
188
189         me.mon(me.store, {
190             scope: me,
191             rootchange: me.onRootChange,
192             clear: me.onClear
193         });
194
195         me.relayEvents(me.store, [
196 <span id='Ext-tree-Panel-event-beforeload'>            /**
197 </span>             * @event beforeload
198              * @alias Ext.data.Store#beforeload
199              */
200             'beforeload',
201
202 <span id='Ext-tree-Panel-event-load'>            /**
203 </span>             * @event load
204              * @alias Ext.data.Store#load
205              */
206             'load'
207         ]);
208
209         me.store.on({
210 <span id='Ext-tree-Panel-event-itemappend'>            /**
211 </span>             * @event itemappend
212              * @alias Ext.data.TreeStore#append
213              */
214             append: me.createRelayer('itemappend'),
215
216 <span id='Ext-tree-Panel-event-itemremove'>            /**
217 </span>             * @event itemremove
218              * @alias Ext.data.TreeStore#remove
219              */
220             remove: me.createRelayer('itemremove'),
221
222 <span id='Ext-tree-Panel-event-itemmove'>            /**
223 </span>             * @event itemmove
224              * @alias Ext.data.TreeStore#move
225              */
226             move: me.createRelayer('itemmove'),
227
228 <span id='Ext-tree-Panel-event-iteminsert'>            /**
229 </span>             * @event iteminsert
230              * @alias Ext.data.TreeStore#insert
231              */
232             insert: me.createRelayer('iteminsert'),
233
234 <span id='Ext-tree-Panel-event-beforeitemappend'>            /**
235 </span>             * @event beforeitemappend
236              * @alias Ext.data.TreeStore#beforeappend
237              */
238             beforeappend: me.createRelayer('beforeitemappend'),
239
240 <span id='Ext-tree-Panel-event-beforeitemremove'>            /**
241 </span>             * @event beforeitemremove
242              * @alias Ext.data.TreeStore#beforeremove
243              */
244             beforeremove: me.createRelayer('beforeitemremove'),
245
246 <span id='Ext-tree-Panel-event-beforeitemmove'>            /**
247 </span>             * @event beforeitemmove
248              * @alias Ext.data.TreeStore#beforemove
249              */
250             beforemove: me.createRelayer('beforeitemmove'),
251
252 <span id='Ext-tree-Panel-event-beforeiteminsert'>            /**
253 </span>             * @event beforeiteminsert
254              * @alias Ext.data.TreeStore#beforeinsert
255              */
256             beforeinsert: me.createRelayer('beforeiteminsert'),
257
258 <span id='Ext-tree-Panel-event-itemexpand'>            /**
259 </span>             * @event itemexpand
260              * @alias Ext.data.TreeStore#expand
261              */
262             expand: me.createRelayer('itemexpand'),
263
264 <span id='Ext-tree-Panel-event-itemcollapse'>            /**
265 </span>             * @event itemcollapse
266              * @alias Ext.data.TreeStore#collapse
267              */
268             collapse: me.createRelayer('itemcollapse'),
269
270 <span id='Ext-tree-Panel-event-beforeitemexpand'>            /**
271 </span>             * @event beforeitemexpand
272              * @alias Ext.data.TreeStore#beforeexpand
273              */
274             beforeexpand: me.createRelayer('beforeitemexpand'),
275
276 <span id='Ext-tree-Panel-event-beforeitemcollapse'>            /**
277 </span>             * @event beforeitemcollapse
278              * @alias Ext.data.TreeStore#beforecollapse
279              */
280             beforecollapse: me.createRelayer('beforeitemcollapse')
281         });
282
283         // If the user specifies the headers collection manually then dont inject our own
284         if (!me.columns) {
285             if (me.initialConfig.hideHeaders === undefined) {
286                 me.hideHeaders = true;
287             }
288             me.columns = [{
289                 xtype    : 'treecolumn',
290                 text     : 'Name',
291                 flex     : 1,
292                 dataIndex: me.displayField
293             }];
294         }
295
296         if (me.cls) {
297             cls.push(me.cls);
298         }
299         me.cls = cls.join(' ');
300         me.callParent();
301
302         me.relayEvents(me.getView(), [
303 <span id='Ext-tree-Panel-event-checkchange'>            /**
304 </span>             * @event checkchange
305              * Fires when a node with a checkbox's checked property changes
306              * @param {Ext.data.Model} node The node who's checked property was changed
307              * @param {Boolean} checked The node's new checked state
308              */
309             'checkchange'
310         ]);
311
312         // If the root is not visible and there is no rootnode defined, then just lets load the store
313         if (!me.getView().rootVisible &amp;&amp; !me.getRootNode()) {
314             me.setRootNode({
315                 expanded: true
316             });
317         }
318     },
319
320     onClear: function(){
321         this.view.onClear();
322     },
323
324 <span id='Ext-tree-Panel-method-setRootNode'>    /**
325 </span>     * Sets root node of this tree.
326      * @param {Ext.data.Model/Ext.data.NodeInterface/Object} root
327      * @return {Ext.data.NodeInterface} The new root
328      */
329     setRootNode: function() {
330         return this.store.setRootNode.apply(this.store, arguments);
331     },
332
333 <span id='Ext-tree-Panel-method-getRootNode'>    /**
334 </span>     * Returns the root node for this tree.
335      * @return {Ext.data.NodeInterface}
336      */
337     getRootNode: function() {
338         return this.store.getRootNode();
339     },
340
341     onRootChange: function(root) {
342         this.view.setRootNode(root);
343     },
344
345 <span id='Ext-tree-Panel-method-getChecked'>    /**
346 </span>     * Retrieve an array of checked records.
347      * @return {Ext.data.Model[]} An array containing the checked records
348      */
349     getChecked: function() {
350         return this.getView().getChecked();
351     },
352
353     isItemChecked: function(rec) {
354         return rec.get('checked');
355     },
356
357 <span id='Ext-tree-Panel-method-expandAll'>    /**
358 </span>     * Expand all nodes
359      * @param {Function} callback (optional) A function to execute when the expand finishes.
360      * @param {Object} scope (optional) The scope of the callback function
361      */
362     expandAll : function(callback, scope) {
363         var root = this.getRootNode(),
364             animate = this.enableAnimations,
365             view = this.getView();
366         if (root) {
367             if (!animate) {
368                 view.beginBulkUpdate();
369             }
370             root.expand(true, callback, scope);
371             if (!animate) {
372                 view.endBulkUpdate();
373             }
374         }
375     },
376
377 <span id='Ext-tree-Panel-method-collapseAll'>    /**
378 </span>     * Collapse all nodes
379      * @param {Function} callback (optional) A function to execute when the collapse finishes.
380      * @param {Object} scope (optional) The scope of the callback function
381      */
382     collapseAll : function(callback, scope) {
383         var root = this.getRootNode(),
384             animate = this.enableAnimations,
385             view = this.getView();
386
387         if (root) {
388             if (!animate) {
389                 view.beginBulkUpdate();
390             }
391             if (view.rootVisible) {
392                 root.collapse(true, callback, scope);
393             } else {
394                 root.collapseChildren(true, callback, scope);
395             }
396             if (!animate) {
397                 view.endBulkUpdate();
398             }
399         }
400     },
401
402 <span id='Ext-tree-Panel-method-expandPath'>    /**
403 </span>     * Expand the tree to the path of a particular node.
404      * @param {String} path The path to expand. The path should include a leading separator.
405      * @param {String} field (optional) The field to get the data from. Defaults to the model idProperty.
406      * @param {String} separator (optional) A separator to use. Defaults to `'/'`.
407      * @param {Function} callback (optional) A function to execute when the expand finishes. The callback will be called with
408      * (success, lastNode) where success is if the expand was successful and lastNode is the last node that was expanded.
409      * @param {Object} scope (optional) The scope of the callback function
410      */
411     expandPath: function(path, field, separator, callback, scope) {
412         var me = this,
413             current = me.getRootNode(),
414             index = 1,
415             view = me.getView(),
416             keys,
417             expander;
418
419         field = field || me.getRootNode().idProperty;
420         separator = separator || '/';
421
422         if (Ext.isEmpty(path)) {
423             Ext.callback(callback, scope || me, [false, null]);
424             return;
425         }
426
427         keys = path.split(separator);
428         if (current.get(field) != keys[1]) {
429             // invalid root
430             Ext.callback(callback, scope || me, [false, current]);
431             return;
432         }
433
434         expander = function(){
435             if (++index === keys.length) {
436                 Ext.callback(callback, scope || me, [true, current]);
437                 return;
438             }
439             var node = current.findChild(field, keys[index]);
440             if (!node) {
441                 Ext.callback(callback, scope || me, [false, current]);
442                 return;
443             }
444             current = node;
445             current.expand(false, expander);
446         };
447         current.expand(false, expander);
448     },
449
450 <span id='Ext-tree-Panel-method-selectPath'>    /**
451 </span>     * Expand the tree to the path of a particular node, then select it.
452      * @param {String} path The path to select. The path should include a leading separator.
453      * @param {String} field (optional) The field to get the data from. Defaults to the model idProperty.
454      * @param {String} separator (optional) A separator to use. Defaults to `'/'`.
455      * @param {Function} callback (optional) A function to execute when the select finishes. The callback will be called with
456      * (bSuccess, oLastNode) where bSuccess is if the select was successful and oLastNode is the last node that was expanded.
457      * @param {Object} scope (optional) The scope of the callback function
458      */
459     selectPath: function(path, field, separator, callback, scope) {
460         var me = this,
461             keys,
462             last;
463
464         field = field || me.getRootNode().idProperty;
465         separator = separator || '/';
466
467         keys = path.split(separator);
468         last = keys.pop();
469
470         me.expandPath(keys.join(separator), field, separator, function(success, node){
471             var doSuccess = false;
472             if (success &amp;&amp; node) {
473                 node = node.findChild(field, last);
474                 if (node) {
475                     me.getSelectionModel().select(node);
476                     Ext.callback(callback, scope || me, [true, node]);
477                     doSuccess = true;
478                 }
479             } else if (node === me.getRootNode()) {
480                 doSuccess = true;
481             }
482             Ext.callback(callback, scope || me, [doSuccess, node]);
483         }, me);
484     }
485 });
486 </pre>
487 </body>
488 </html>