许多类在使用配置对象创建(实例化)类时都有快捷名称。快捷名称被称为 别名
(如果该类扩展了 Ext.Component,则称为 xtype
)。别名/xtype 列在适用类的类名旁边,以便快速参考。
框架类或其成员可以指定为 private
或 protected
。否则,类/成员为 public
。Public
、protected
和 private
是访问描述符,用于传达应如何以及何时使用类或类成员。
Public 类和类成员可供任何其他类或应用程序代码使用,并且在主要产品版本中可以作为稳定和持久的依赖项。公共类和成员可以通过子类安全地扩展。
Protected 类成员是稳定的 public
成员,旨在由拥有类或其子类使用。受保护的成员可以通过子类安全地扩展。
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 文档页面顶部的类名来查看类源代码。可以通过单击成员行右侧的“查看源代码”链接来查看类成员的源代码。
最常被请求的教程之一是如何制作登录系统。我们将从构建登录系统开始这个持续进行的教程。请注意,这只是创建登录系统的一种方法。还有许多其他可行选项。这仅仅旨在向您展示一种创建具有登录/注销功能的应用程序的方法。
免责声明: 本教程旨在向用户展示他们可以设计应用程序以包含“已登录”和“已注销”视图的一种方式。本教程不尝试实现安全数据连接、会话管理、真实身份验证等。它仅用于教育目的。
我们要做的第一件事是使用 Sencha Cmd 生成应用程序。您可以通过从命令行界面或 CLI 发出以下命令来执行此操作
sencha -sdk /path/to/ExtSDK generate app TutorialApp ./TutorialApp
注意: 如果您没有 Sencha Cmd,或者不熟悉使用它,请在继续之前阅读 Cmd 简介指南。
生成的 TutorialApp 是一个功能齐全的 Cmd 生成的应用程序,我们将用它作为登录/注销应用程序的基础。您可以通过导航到 Web 服务器上应用程序的位置来查看此应用程序。例如
https:///TutorialApp
或者通过在新生成的 TutorialApp 文件夹中启动“sencha app watch”。这将启动一个 Jetty 服务器,从而无需传统的 Web 服务器。如果您使用“sencha app watch”,您可以在此处找到该应用程序
https://:1841/
接下来,我们将导航到我们新生成的 TutorialApp。向下钻取到您生成的应用程序的“app/view/”文件夹中。您应该看到默认生成的“main”文件夹。此文件夹包含 Main.js、MainController.js 和 MainModel.js 文件。
让我们继续为登录功能准备我们的项目,方法是创建一个名为“login”的新 app/view 文件夹。创建“app/view/login/”文件夹后,将以下文件添加到该文件夹
- Login.js
- LoginController.js
生成的文件结构应如下所示
mainView 配置 application 是一种非常方便的方式,可以通过利用 Viewport 插件 自动加载和实例化“TutorialApp.view.main.Main”。但是,在选择初始视图之前,我们需要在 Ext.application 的 launch 方法 中执行一些评估。例如,如果用户尚未登录,我们不想创建 Main 视图。
为了本练习的目的,让我们从 "{appRoot}/app.js" 中的 application 配置中删除 mainView
配置。
注意: 由于我们已删除 mainView
,因此刷新您的应用程序将导致空白页,因为没有实例化任何类。
接下来,让我们创建一个登录视图。为此,我们将打开空白的 "{appRoot}/app/view/login/Login.js" 文件并开始定义登录窗口。
首先,让我们定义我们的类并扩展基类 Window 类。如下所示
Ext.define('TutorialApp.view.login.Login', {
extend: 'Ext.window.Window',
xtype: 'login'
});
现在,我们已将登录类定义为 Ext.window.Window 的扩展,可以使用 xtype login
实例化它。让我们开始为我们的类赋予一些独特的属性。我们将从为窗口本身添加一些其他配置开始。
Ext.define('TutorialApp.view.login.Login', {
extend: 'Ext.window.Window',
xtype: 'login'
requires: [
'TutorialApp.view.login.LoginController',
'Ext.form.Panel'
],
controller: 'login',
bodyPadding: 10,
title: 'Login Window',
closable: false,
autoShow: true
});
让我们逐步了解这些配置的含义。
requires 块确保我们在实例化登录窗口之前包含任何可能依赖的类。我们需要包含 LoginController.js
,因为我们即将将其指定为下一行中的控制器。我们还需要包含 Ext.form.Panel,因为我们的视图包含 Form 面板。
controller 配置指定一个 ViewController,然后将其附加到登录窗口的实例。此控制器提供了一个位置,用于包含与登录窗口或其子组件相关的所有逻辑。在这里,我们将控制器指定为 login
,这将是我们控制器的别名。
bodyPadding 配置纯粹是美观的。此配置在窗口主体内容的外部应用“10px”的内边距。
传递给 title 配置的字符串会导致创建 header 并添加字符串值作为其标题。
closable 确定窗口是否可以关闭。窗口默认情况下具有可关闭按钮。但是,由于这是一个登录窗口,我们不希望用户关闭它。如果他们确实关闭了它,他们将留下一个空白页。
窗口默认情况下是隐藏的。将 autoShow 设置为 true
将在创建窗口后立即显示窗口。另一种选择是在我们希望窗口可见时以编程方式调用窗口的 show 方法。
现在我们已经讨论了窗口的配置,让我们为其添加一些子组件。由于这将是一个登录表单,我们将创建一个 Form 面板作为窗口的子项。然后,我们将添加两个 文本字段、一个 显示字段 和一个提交 按钮。
此文件的最终代码应如下所示
Ext.define('TutorialApp.view.login.Login', {
extend: 'Ext.window.Window',
xtype: 'login',
requires: [
'TutorialApp.view.login.LoginController',
'Ext.form.Panel'
],
controller: 'login',
bodyPadding: 10,
title: 'Login Window',
closable: false,
autoShow: true,
items: {
xtype: 'form',
reference: 'form',
items: [{
xtype: 'textfield',
name: 'username',
fieldLabel: 'Username',
allowBlank: false
}, {
xtype: 'textfield',
name: 'password',
inputType: 'password',
fieldLabel: 'Password',
allowBlank: false
}, {
xtype: 'displayfield',
hideEmptyLabel: false,
value: 'Enter any non-blank password'
}],
buttons: [{
text: 'Login',
formBind: true,
listeners: {
click: 'onLoginClick'
}
}]
}
});
让我们讨论一下这些添加内容。
我们添加到登录窗口的第一个配置是 items 配置。在 容器 中,例如 Form 面板和登录窗口本身,items
配置可以容纳一个 Component 或 Component 配置对象。items
配置也可以是 Component 或 Component 配置对象的数组。这些组件将使用容器的 layout 显示在容器的主体中。
每个 Component 类都有自己的 xtype。您可以将 xtype
视为轻松创建 Component 实例的快捷方式。在本例中,我们使用 xtype
“form”(“form”是 Ext.form.Panel 类的 xtype
)配置了带有子项的登录窗口。Form 面板是特殊类型的面板,包含用于处理输入字段的便捷配置选项。
接下来,您将看到另一个熟悉的 items
数组。在这里,我们通过更深一层地使用 items 数组来嵌套其他项目。我们将更多组件放置在父组件内,父组件是 Form 面板。在本例中,我们嵌套的组件是构成登录表单的表单字段。
我们可以快速浏览此组件数组,因为它们是不言自明的。第一个项目具有 xtype
[[ext: Ext.form.field.Text textfield]]、name
“username”、fieldLabel
“username” 和 allowBlank
“false”。这归结为一个带有名称值和字段标签的文本字段。该字段不能为空白并通过验证(请参阅下面的“formBind”)。
下一个字段几乎相同,除了类型被设置为“password”。这会将您的输入转换为 *
以确保安全。
此 items
数组的最后一个成员是一个 displayfield。Display field 是一个文本字段,它不会随您的表单一起提交。它对于在不与用户交互的情况下传达数据非常有用。在本例中,我们通知用户任何非空白密码都将允许提交表单。
我们在此处添加的最后一点是 buttons 数组。这是一个方便的配置,用于将按钮添加到面板中的页脚 Toolbar。此特定按钮将包含文本“Login”,用户将单击该按钮以提交登录表单。
我们的按钮包含一个名为 formBind 的配置,它被设置为“true”。当组件的 formBind
设置为“true”时,它将根据表单的有效性状态禁用/启用。这意味着在两个输入字段都包含值之前,按钮将不可单击。
listeners 对象配置了事件以及将响应这些事件触发的方法/函数。在本例中,我们正在等待有人单击按钮。单击后,我们将事件“转发”到一个名为 onLoginClick
的方法。onLoginClick
方法稍后将在我们的 Login 控制器中定义。
注意: 我们尚未以任何方式实例化 Login 视图,因此刷新应用程序不会显示任何更改。
接下来,让我们创建 Login 控制器,它是一个类,包含用于处理用户与 Login 视图交互的任何逻辑。为此,我们将打开空白的 {appRoot}/app/view/login/LoginController.js
文件并开始定义我们的 Login 窗口的逻辑。
LoginController.js
的全部内容如下
Ext.define('TutorialApp.view.login.LoginController', {
extend: 'Ext.app.ViewController',
alias: 'controller.login',
onLoginClick: function() {
// This would be the ideal location to verify the user's credentials via
// a server-side lookup. We'll just move forward for the sake of this example.
// Set the localStorage value to true
localStorage.setItem("TutorialLoggedIn", true);
// Remove Login Window
this.getView().destroy();
// Add the main view to the viewport
Ext.widget('app-main');
}
});
上面的代码可能有点脱离上下文,但是当我们讨论下一节中的 launch
方法时,它会更有意义。此类包含通过单击登录按钮调用的 onLoginClick
方法。
这些代码段都有注释来描述每个语句的用途,但是让我们逐段查看以进行进一步解释。
首先,我们创建一个名为 onLoginClick
的方法。这是我们在 Login 视图中将登录按钮的单击事件定向到的方法。
正如注释中提到的,您可以在此处调用服务器以验证用户的凭据是否有效。这通常以 AJAX 或 REST 请求的形式出现。但是,对于本教程,我们将接受任何非空白输入。成功后,您将继续执行其余代码。如果失败,您将允许用户重新输入其凭据。当然,在这种情况下没有失败的可能性,所以让我们继续前进!
在本教程中,我们使用 localStorage 来维护用户登录状态。在成功进行凭据检查后,我们可以确定用户具有访问 Main 应用程序视图的适当权限。然后,我们可以在 localStorage 中设置一个键/值对,以告知应用程序用户是有效的。接下来,我们将在初始 launch
方法中检查 TutorialLoggedIn
localStorage 键是否设置为“true”(在下面的“launch”部分中更详细地介绍)。
ViewControllers 引入了一个非常有用的方法,称为 getView()。getView()
方法返回与调用它的 ViewController 关联的当前视图。在本例中,该视图是 Login 窗口。由于我们将登录点击视为成功登录,因此我们不再希望显示 Login 窗口。因此,我们使用 this.getView().destroy()
来获取对 Login 窗口的引用,然后 destroy 它。
现在我们已经销毁了 Login 窗口,我们希望更改视图以显示 Main 视图。在本例中,我们使用 Ext.widget('app-main')
来实例化“{appRoot}/app/view/main/Main.js”视图。
注意: 'app-main'
指的是在我们的 Sencha Cmd 生成的“TutorialApp.view.main.Main”类(位于“{appRoot}/app/view/main.Main.js”中)上定义的 xtype。
接下来,让我们讨论“{appRoot}/app/Application.js”和 launch
函数。
“Application.js”是您的应用程序的核心。您可以在与您的“view”、“store”和“model”文件夹相同的级别找到“Application.js”。它提供了一个方便的方法,称为 launch
,当您的应用程序加载了所有必需的类时,它会触发。这是本教程“Application.js”文件的完整代码。
/**
* The main application class. An instance of this class is created by `app.js` when it calls
* Ext.application(). This is the ideal place to handle application launch and initialization
* details.
*/
Ext.define('TutorialApp.Application', {
extend: 'Ext.app.Application',
name: 'TutorialApp',
stores: [
// TODO: add global / shared stores here
],
views: [
'TutorialApp.view.login.Login',
'TutorialApp.view.main.Main'
],
launch: function () {
// It's important to note that this type of application could use
// any type of storage, i.e., Cookies, LocalStorage, etc.
var loggedIn;
// Check to see the current value of the localStorage key
loggedIn = localStorage.getItem("TutorialLoggedIn");
// This ternary operator determines the value of the TutorialLoggedIn key.
// If TutorialLoggedIn isn't true, we display the login window,
// otherwise, we display the main view
Ext.widget(loggedIn ? 'app-main' : 'login');
}
});
让我们检查一下这些部分正在做什么。
我们已经描述了 requires 的作用,但是让我们谈谈这个特定的数组。对于“Application.js”的目的,我们需要准备应用程序以加载 Login 或 Main 视图,具体取决于即将进行的登录评估的结果。为此,我们必须同时 require “TutorialApp.view.main.Main”和“TutorialApp.view.login.Login”,以便任何结果都可用。
如前所述,launch
方法是一个函数,当您的应用程序加载了运行所需的一切内容时,它将执行。这是为登录/注销应用程序执行有关用户状态逻辑的理想位置。
下一步是检查先前设置的名为 TutorialLoggedIn
的 localStorage 键。我们只是将 loggedIn 变量设置为该键的值的结果。如果它不存在,则 loggedIn 将为 null。如果它确实存在,我们之前已在 LoginController 的逻辑中将 TutorialLoggedIn
设置为 true。
大多数编程语言都包含一种称为三元运算符的条件速记形式。三元运算符允许您最大限度地减少传统 if/else 语句所需的代码量。在本例中,我们使用三元运算符来说,“如果 loggedIn 存在(不是 null),让我们加载 Main 视图,否则,加载 Login 视图”。然后,我们使用 Ext.widget
方法来实例化三元运算符的结果。
您可能还记得,我们在本教程的早期从“{appRoot}/app.js”中删除了 mainView
配置。由于我们没有定义 Viewport,因此您的 Main 视图将不知道在何处呈现。我们将通过混合 viewport 插件 来更改这一点,以便“{appRoot}/app/view/main/Main.js”将充当我们应用程序的 viewport。这样,Main 视图将占据浏览器中所有可用的宽度和高度。这就像将以下行添加到“{appRoot}/app/view/main/Main.js”一样简单
plugins: 'viewport',
我们生成的 {appRoot}/app/view/main/Main.js
文件将如下所示
/**
* This class is the main view for the application. It is specified in `app.js` as the
* "mainView" property. That setting automatically applies the "viewport"
* plugin to promote that instance of this class to the body element.
*
* TODO - Replace this content of this view to suite the needs of your application.
*/
Ext.define('TutorialApp.view.main.Main', {
extend: 'Ext.container.Container',
requires: [
'TutorialApp.view.main.MainController',
'TutorialApp.view.main.MainModel'
],
xtype: 'app-main',
controller: 'main',
plugins: 'viewport',
viewModel: {
type: 'main'
},
layout: {
type: 'border'
},
items: [{
xtype: 'panel',
bind: {
title: '{name}'
},
region: 'west',
html: '<ul><li>This area is commonly used for navigation, for example, using a "tree" component.</li></ul>',
width: 250,
split: true,
tbar: [{
text: 'Button',
handler: 'onClickButton'
}]
},{
region: 'center',
xtype: 'tabpanel',
items:[{
title: 'Tab 1',
html: '<h2>Content appropriate for the current navigation.</h2>'
}]
}]
});
您无需修改“{appRoot}/app/view/main/Main.js”的其他任何部分,因为我们将为我们的 click 处理程序使用默认生成的 onClickButton
方法。
我们快完成了!现在剩下要做的就是为用户提供一种注销应用程序的方法,这将销毁 localStorage 中的 TutorialLoggedIn
键。此逻辑应在“{appRoot}/app/view/main/MainController.js”文件中进行。如果您愿意,可以删除其他生成的代码。这是本教程的“MainController.js”的最终定义
Ext.define('TutorialApp.view.main.MainController', {
extend: 'Ext.app.ViewController',
alias: 'controller.main',
onClickButton: function () {
// Remove the localStorage key/value
localStorage.removeItem('TutorialLoggedIn');
// Remove Main View
this.getView().destroy();
// Add the Login Window
Ext.widget('login');
}
});
我们应该不需要在此处进行太多深入探讨,因为它基本上是我们 {appRoot}/app/view/login/LoginController.js
代码的逆过程。
为了总结此功能,onClickButton
是由我们生成的“{appMain}/app/view/main/Main.js”视图中的按钮处理程序调用的函数。一旦检测到 click
事件,将执行以下步骤
删除维护用户登录状态的 localStorage 键。
销毁当前视图,即 TutorialApp.view.main.Main。
重新创建登录视图。
您现在应该能够在浏览器中加载您的应用程序,并看到一个功能齐全的登录/注销应用程序。
我们希望您喜欢本教程。这仅仅是一个应用程序的基础,但希望我们已经介绍了一些使您未来的项目更简单的概念。请随时通过我们的 论坛 建议未来教程的想法。此外,请随时通过 支持门户 或 论坛 跟进问题。