组件和容器
组件模型
XType和ComponentManager
- XType
定义:XType是一个纯JavaScript对象,包含一个xtype属性,属性的值是一个字符串,标识该XType对应的类。
作用:Xtype允许组件延迟实例化,加速复杂用户界面的类的实例化,并大大提高代码的整洁度。 - ComponentManager
定义:ExtJs系统会自动将部件的XType注册到ComponentManager。
作用:包含所有组件操作的方法。
使用方法
var myPanel = {
xtype: 'panel',
width: 100,
height: 100,
html: 'hello'
}
上面的代码相当于配置一个宽高分别为100px的Ext.Panel部件。由此可以看出,在ComponentManager中所有组件与一个唯一的字符串是一一对应的,相当于可以通过这个字符串获取所对应的组件类的引用。
自定义类注册XType
Ext.define('MyApp.CustomClass', {
extend: 'Ext.panel.Panel',
alias: 'widget.myCustomComponent'
});
// 使用
new Ext.Panel({
...
items: {
xtype: 'myCustomComponent',
...
}
});
在初始化一个包含子元素的可视化组件时(如上代码中的Ext.Panel),它会检查自己是否有this.items,并检查this.items里是否有XType的配置对象。如果找到配置对象,会尝试用ComponentManager.create创建一个该组件的实例,如果没有定义,则可视化组件会在调用ComponentManager.create的时候定义它的defaultType属性。
如下代码,会创建两个panel:
var panel1 = {
xtype : 'panel',
title : 'Plain Panel',
html : 'Panel with an xtype specified'
};
var panel2 = {
title : 'Plain Panel 2',
html : 'Panel with <b>no</b> xtype specified'
};
Ext.create('Ext.window.Window', {
width : 200,
height : 150,
title : 'Accordion window',
layout : 'accordion',
border : false,
layoutConfig : {
animate : true
},
items : [
panel1,
panel2
]
}).show();
组件渲染
直接渲染
通过renderTo或者applyTo属性实例化。
懒惰(按需)渲染
忽略renderTo和applyTo属性,在必要的时候调用该组件的render方法。
组件生命周期
ExtJs组件的生命周期主要被划分为三个阶段:初始化、渲染、销毁。
组件生命周期的每一阶段都要经历好几步,这是通过基类Ext.Component来控制的。
初始化
应用组件的配置:
当初始化一个组件的实例时,传递的组件配置对象包含了希望让组件拥有的所有功能,这些都是在Ext.Component基类的前几行代码完成的。注册事件:
诸如enable/disable,show,hide,render,destory,state restore,state save等等事件,是所有继承于Ext.Component的组件都会默认拥有的基本事件,它们将会在执行某些行为之前或之后被触发。ComponentMgr注册组件实例:
在这里,每一个组件的实例都会生成一个字符串作为其ID值,供Ext.Cmp()方法来获得该实例的引用。在实例的配置中,可以通过配置id值给其传递ID值,不过如果设置了同样的ID值,Ext.Cmp()方法找到的实例引用将会是最后一个设置了该ID的组件。也就是说,最后设置的ID值将会覆盖前面的所有相同ID值。调用initComponent()方法:
关于initComponent()方法,在直接或间接继承了Ext.Component基类的组件中,该方法会在Component的构造函数constructor中被调用。
这里来看看其回调顺序:
Ext.Window ==> Ext.Panel ==> Ext.Container ==> Ext.BoxComponent ==> Ext.Component
在自定义组件时,一般都会覆盖父类的initComponent()方法,并且在最后用this.callParent()来回调父类函数,则在实例化组件的过程中,container的initComponent方法里的this已经变成了该实例对象本身。如果不这么做,譬如直接将itsms写入配置中,则在内部调用container的initComponent方法时,this所指的对象将不是目标实例化的对象。配置内的items的内容将残留在new的对象上,从而导致在连续实例化同一个自定义组件时,除了第一个实例,后面的实例都将失败的结果。
于是在自定义组件的时候,最好将配置项写入initComponent方法中,并在配置项最后使用this.callParent()来回调其父类函数。有许多工作都会在initComponent方法里完成。例如注册自定义的事件、设定data stores、创建子控件等。initComponent可以看做constructor的补充,因此经常用于扩展组件的入口点。加载插件和组件渲染:
如果在constructor的参数中传递了plugin对象,plugin的init方法将会被调用,同时会将父对象作为参数传递进init方法里。如果组件中配置了renderTo或者applyTo,则组件将马上被渲染,否则,它会被延迟渲染,直到组件被显式调用显示,或被它的容器所调用。
渲染
触发beforeRender事件:
这是在组件被render渲染前被调用的。用以提供处理函数或者阻止组件的继续渲染。设置容器:
如果没有父容器被指定,默认它的父对象被指定为它的容器。调用onReader方法:
这是为子类执行呈现工作的一个非常重要的方法,这是一个模板方法,在子类中可以根据需来重写它的实现逻辑。直接被创建的类的 onRender 首先被调用,然后它可以通过superclass.onRender 来调用基类的 onRender 方法。这个方法很容易被重新实现,如果需要你可以在继承关系的任意类中重写这个方法。不隐藏组件:
默认,大多数组件都会通过设置像 x-hidden 这个样式来使它隐藏。当 autoShow 设置为true 时,这个隐藏功能的样式会被移除。应用自定义样式:
所有的 Component 子类都支持指定 cls 配置属性,通过它可以为 Component 所呈现的HTML 元素指定 CSS 样式。通过添加组件的 cls 属性,使用标准的样式规则,是一个自定义可视组件显示效果的非常完美的方法。render 方法被触发:
简单的通知组件已经被成功的呈现了。调用afterRender:
这是另一个模板方法,子类根据逻辑需要可以重新实现或覆盖该方法。所有的子类可以通过调 superclass.afterRender.来调用父类的方法。组件隐藏或不可用:
根据配置选项的值来设置。状态事件被初始化:
可以状态化的组件会定义一些事件来指定状态的初始化和保存。如果提供,这些事件会被添加。
销毁
触发beforedestroy:
这是一个可取消的事件,如果需要,可能通过提供事件代理来阻止组件被销毁。调用beforeDestroy方法:
又一个模板方法,在子类中可以重新实现和调用父类的方法。移除事件监听者(代理):
如果组件已被呈现,则移除它底层的 HTML 元素的事件监听列表,然后将元素从 DOM中移除。onDestroy被调用:
这个还是一个模板方法,在子类可以重新实现。这里需要注意的是,容器类提供了一个默认的 onDestroy 实现,它会循环销毁它的成员组。组件实例从ComponentManager中反注册:
不可以再通过Ext.getCmp获取到对象实例。destroy事件被触发:
这只是一个简单的提醒,表示组件销毁成功。移除Component上的事件代理:
组件可以独立于元素,自己拥有事件代理,如果存在则移除它们。
容器
Container是一个幕后组件类,它为组件管理其子元素提供了基础
处理子元素
Ext.getCmp('myWin').add({
title: 'Appended Panel',
id: 'addedPanel',
html: 'hello'
});
如上代码可以在myWin容器中新增一个id为addedPanel的panel组件。
Ext.getCmp('myWin').insert(1, {
title: 'Inserted Panel',
id: 'insertPanel',
html: 'hello'
});
如上代码可以在myWin容器下标为1的位置插入一个id为insertPanel的panel组件。
查询组件
ExtJs 4.0开始带有一个ComponentQuery类,它有一个选择器引擎。ComponentQuery是模仿浏览器的选择器引擎设计的,所以可以以任何源节点为根执行查询操作。
查询组件与CSS的查询规则类似。例如:
Ext.ComponentQuery.query('#myDiv');
可以查询出一个id为myDiv的元素。
注:不管找到多少元素,ComponentQuery返回的都将是一个数组,因此如果想要测试查询是否成功需要判断返回的数组长度是否大于0,同时如果想要获取元素,必须用下标访问。