文档帮助

术语、图标和标签

许多类在使用配置对象创建(实例化)类时使用快捷名称。快捷名称称为别名(如果类扩展了 Ext.Component,则称为xtype)。别名/xtype 列在适用类的类名旁边,以便快速参考。

访问级别

框架类或其成员可以指定为privateprotected。否则,类/成员为publicPublicprotectedprivate是访问描述符,用于传达类或类成员的使用方式和时间。

成员类型

成员语法

以下是一个示例类成员,我们可以对其进行剖析以显示类成员的语法(在本例中,从 Ext.button.Button 类中查看的 lookupComponent 方法)。

lookupComponent ( item ) : Ext.Component
protected

当在 items 配置的初始化期间或添加新项目时,将原始配置对象添加到此容器时调用added,或{@link #insert 插入}。

此方法将传递的对象转换为实例化的子组件。

当需要对子项创建应用特殊处理时,可以在子类中覆盖此方法。

参数

item :  Object

要添加的配置对象。

返回值
Ext.Component

要添加的组件。

让我们看看成员行的每个部分

成员标志

API 文档使用许多标志来进一步传达类成员的功能和意图。标签可以用文本标签、缩写或图标表示。

类图标

- 表示框架类。

- 单例框架类。*有关更多信息,请参阅单例标志。

- 组件类型框架类(Ext JS 框架中任何扩展 Ext.Component 的类)。

- 表示该类、成员或指南在当前查看的版本中是新的。

成员图标

- 表示类型为 config 的类成员

- 表示类型为 property 的类成员

- 表示类型为 method 的类成员

- 表示类型为 event 的类成员

- 表示类型为 theme variable 的类成员

- 表示类型为 theme mixin 的类成员

- 表示该类、成员或指南在当前查看的版本中是新的。

类成员快速导航菜单

在 API 文档页面上的类名下方,有一排按钮,对应于当前类拥有的成员类型。每个按钮都显示了按类型划分的成员数量(此数量会随着过滤器的应用而更新)。单击按钮将导航到该成员部分。将鼠标悬停在成员类型按钮上将显示一个弹出菜单,其中包含该类型的所有成员,以便快速导航。

Getter 和 Setter 方法

与类配置选项相关的 Getter 和 Setter 方法将显示在方法部分以及 API 文档和成员类型菜单的配置部分中,紧挨着它们所操作的配置。Getter 和 Setter 方法文档将位于配置行中,以便于参考。

历史记录栏

您的页面历史记录保存在本地存储中,并显示在顶部标题栏下方(使用可用的空间)。默认情况下,仅显示与您当前查看的产品/版本匹配的页面搜索结果。您可以通过单击历史记录栏右侧的 按钮并选择“全部”单选按钮来扩展显示的内容。这将显示所有产品/版本的最近页面历史记录。

在历史记录配置菜单中,您还会看到最近访问页面的列表。结果会根据“当前产品/版本”和“全部”单选按钮进行过滤。单击 按钮将清除历史记录栏以及本地存储中的历史记录。

如果在历史记录配置菜单中选择了“全部”,则“在历史记录栏中显示产品详细信息”的复选框选项将被启用。选中后,每个历史页面的产品/版本将与页面名称一起显示在历史记录栏中。将鼠标悬停在历史记录栏中的页面名称上也会显示产品/版本作为工具提示。

搜索和过滤器

可以使用页面顶部的搜索字段搜索 API 文档和指南。

在 API 文档页面上,还有一个过滤器输入字段,可以使用过滤器字符串过滤成员行。除了按字符串过滤外,还可以按访问级别、继承和只读属性过滤类成员。这可以通过页面顶部的复选框来完成。

API 类导航树底部的复选框用于过滤类列表,以包含或排除私有类。

单击空的搜索字段将显示您最近的 10 次搜索,以便快速导航。

API 文档类元数据

每个 API 文档页面(除了 Javascript 原语页面)都具有一个与该类相关的元数据菜单视图。此元数据视图将包含以下一项或多项

展开和折叠示例和类成员

可运行示例(Fiddle)在页面上默认展开。您可以使用代码块左上角的箭头单独折叠和展开示例代码块。您还可以使用页面右上角的切换按钮切换所有示例的折叠状态。切换所有状态将在页面加载之间记住。

类成员在页面上默认折叠。您可以使用成员行左侧的箭头图标展开和折叠成员,或者使用右上角的展开/折叠所有切换按钮全局操作。

桌面与移动视图

在较窄的屏幕或浏览器上查看文档将导致视图针对较小的外形尺寸进行优化。桌面视图和“移动”视图之间的主要区别在于

查看类源代码

可以通过单击 API 文档页面顶部的类名来查看类源代码。可以通过单击成员行右侧的“查看源代码”链接来查看类成员的源代码。

Ext JS 7.8.0 - Classic Toolkit


顶部
指南适用于: classic

The Tree Panel 组件是 ExtJS 中最通用的组件之一,是用于在 应用程序 中显示层次结构数据的绝佳工具。Tree Panel 扩展自与 Grid Panel 相同的类,因此,Grid Panel 的大多数优势 - 功能、扩展和插件也可以用于 Tree Panel。可以预期,诸如列、列调整大小、拖放、渲染器、排序和过滤之类的功能在两个组件中都具有类似的工作方式。

让我们从创建一个非常简单的树开始。

此 Tree Panel 将自身渲染到文档主体。我们定义了一个默认情况下展开的根 节点。根节点有三个子节点,前两个是叶节点,这意味着它们不能有任何子节点。第三个节点不是叶节点,并且有一个子叶节点。text 属性用作节点的文本标签。

在内部,树面板将数据存储在 TreeStore 中。上面的示例使用 root 配置作为配置存储的快捷方式。如果我们要单独配置存储,代码将如下所示

var store = Ext.create('Ext.data.TreeStore', {
    root: {
        text: 'Root',
        expanded: true,
        children: [
            {
                text: 'Child 1',
                leaf: true
            },
            {
                text: 'Child 2',
                leaf: true
            },
            ...
        ]
    }
});

Ext.create('Ext.tree.Panel', {
    title: 'Simple Tree',
    store: store,
    ...
});

从 Store 继承

重要的是要注意,TreeStore 的 Store 角色根据与 根节点 相关的链接节点结构管理其自身内容。

仅可见节点在存储中表示。当展开父节点时,其子节点将立即插入到存储中,紧随其后。当该节点折叠时,这些节点将从存储中删除。

由于祖先折叠或过滤而被认为在任何情况下都不可见的节点不会出现在存储的记录集合中。

所有节点仍然可以通过树层次结构访问

存储纯粹用于驱动用户界面。节点以一对一的方式映射到视图中。

要操作存储的内容,您必须始终通过 Ext.data.TreeModel 提供的接口来 插入追加删除 节点到或从其他节点。

某些方法(如 eachcollect)在 TreeStore 级别重新实现,以提供对结构中所有节点的访问,而不仅仅是扁平存储角色中的可见节点。

节点接口

在上面的示例中,我们在树节点上设置了一些不同的属性。但是节点到底是什么?如前所述,树面板绑定到 TreeStore。ExtJS 中的存储管理 Model 实例的集合。

在没有配置的模型类型的情况下,存储将使用 Ext.data.TreeModel 类来封装数据。这是一个模型子类,它已用在树中使用所需的字段、方法和属性进行装饰。以下是显示开发人员工具中节点结构的屏幕截图。

A TreeModel instance illustrating the
TreeModel

要查看节点上可用的所有字段、方法和属性,请参阅 NodeInterface 类的 API 文档。

您为在树中使用而定义的模型应扩展 Ext.data.TreeModel 类。

更改您的树

让我们尝试一些简单的事情。当您将 useArrows 配置设置为 true 时,树面板会隐藏线条并使用箭头作为展开和折叠图标。

Arrows

rootVisible 属性设置为 false 将在视觉上删除根节点。通过这样做,根节点将自动展开。下图显示了相同的树,其中 rootVisible 设置为 falselines 设置为 false。

Root not visible and no lines

多列

由于 树面板 扩展自与 网格面板 相同的基类,因此添加更多列非常容易。

columns 配置期望一个 Ext.grid.column.Column 配置数组,就像 网格面板 一样。唯一的区别是树面板至少需要一个 xtype 为 'treecolumn' 的列。这种类型的列具有树特定的视觉效果,如深度、线条以及展开和折叠图标。典型的树面板只有一个 'treecolumn'。

fields 配置传递给内部创建的存储使用的 Ext.data.Model。请注意,列上的 dataIndex 配置如何映射到我们指定的字段 - namedescription

还值得注意的是,当没有定义列时,树将自动创建一个带有 dataIndex 设置为 'text' 的单个 treecolumn

如果未配置任何定义的列带有 标题文本,则不会显示标题容器。

添加节点

树面板的根节点不需要在初始配置中指定。我们总是可以稍后添加它。

var tree = Ext.create('Ext.tree.Panel');
tree.setRootNode({
    text: 'Root',
    expanded: true,
    children: [{
        text: 'Child 1',
        leaf: true
    }, {
        text: 'Child 2',
        leaf: true
    }]
});

虽然这对只有几个静态节点的非常小的树很有用,但大多数树面板将包含更多节点。所以让我们看看如何以编程方式向树添加新节点。

var root = tree.getRootNode();

var parent = root.appendChild({
    text: 'Parent 1'
});

parent.appendChild({
    text: 'Child 3',
    leaf: true
});

parent.expand();

每个不是叶子节点的节点都有一个 appendChild 方法,它接受一个节点或节点的配置对象作为第一个参数,并返回附加的节点。上面的例子还调用了 expand 方法来展开新创建的父节点。

Appending to the tree

同样有用的是在创建新父节点时能够内联定义子节点。以下代码给了我们相同的结果。

var parent = root.appendChild({
    text: 'Parent 1',
    expanded: true,
    children: [{
        text: 'Child 3',
        leaf: true
    }]
});

有时我们想将节点插入树中的特定位置,而不是附加它。除了 appendChild 方法外,Ext.data.NodeInterface 还提供了 insertBeforeinsertChild 方法。

var child = parent.insertChild(0, {
    text: 'Child 2.5',
    leaf: true
});

parent.insertBefore({
    text: 'Child 2.75',
    leaf: true
}, child.nextSibling);

insertChild 方法需要一个插入子节点的索引。insertBefore 方法需要一个引用节点。新节点将插入引用节点之前。

Inserting children into the tree

TreeModel 还提供了节点上的几个其他属性,可用于引用其他节点。

通过代理加载和保存

加载和保存树数据比处理平面数据要复杂一些,因为需要所有字段来表示树的层次结构。本节将解释使用树数据的复杂性。

TreeModel 字段

在使用树数据时,首先要理解的是 TreeModel 类字段的工作原理。树中的每个节点只是一个 Model 实例,用 TreeModel 的字段和方法装饰。假设一个应用程序有一个名为“Person”的模型。一个人只有两个字段 - “id”和“name”。

为了在 TreeStore 中使用它,扩展 TreeModel

Ext.define('Person', {
    extend: 'Ext.data.TreeModel',
    fields: ['id', {
        name: 'name',
        type: 'string'
    }]
});

生成的模型有许多额外的字段,这些字段用于管理节点在树结构中的上下文。

console.log(store.getRoot().getFields().length); // outputs '27'

Person 模型的原型只是通过扩展 TreeModel 就添加了 25 个额外的字段。

那么这 25 个额外的字段到底是什么,它们的作用是什么?快速查看 TreeModel 源代码就会发现它用以下字段装饰了模型。这些字段在内部用于存储与树的结构和状态相关的信息。

{
    name: 'parentId',
    type: idType,
    defaultValue: null,
    useNull: idField.useNull
}, {
    name: 'index',
    type: 'int',
    defaultValue: -1,
    persist: false,
    convert: null
}, {
    name: 'depth',
    type: 'int',
    defaultValue: 0,
    persist: false,
    convert: null
}, {
    name: 'expanded',
    type: 'bool',
    defaultValue: false,
    persist: false,
    convert: null
}, {
    name: 'expandable',
    type: 'bool',
    defaultValue: true,
    persist: false,
    convert: null
}, {
    name: 'checked',
    type: 'auto',
    defaultValue: null,
    persist: false,
    convert: null
}, {
    name: 'leaf',
    type: 'bool',
    defaultValue: false
}, {
    name: 'cls',
    type: 'string',
    defaultValue: '',
    persist: false,
    convert: null
}, {
    name: 'iconCls',
    type: 'string',
    defaultValue: '',
    persist: false,
    convert: null
}, {
    name: 'icon',
    type: 'string',
    defaultValue: '',
    persist: false,
    convert: null
}, {
    name: 'root',
    type: 'boolean',
    defaultValue: false,
    persist: false,
    convert: null
}, {
    name: 'isLast',
    type: 'boolean',
    defaultValue: false,
    persist: false,
    convert: null
}, {
    name: 'isFirst',
    type: 'boolean',
    defaultValue: false,
    persist: false,
    convert: null
}, {
    name: 'allowDrop',
    type: 'boolean',
    defaultValue: true,
    persist: false,
    convert: null
}, {
    name: 'allowDrag',
    type: 'boolean',
    defaultValue: true,
    persist: false,
    convert: null
}, {
    name: 'loaded',
    type: 'boolean',
    defaultValue: false,
    persist: false,
    convert: null
}, {
    name: 'loading',
    type: 'boolean',
    defaultValue: false,
    persist: false,
    convert: null
}, {
    name: 'href',
    type: 'string',
    defaultValue: '',
    persist: false,
    convert: null
}, {
    name: 'hrefTarget',
    type: 'string',
    defaultValue: '',
    persist: false,
    convert: null
}, {
    name: 'qtip',
    type: 'string',
    defaultValue: '',
    persist: false,
    convert: null
}, {
    name: 'qtitle',
    type: 'string',
    defaultValue: '',
    persist: false,
    convert: null
}, {
    name: 'qshowDelay',
    type: 'int',
    defaultValue: 0,
    persist: false,
    convert: null
}, {
    name: 'children',
    type: 'auto',
    defaultValue: null,
    persist: false,
    convert: null
}, {
    name: 'visible',
    type: 'boolean',
    defaultValue: true,
    persist: false,
}, {
    name: 'text',
    type: 'string',
    persist: 'false
}

TreeModel 字段是保留名称

需要注意的是,所有上述字段名都应视为“保留”名称。例如,如果模型打算在树中使用,则不允许在模型中使用名为“parentId”的字段,因为模型的字段将覆盖 TreeModel 字段。此规则的例外情况是,当有合法需要覆盖字段的持久性时。

持久字段与非持久字段

大多数 TreeModel 字段默认设置为 persist: false。这意味着它们默认是非持久字段。非持久字段不会在调用 TreeStore 的 sync 方法或在模型上调用 save() 时通过代理保存。在大多数情况下,这些字段中的大多数可以保留其默认持久性设置,但有些情况下需要覆盖某些字段的持久性。
以下示例演示了如何覆盖 persistence TreeModel 字段。覆盖 TreeModel 字段时,重要的是只更改 persist 属性。nametypedefaultValue 属性永远不应该更改。

    // overriding the persistence of TreeModel fields in a Model definition
    Ext.define('Person', {
        extend: 'Ext.data.TreeModel',
        fields: [
            // Person fields
            { name: 'id', type: 'int' },
            { name: 'name', type: 'string' }

            // override a non-persistent TreeModel field to make it persistent
            { name: 'iconCls', type: 'string',  defaultValue: null, persist: true }
        ]
    });

让我们更深入地了解每个 TreeModel 字段以及可能需要覆盖其 persist 属性的场景。在以下每个示例中,假设使用 Server Proxy,除非另有说明。

默认情况下持久化

  • parentId - 用于存储节点父节点的 id。此字段应始终持久化,不应覆盖。
  • leaf - 用于指示节点是叶子节点,因此不能向其追加子节点。此字段通常不需要覆盖。

默认情况下不持久化

  • index - 用于存储节点在其父节点内的顺序。当节点被 插入移除 时,插入或移除点之后的所有兄弟节点的索引都会更新。如果需要,应用程序可以使用此字段来持久化节点的排序。

    但是,如果服务器使用不同的方法来存储顺序,则可能更适合将索引字段保留为非持久化。当使用 WebStorage Proxy 时,如果需要存储顺序,则必须覆盖此字段以使其持久化。

    此外,如果使用客户端 排序,建议将 index 字段保留为非持久化,因为排序会更新所有已排序节点的索引,如果 persist 属性为 true,这会导致它们在下次同步或保存时被持久化。

  • depth - 用于存储节点在树层次结构中的深度。如果服务器需要存储深度字段,请覆盖此字段以打开持久化。当使用 WebStorage Proxy 时,建议不要覆盖 depth 字段的持久化,因为它不需要正确存储树结构,只会占用额外的空间。

  • checked - 如果树使用 复选框功能,则应覆盖此字段以使其持久化。
  • expanded - 用于存储节点的展开/折叠状态。此字段通常不需要覆盖。
  • expandable - 在内部使用,用于指示此节点是可展开的。不要覆盖此字段的持久化。
  • cls - 用于在节点在 TreePanel 中呈现时向其应用 CSS 类。如果需要,请覆盖此字段以使其持久化。
  • iconCls - 用于在节点在 TreePanel 中呈现时向其图标应用 css 类。如果需要,请覆盖此字段以使其持久化。
  • icon - 用于在节点在 TreePanel 中呈现时向其应用自定义图标。如果需要,请覆盖此字段以使其持久化。
  • root - 用于指示此节点是根节点。此字段不应该被覆盖。
  • isLast - 用于指示此节点是其兄弟节点中的最后一个。此字段通常不需要覆盖。
  • isFirst - 用于指示此节点是其兄弟节点中的第一个。此字段通常不需要覆盖。
  • allowDrop - 在内部使用,用于拒绝在节点上放置。不要覆盖此字段的持久化。
  • allowDrag - 在内部使用,用于拒绝拖动节点。不要覆盖此字段的持久化。
  • loaded - 在内部使用,用于指示节点的子节点已加载。
    不要覆盖此字段的持久化。
  • loading - 在内部使用,用于指示代理正在加载节点的子节点。不要覆盖此字段的持久化。
  • href - 用于指定节点应链接到的 URL。如果需要,请覆盖以使其持久化。
  • hrefTarget - 用于指定 href 的目标。如果需要,请覆盖以使其持久化。
  • qtip - 用于向节点添加 工具提示 文本。如果需要,请覆盖以使其持久化。
  • qtitle - 用于指定 tooltip 的标题。如果需要,请覆盖以使其持久化。
  • children - 在加载节点及其子节点时,在内部使用一次请求。不要覆盖此字段的持久化。

加载数据

有两种方法可以加载树数据。第一种是让代理一次性获取整个树。对于大型树,一次性加载所有内容并不理想,因此可能更适合使用第二种方法 - 在展开每个节点时动态加载其子节点。

加载整个树

在内部,树仅在响应节点展开时加载数据。但是,如果代理检索包含整个树结构的嵌套对象,则可以加载整个层次结构。为此,将 TreeStore 的根节点初始化为 expanded

Ext.define('Person', {
    extend: 'Ext.data.Model',
    fields: [
        { name: 'id', type: 'int' },
        { name: 'name', type: 'string' }
    ],
    proxy: {
        type: 'ajax',
        api: {
            create: 'createPersons',
            read: 'readPersons',
            update: 'updatePersons',
            destroy: 'destroyPersons'
        }
    }

});

var store = Ext.create('Ext.data.TreeStore', {
    model: 'Person',
    root: {
        name: 'People',
        expanded: true
    }
});

Ext.create('Ext.tree.Panel', {
    renderTo: document.body,
    width: 300,
    height: 200,
    title: 'People',
    store: store,
    columns: [{
        xtype: 'treecolumn', 
        header: 'Name', 
        dataIndex: 'name', 
        flex: 1
    }]
});

假设 readPersons URL 返回以下 JSON 对象

{
    "success": true,
    "children": [
        { "id": 1, "name": "Phil", "leaf": true },
        { "id": 2, "name": "Nico", "expanded": true, "children": [
            { "id": 3, "name": "Mitchell", "leaf": true }
        ]},
        { "id": 4, "name": "Sue", "loaded": true }
    ]
}

这就是加载整个树所需的全部内容。

Tree with Bulk Loaded Data

需要注意的重要事项

  • 对于所有没有子节点的非叶子节点(例如,上面名为 Sue 的 Person),服务器响应必须将 loaded 属性设置为 true。否则,代理将在展开这些节点时尝试加载其子节点。
  • 那么问题就来了 - 如果服务器被允许在 JSON 响应中的节点上设置 loaded 属性,它是否可以设置任何其他非持久性字段?答案是肯定的 - 有时可以。在上面的示例中,名为“Nico”的节点的 expanded 字段被设置为 true,以便它最初在树面板中展开显示。应谨慎操作,因为在某些情况下这样做不合适,可能会导致严重问题,例如在非根节点上设置 root 属性。一般来说,loadedexpanded 是服务器在 JSON 响应中设置非持久性字段的唯一推荐情况。

动态加载子节点

对于较大的树,可能希望仅加载树的一部分,即仅在父节点展开时加载子节点。假设在上面的示例中,名为“Sue”的节点的 loaded 字段没有被服务器响应设置为 true。树将显示一个展开图标,位于该节点旁边。当节点展开时,代理将向 readPersons URL 发出另一个请求,该请求类似于以下内容

/readPersons?node=4

这告诉服务器检索 id 为 4 的节点的子节点。数据应以与用于加载根节点的数据相同的格式返回。

{
    "success": true,
    "children": [
        { "id": 5, "name": "Evan", "leaf": true }
    ]
}

现在树看起来像这样

Tree with Dynamically Loaded Node

保存数据

创建、更新和删除节点由代理自动无缝处理。

创建新节点

// Create a new node and append it to the tree:
var newPerson = Ext.create('Person', { name: 'Nige', leaf: true });
store.getNodeById(2).appendChild(newPerson);

由于代理直接在模型上定义,因此可以使用模型的 save() 方法来持久化数据。

newPerson.save();

更新现有节点

store.getNodeById(1).set('name', 'Philip');

删除节点

store.getRootNode().lastChild.remove();

批量操作

在创建、更新和删除多个节点后,可以通过调用 TreeStore 的 sync() 方法将所有这些节点持久化到一个操作中。

store.sync();

过滤树数据

远程过滤与任何其他存储类型的工作方式相同。过滤器被编码并传递给服务器,服务器必须只响应所需的数据。

树的本地过滤与网格中过滤扁平数据集不同。

过滤条件可能包含位于被过滤条件排除的父节点下的节点。

默认情况下,通过过滤条件但其父节点未通过的节点不包含在存储中。这是自上而下的过滤,可以使用 filterer 配置重新配置。

要强制包含通过过滤器的节点的祖先节点,请将 filterer 配置为 bottomup

有关此类型过滤的说明,请参阅 过滤树 示例。

这种过滤需要遍历树结构,以在更新视图之前确定所有节点的可见性状态。

对于应用程序代码对单个记录进行细粒度或编程过滤,您可以设置 TreeModelvisible 字段,视图将根据该设置显示或隐藏节点,具体取决于该值。

Ext JS 7.8.0 - Classic Toolkit