许多类在使用配置对象创建(实例化)类时都有快捷名称。快捷名称被称为 alias
(或如果类继承自 Ext.Component,则称为 xtype
)。别名/xtype 列在适用类的类名旁边,以便快速参考。
框架类或其成员可以指定为 private
或 protected
。否则,类/成员为 public
。Public
、protected
和 private
是访问描述符,用于说明类或类成员应如何以及何时使用。
Public 类和类成员可供任何其他类或应用程序代码使用,并且在主要产品版本中可以作为稳定和持久的内容依赖。Public 类和成员可以通过子类安全地扩展。
Protected 类成员是稳定的 public
成员,旨在由拥有类或其子类使用。Protected 成员可以通过子类安全地扩展。
Private 类和类成员在框架内部使用,不供应用程序开发人员使用。Private 类和成员可能随时更改或从框架中省略,恕不另行通知,不应在应用程序逻辑中依赖。
static
标签。*请参阅下面的“静态”。下面是一个类成员示例,我们可以剖析它以展示类成员的语法(在本例中为从 Ext.button.Button 类查看的 lookupComponent 方法)。
让我们看一下成员行的每个部分
lookupComponent
)( item )
)Ext.Component
)。对于除 undefined
之外不返回任何内容的方法,可以省略此项,或者可以显示为以正斜杠 /
分隔的多个可能值,表示返回的内容可能取决于方法调用的结果(即,如果 get 方法调用成功,方法可能返回 Component,如果失败,则返回 false
,这将显示为 Ext.Component/Boolean
)。PROTECTED
- 请参阅下面的“标志”部分)Ext.container.Container
)。如果成员源自当前类,则源类将显示为蓝色链接;如果成员从祖先类或混入类继承,则显示为灰色。view source
)item : Object
)。undefined
之外的值,则“返回值”部分将注明返回的类或对象类型以及描述(示例中的 Ext.Component
)Available since 3.4.0
- 示例中未显示),紧跟在成员描述之后Defaults to: false
)API 文档使用许多标志来进一步说明类成员的功能和意图。标签可以用文本标签、缩写或图标表示。
classInstance.method1().method2().etc();
false
,则标记为可阻止的事件将不会触发- 表示框架类
- 单例框架类。*有关更多信息,请参阅单例标志
- 组件类型框架类(Ext JS 框架中任何继承自 Ext.Component 的类)
- 表示类、成员或指南在当前查看的版本中是新的
- 表示类型为 config
的类成员
- 表示类型为 property
的类成员
- 表示类型为 method
的类成员
- 表示类型为 event
的类成员
- 表示类型为 theme variable
的类成员
- 表示类型为 theme mixin
的类成员
- 表示类、成员或指南在当前查看的版本中是新的
在 API 文档页面的类名正下方是一行按钮,对应于当前类拥有的成员类型。每个按钮显示按类型划分的成员计数(此计数会随着应用过滤器而更新)。单击按钮会将您导航到该成员部分。将鼠标悬停在成员类型按钮上将显示该类型的所有成员的弹出菜单,以便快速导航。
与类配置选项相关的 getter 和 setter 方法将显示在方法部分以及 API 文档和成员类型菜单的配置部分中,紧挨着它们所操作的配置下方。getter 和 setter 方法文档将在配置行中找到,以便于参考。
您的页面历史记录保存在本地存储中,并显示在顶部标题栏正下方(使用可用的实际空间)。默认情况下,显示的唯一搜索结果是与您当前查看的产品/版本匹配的页面。您可以通过单击历史记录栏右侧的 按钮并选择“全部”单选选项来展开显示的内容。这将显示所有产品/版本在历史记录栏中的所有最近页面。
在历史记录配置菜单中,您还将看到最近访问页面的列表。结果按“当前产品/版本”和“全部”单选选项进行过滤。单击 按钮将清除历史记录栏以及本地存储中保存的历史记录。
如果在历史记录配置菜单中选择“全部”,则将启用“在历史记录栏中显示产品详细信息”复选框选项。选中后,每个历史页面的产品/版本将与页面名称一起显示在历史记录栏中。将光标悬停在历史记录栏中的页面名称上也会将产品/版本显示为工具提示。
可以使用页面顶部的搜索字段搜索 API 文档和指南。
在 API 文档页面上,还有一个过滤器输入字段,用于使用过滤器字符串过滤成员行。除了按字符串过滤外,您还可以按访问级别、继承和只读过滤类成员。这可以通过使用页面顶部的复选框来完成。
API 类导航树底部的复选框过滤类列表,以包含或排除私有类。
单击空的搜索字段将显示您最近 10 次的搜索,以便快速导航。
每个 API 文档页面(JavaScript 原始类型页面除外)都有一个与该类相关的元数据菜单视图。此元数据视图将具有以下一个或多个
Ext.button.Button
类具有 Ext.Button
的备用类名)。备用类名通常为了向后兼容性而维护。可运行的示例 (Fiddles) 默认在页面上展开。您可以使用代码块左上角的箭头单独折叠和展开示例代码块。您还可以使用页面右上角的切换按钮切换所有示例的折叠状态。切换所有状态将在页面加载之间记住。
类成员默认在页面上折叠。您可以使用成员行左侧的箭头图标展开和折叠成员,或者使用右上角的展开/折叠全部切换按钮全局展开和折叠成员。
在较窄的屏幕或浏览器上查看文档将导致针对较小外形尺寸优化的视图。桌面视图和“移动”视图之间的主要区别是
可以通过单击 API 文档页面顶部的类名来查看类源代码。可以通过单击成员行右侧的“view source”链接来查看类成员的源代码。
Tree Panel 组件是 ExtJS 中功能最全面的组件之一,是用于在 应用程序 中显示分层数据的出色工具。Tree Panel 继承自与 Grid Panel 相同的类,因此,Grid Panel 的大多数优点(功能、扩展和插件)也可以在 Tree Panel 上使用。诸如列、列大小调整、拖放、渲染器、排序和过滤等功能预计在这两个组件上的工作方式类似。
让我们从创建一个非常简单的树开始。
Ext.create('Ext.tree.Panel', {
renderTo: document.body,
title: 'Simple Tree',
width: 300,
height: 250,
root: {
text: 'Root',
expanded: true,
children: [
{
text: 'Child 1',
leaf: true
},
{
text: 'Child 2',
leaf: true
},
{
text: 'Child 3',
expanded: true,
children: [
{
text: 'Grandchild',
leaf: true
}
]
}
]
}
});
此 Tree Panel 将自身渲染到文档主体。我们定义了一个默认展开的根 节点 。根节点有三个子节点,前两个是叶节点,这意味着它们不能有任何子节点。第三个节点不是叶节点,并且有一个子叶节点。text
属性用作节点的文本标签。
在内部,Tree Panel 将其数据存储在 TreeStore 中。上面的示例使用 root
配置作为配置 Store 的快捷方式。如果我们分别配置 Store,代码将如下所示
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,
...
});
重要的是要注意,TreeStore 的 Store 角色根据基于 根节点 的链接节点结构管理其自身的内容。
只有可见节点才在 store 中表示。当父节点展开时,其子节点会紧随其后立即插入到 store 中。当该节点折叠时,这些节点将从 store 中移除。
被认为在任何方面都不可见的节点,无论是由于祖先折叠还是由于过滤,都不会出现在 Store 的记录集合中。
所有节点仍然可以通过树层次结构访问
store 纯粹用于驱动用户界面。节点以一对一的方式映射到视图中。
要操作 store 的内容,您必须始终通过 Ext.data.TreeModel 提供的接口来 插入 、 追加 或 移除 节点到其他节点或从其他节点移除。
某些方法(例如 each 和 collect )在 TreeStore 级别重新实现,以提供对结构中所有节点的访问,而不是仅对平面 Store 角色中的可见节点进行访问。
在上面的示例中,我们在 Tree 节点上设置了几个不同的属性。但节点到底是什么?如前所述,Tree Panel 绑定到 TreeStore 。ExtJS 中的 Store 管理 Model 实例的集合。
在没有配置 Model 类型的情况下,Store 将使用 Ext.data.TreeModel 类来封装数据。这是一个 Model 子类,它已使用在 Tree 中使用所需的字段、方法和属性进行修饰。以下是显示开发者工具中节点结构的屏幕截图。
为了查看节点上可用的完整字段、方法和属性集,请参阅 NodeInterface 类的 API 文档。
您为在树中使用而定义的 Model 应扩展 Ext.data.TreeModel 类。
让我们尝试一些简单的操作。当您将 useArrows
配置设置为 true 时,Tree Panel 会隐藏线条并使用箭头作为展开和折叠图标。
将 rootVisible
属性设置为 false
会在视觉上移除根节点。通过这样做,根节点将自动展开。下图显示了相同的树,其中 rootVisible
设置为 false
,lines
设置为 false。
由于 Tree Panel 继承自与 Grid Panel 相同的基类,因此添加更多列非常容易。
var tree = Ext.create('Ext.tree.Panel', {
renderTo: document.body,
title: 'TreeGrid',
width: 300,
height: 150,
fields: ['name', 'description'],
columns: [{
xtype: 'treecolumn',
text: 'Name',
dataIndex: 'name',
width: 150,
sortable: true
}, {
text: 'Description',
dataIndex: 'description',
flex: 1,
sortable: true
}],
root: {
name: 'Root',
description: 'Root description',
expanded: true,
children: [{
name: 'Child 1',
description: 'Description 1',
leaf: true
}, {
name: 'Child 2',
description: 'Description 2',
leaf: true
}]
}
});
`columns` 配置期望 Ext.grid.column.Column 配置的数组,就像 Grid Panel 会有的那样。唯一的区别是 Tree Panel 至少需要一个 xtype
为 'treecolumn' 的列。这种类型的列具有特定于树的可视效果,例如深度、线条以及展开和折叠图标。典型的 Tree Panel 只有一个 'treecolumn'。
`fields` 配置传递给内部创建的 Store 使用的 Ext.data.Model 。请注意列上的 `dataIndex` 配置如何映射到我们指定的字段 - `name` 和 `description`。
还值得注意的是,当未定义列时,Tree 将自动创建一个 `dataIndex` 设置为 `'text'` 的 `treecolumn`。
如果未定义列配置了 header text ,则不会显示标题容器。
Tree Panel 的根节点不必在初始配置中指定。我们总是可以在以后添加它
var tree = Ext.create('Ext.tree.Panel');
tree.setRootNode({
text: 'Root',
expanded: true,
children: [{
text: 'Child 1',
leaf: true
}, {
text: 'Child 2',
leaf: true
}]
});
虽然这对于只有少量静态节点的非常小的树很有用,但大多数 Tree Panel 将包含更多节点。因此,让我们看一下如何以编程方式向 Tree 添加新节点。
var root = tree.getRootNode();
var parent = root.appendChild({
text: 'Parent 1'
});
parent.appendChild({
text: 'Child 3',
leaf: true
});
parent.expand();
每个不是叶节点的节点都有一个 `appendChild` 方法,该方法接受 Node 或 Node 的配置对象作为其第一个参数,并返回附加的 Node。上面的示例还调用了 `expand` 方法来展开新创建的父节点。
在创建新的父节点时内联定义子节点的能力也很有用。以下代码为我们提供了相同的结果。
var parent = root.appendChild({
text: 'Parent 1',
expanded: true,
children: [{
text: 'Child 3',
leaf: true
}]
});
有时我们希望将节点插入到树中的特定位置,而不是附加它。除了 `appendChild` 方法,`Ext.data.NodeInterface` 还提供了 `insertBefore` 和 `insertChild` 方法。
var child = parent.insertChild(0, {
text: 'Child 2.5',
leaf: true
});
parent.insertBefore({
text: 'Child 2.75',
leaf: true
}, child.nextSibling);
`insertChild` 方法期望子节点将被插入的索引。`insertBefore` 方法期望一个参考节点。新节点将插入在参考节点之前。
TreeModel 还提供了节点上的几个更多属性,这些属性可用于引用其他节点。
由于表示树的层次结构需要所有字段,因此加载和保存树数据比处理平面数据稍微复杂一些。本节将解释使用树数据的复杂性。
使用树数据时,首先也是最重要的事情是了解 TreeModel
类的字段如何工作。树中的每个节点都只是一个使用 TreeModel 的字段和方法修饰的 Model
实例。假设一个应用程序有一个名为 “Person” 的 Model。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 源代码可以发现,它使用以下字段修饰 Model。这些字段在内部用于存储与树的结构和状态相关的信息
{
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
}
重要的是要注意,以上所有字段名称都应被视为“保留”名称。例如,如果 Model 旨在在树中使用,则不允许在 Model 中使用名为“parentId”的字段,因为 Model 的字段将覆盖 TreeModel 字段。此规则的例外情况是当确实需要覆盖字段的持久性时。
TreeModel 的大多数字段默认设置为 persist: false
。这意味着它们默认是非持久字段。当调用 TreeStore 的 sync
方法或在 Model 上调用 save()
时,非持久字段不会通过 Proxy 保存。在大多数情况下,这些字段的大部分可以保留其默认持久性设置,但在某些情况下,有必要覆盖某些字段的持久性。
以下示例演示如何覆盖 TreeModel 字段的 persistence
。覆盖 TreeModel 字段时,重要的是仅更改 persist
属性。name
、type
和 defaultValue
属性永远不应更改。
// 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
属性的场景。在下面的每个示例中,除非另有说明,否则假定正在使用 服务器代理。
默认持久化
parentId
- 用于存储节点父节点的 ID。此字段应始终持久化,不应被覆盖。leaf
- 用于指示节点是叶节点,因此不能向其追加子节点。此字段通常不需要被覆盖。默认非持久化
index
- 用于存储节点在其父节点中的顺序。当一个节点被 插入 或 移除 时,插入点或移除点之后的所有兄弟节点都将更新其索引。如果需要,应用程序可以使用此字段来持久化节点的顺序。
但是,如果服务器使用不同的方法来存储顺序,则将 index 字段保留为非持久化可能更合适。当使用 WebStorage 代理 且需要存储顺序时,必须覆盖此字段使其持久化。
此外,如果正在使用客户端 排序,建议将 index
字段保留为非持久化,因为排序会更新所有已排序节点的索引,如果 persist
属性为 true,则会导致它们在下次同步或保存时被持久化。
depth
- 用于存储节点在树层次结构中的深度。如果服务器需要存储深度字段,请覆盖此字段以启用持久化。当使用 WebStorage 代理 时,建议不要覆盖 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 }
]
}
这就是加载整个树所需的全部内容。
需要注意的重要事项
loaded
属性设置为 true
。否则,代理将在这些节点展开时尝试加载其子节点。loaded
属性,它是否可以设置任何其他非持久化字段?答案是肯定的 - 有时可以。在上面的示例中,名为 "Nico" 的节点的 expanded
字段设置为 true
,以便它最初在 Tree Panel 中显示为展开状态。应谨慎使用,因为在某些情况下这不合适,并且可能导致严重问题,例如在不是根节点的节点上设置 root
属性。一般来说,loaded
和 expanded
是建议服务器在 JSON 响应中设置非持久化字段的唯一情况。对于较大的树,可能需要仅加载树的部分内容,即仅在其父节点展开时才加载子节点。假设在上面的示例中,服务器响应未将名为 "Sue" 的节点的 loaded
字段设置为 true
。树将在节点旁边显示一个展开器图标。当节点展开时,代理将向 readPersons
URL 发出另一个请求,如下所示
/readPersons?node=4
这告诉服务器检索 id
为 4 的节点的子节点。返回的数据应与用于加载根节点的数据格式相同
{
"success": true,
"children": [
{ "id": 5, "name": "Evan", "leaf": true }
]
}
现在树看起来像这样
创建、更新和删除节点由代理自动无缝处理。
// 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();
远程过滤的工作方式与其他任何 store 类型相同。过滤器被编码并传递到服务器,服务器必须仅使用所需的数据进行响应。
树的本地过滤与过滤网格等平面数据集不同。
过滤器条件可能包括低于父节点的节点,而父节点被过滤器条件排除。
默认情况下,通过过滤器条件的节点,但其父节点未通过过滤器条件的节点不包含在 store 中。这是自上而下的过滤,可以使用 filterer 配置进行重新配置。
要强制包含通过过滤器的节点的祖先节点,请将 filterer
配置为 bottomup
。
有关此类过滤的说明,请参阅 过滤树 示例。
这种过滤需要遍历树结构,以确定所有节点的可见性状态,然后再更新视图。
对于应用程序代码对单个记录进行细粒度或程序化过滤,您可以设置 TreeModel
的 visible
字段,视图将遵循该设置,并根据该值显示或隐藏节点。