组件
一个Ext JS程序的用户界面是由一个或多个被称为组件(Component)的部件组成。所有的组件都是Ext.Component类的子类。Ext.Component能让它的子类参与自动化的生命周期管理,包括实例化,渲染,大小和位置,以及销毁。Ext JS提供了大量的组件,任何一个组件都可以被轻易地扩展并创建自定义组件。
组件架构
容器是一种可以盛放其它组件的特殊的组件。一个典型的应用是由许多的嵌套在一起的组件组成的,这些组件形成了树形结构的架构。容器负责管理它的子组件的的生命周期,包括创建,渲染,大小及位置及销毁。经典的应用程序的组件从最顶端的Viewport开始,Viewport包含嵌套在其中的其它的容器或者组件。
子组件通过使用容器的items配置属性来添加到容器中。这个示例使用Ext.create()方法来实例化两个Panel,然后把这些Panel作为子组件添加到Viewport里:
var childPanel1 = Ext.create('Ext.panel.Panel', {
title: 'Child Panel 1',
html: 'A Panel'
});
var childPanel2 = Ext.create('Ext.panel.Panel', {
title: 'Child Panel 2',
html: 'Another Panel'
});
Ext.create('Ext.container.Viewport', {
items: [ childPanel1, childPanel2 ]
});
容器使用Layout Manager来调整它的子组件的大小与位置。更多关于布局和容器的信息请浏览本类文章中的第二篇。
xtypes和快速实例化
每个组件都有一个符号名称,叫做xtype。例如Ext.panel.Panel有一个xtype叫做’panel’。
上面的示例展示了如何添加已经实例化的组件和容器。然而,在一个大型应用程序中,这并不是一个理想的做法。因为并不是所有的组件都需要被立即实例化,并且有些组件可能永远不需要实例化,这取决于程序怎样被操作。例如,在使用Tab Panel的程序中,当每个tab被用户点击时,才需要渲染其中的内容。在这种情况下,xtype派上用场。使用它可以让子组件预先配置好,但直到容器决定要使用它时才会被实例化。
如下示例演示了使用Tab Panel作为容器的子组件是如何被按需实例化和渲染的。每个tab都有一个事件监听器,当它被点击时就会显示一个警告信息。
Ext.create('Ext.tab.Panel', {
renderTo: Ext.getBody(),
height: 100,
width: 200,
items: [
{
// Explicitly define the xtype of this Component configuration.
// This tells the Container (the tab panel in this case)
// to instantiate a Ext.panel.Panel when it deems necessary
xtype: 'panel',
title: 'Tab One',
html: 'The first tab',
listeners: {
render: function() {
Ext.MessageBox.alert('Rendered One', 'Tab One was rendered.');
}
}
},
{
// xtype for all Component configurations in a Container
title: 'Tab Two',
html: 'The second tab',
listeners: {
render: function() {
Ext.MessageBox.alert('Rendered One', 'Tab Two was rendered.');
}
}
}
]
});
运行这段代码会立即显示第一个tab的警告框。这是因为第一个tab是默认的激活tab,所以它的容器tab面板会立即实例化并渲染它。
第二个选项卡(tab)的警告框不会立即显示直到它被点击。这说明第二个选项卡直到需要时才被渲染,因为渲染事件(render)没有被触发直到第二个tab被激活选中。
显示与隐藏
所有的组件都内置了show和hide方法。默认的CSS隐藏组件的办法是使用”display: none”,但也可以使用hideMode配置项来改变隐藏的方式:
var panel = Ext.create('Ext.panel.Panel', {
renderTo: Ext.getBody(),
title: 'Test',
html: 'Test Panel',
hideMode: 'visibility' // use the CSS visibility property to show and hide this
component
});
panel.hide(); // hide the component
panel.show(); // show the component
浮动组件
浮动组件是在文档流的外面,使用CSS的绝对定位,它们也不参与容器的布局。诸如Windows的组件默认就是浮动的,但任何组件都可以通过 使用floating配置选项来变为浮动组件。
var panel = Ext.create('Ext.panel.Panel', {
width: 200,
height: 100,
floating: true, // make this panel an absolutely-positioned floating component
title: 'Test',
html: 'Test Panel'
});
上面的代码实例化了一个面板(Panel)但没有渲染它。通常情况下,一个组件要么声明一个renderTo配置选项,要么作为一个容器的子组件被添加,但是浮动组件这两个条件都不需要。当浮动组件的show方法第一次被调用时,它们会自动被渲染到文档体上。
panel.show(); // render and show thefloating panel
如下是一些其它与浮动组件相关的配置项与方法:
draggable-使浮动组件能够在屏幕上拖动。
shadow-自定义浮动组件的阴影效果。
alignTo()-使浮动组件依靠在特定的组件上。
center()-使组件在它的容器的居中。
创建自定义组件
构造或扩展
当创建一个新的用户界面(UI)类的时候,你必须要决定这个类是拥有一个组件的实例,还是要扩展该组件。
推荐的做法是根据功能需要扩展最近的类。这是因为如果使用了合适的组件管理容器,ExtJS提供的生命周期管理会根据需要自动完成组件的渲染,大小及位置调整以及销毁(当组件从容器中移除时)。
写一个可以排列在组件结构中的新类往往比写一个包含其它Ext JS组件,并且需要在外部对其进行管理的新类要容易。
子类
类体系能使你从容扩展Ext JS框架中的任何类。
Ext.Base类是Ext JS所有类的基类,它的原型与静态成员会被其它的所有类继承。
有时你想要从最低层的类(即Ext.Base)开始写代码,然而,大多数情况下,开发者都会从高一点的继承链来开始写功能。
如下的示例创建了一个Ext.Component的子类:
Ext.define('My.custom.Component', {
extend: 'Ext.Component',
newMethod : function() {
//...
}
});
这个示例创建了一个新类My.custom.Component,这个类除了有继承自Ext.Component的功能(方法,属性等),还具有自己定义的新方法和属性。
模板方法
Ext JS使用模板方法模型来为子类代理功能,而且只为那个子类代理。
这意味着在继承链中的每个类都可以在组件的特定生命周期中添加额外的逻辑功能。每一个类实现它自己的功能,同时允许它的子类继续在此阶段执行它们的功能。
其中一个示例是render函数。render是定义在Component中的一个方法。它负责在生命周期中的渲染阶段初始化组件。render不能被覆盖,但它在调用onRender方法期间,允许子类通过添加onRender方法来为特定的子类添加逻辑功能。每一个onRender方法在实现它的额外的逻辑功能前必须调用父类的onRender方法。
如下示例:
Ext.define('MyComponent',{
extend: 'Ext.Component',
xtype: 'myComp',
onRender: function(){
this.callParent(arguments);
alert('渲染时调用!');
}
});
var myComp=Ext.create({
xtype: 'myComp',
renderTo: Ext.getBody()
});
如下的图表阐明了onRender模板方法的功能。
render方法被调用(在这里是被容器的布局管理器调用)。这个方法也许没有被Ext的基类重写,实现。它会调用当前子类的this.onRender方法(如果子类实现了这个方法(onRender)的话)。子类的onRender方法会调用父类的onRender方法,父类继续调用父类的父类的onRender方法。最终,每个类都可以添加它自己的额外逻辑功能,控制权最后返回到render方法。
这里是一个实现了onRender方法的Component子类的一个实例:
Ext.define('My.custom.Component', {
extend: 'Ext.Component',
onRender: function() {
this.callParent(arguments); // call the superclass onRender method
// perform additional rendering tasks here.
}
});
很重要的需要注意到的一点是,很多模板方法有它相应的事件。例如render事件会在Component渲染后被触发。然而,在创建子类时,使用模板方法去执行特定生命周期阶段的业务逻辑而不是使用事件是十分必要的。事件或许会在程序中被挂起,或者被一个处理函数终止。
如下的模板函数可以被Component的子类实现:
initComponent-这个方法被构造函数调用。它被用来初始化数据,设置配置项和挂载事件处理函数。
beforeShow-这个方法在组件显示前调用。
onShow-允许添加额外的行为到show操作。在调用父类的onShow方法后,该组件将会显示出来。
afterShow-这个方法在组件显示后被调用。
onShowComplete-在afterShow方法完成后调用。
onHide-允许向hide操作中添加额外的行为。调用父类的onHide方法后,组件将会隐藏 。
afterHide-组件被隐藏后此方法将会被调用。
onRender-在渲染期间添加额外的功能。
afterRender-允许渲染完成后添加额外的功能。在此阶段,组件元素将会根据配置生成样式,配置中的CSS类名将会被添加,可见性与可用性(visibility与enable)也会根据配置生成。
onEnable-允许额外的操作添加到enable操作中。在调用父类的onEnable方法后,组件将会可用。
onDisable-允许额外的操作添加到disable操作中。在调用父类的onDisable方法后,组件将不可用。
onAdded-允许当组件被添加到容器时执行额外的操作。在此阶段,组件是在父容器的子组件集合中。在调用父类的onAdded方法后,ownerCt引用的组件将会出现,如果配置中有ref,refOwner将被设置。
onRemoved-允许一个组件从父容器中移除时执行额外的操作。在此阶段,组件已经被从它的父容器的子组件集合中移除,但还没有被销毁(如果父容器的autoDestroy被设置为true,或者remove方法的第二个参数被传入了一个truthy参数(if(参数)为true),则组件会自动销毁)。在调用父类的onRemove方法后,ownerCt和refOwner将不存在。
onResize-允许在resize操作中添加额外的操作。
onPosition-允许在position操作中添加额外的操作。
onDestroy-允许在destroy操作中添加额外的行为。在调用父类的onDestroy方法后,组件将销毁。
beforeDestroy-这个方法在组件被销毁前调用。
afterSetPosition-此方法在组件的位置被设定后调用。
afterComponentLayout-此方法在组件被布局后调用。
beforeComponentLayout-此方法在组件被布局前调用。
继承哪个类
根据基类必须提供的功能来选择继承哪个类是解决开发效率问题的主要方法。每当有UI组件需要渲染跟管理时,选择Ext.panle.Panel成了一种趋势。
Panel(面板)有很多功能:
-
边界(Border)
-
头部Header
-
头部工龄箱Header tools
-
底部Footer
-
底部按钮Footer buttons
-
顶部工龄条Top toolbar
-
底部工龄条Bottom boolbar
-
盛放并管理子组件
如果这些功能并不需要,那么使用Panel组件是一种资源浪费。
组件
如果UI组件不需要包含任何的子组件,也就是说,它仅用来封装一些HTML表单元素,那么继承Ext.Component是合适的选择。例如,下面的类包含了一个HTML图像元素,并允许你设置或获取图片的src属性。当图片被加载完成后,它还会触发一个load事件。
Ext.define('Ext.ux.Image', {
extend: 'Ext.Component', // subclass Ext.Component
alias: 'widget.managedimage', // this component will have an xtype of 'managedimage'
autoEl: {
tag: 'img',
src: Ext.BLANK_IMAGE_URL,
cls: 'my-managed-image'
},
// Add custom processing to the onRender phase.
// Add a 'load' listener to the element.
onRender: function() {
this.autoEl = Ext.apply({}, this.initialConfig, this.autoEl);
this.callParent(arguments);
this.el.on('load', this.onLoad, this);
},
onLoad: function() {
this.fireEvent('load', this);
},
setSrc: function(src) {
if (this.rendered) {
this.el.dom.src = src;
} else {
this.src = src;
}
},
getSrc: function(src) {
return this.el.dom.src || this.src;
}
});
使用:
var image = Ext.create('Ext.ux.Image');
Ext.create('Ext.panel.Panel', {
title: 'Image Panel',
height: 200,
renderTo: Ext.getBody(),
items: [ image ]
});
image.on('load', function() {
console.log('image loaded: ', image.getSrc());
});
image.setSrc('http://www.sencha.com/img/sencha-large.png');
这个示例只是为了演示-在真实应用程序中Ext.Img类应该用来管理图片。
容器
如果你需要的UI组件只是用来盛放其它组件,而不需要前面提到的Panel的其它功能,那么Ext.conainer.Container是一个很适合用来被继承的类。在容器层次,记住用何种布局来渲染和管理子组件是十分重要的。
容器有如下的其它模板方法:
onBeforeAdd-这个方法在添加一个新的子组件前调用。新组件作为参数被传入,你可以修改新的组件,或者在某种程度上对容器作一些准备工作。如果返回false,将会退出添加组件的行为。
onAdd-这个方法在新组件被添加后调用。被添加的组件作为参数传入。这个方法可以被用来更新任何内部的架构,这些架构或许依赖于子组件的状态。
onRemove-这个方法在一个新的组件被移除后调用。已经被移除的组件作为参数被传入。这个方法可以被用来更新任何内部的架构,这些架构或许依赖于子组件的状态。
beforeLayout-这个方法在容器完成布局(和渲染如果需要)它的子组件前调用。
afterLayout-这个方法在容器完成布局(和渲染如果需要)它的子组件后调用。
面板
如果你需要的UI组件必须有一个头部,脚部或者工具条,那么Ext.panel.Panel是更适合用来被继承的类。
重要提示:面板(Panel)是一种容器。记住用何种布局来渲染和管理子组件是十分重要的。
继承自Ext.panel.Panel的类通常都是有特殊用途的,一般用来集成在一个配置好布局中的其它组件(通常是容器或者表单域),并且提供了操作里面子组件的方法,这些方法通常由tbar和bbar实现控制。
面板朋如下的额外的模板方法:
afterCollapse-该方法在面板收起后被调用。
afterExpand-此方法在面板展开后调用。
onDockedAdd-这个方法在一个docked项被添加到面板后调用。
onDocketRemove-此方法在一个docked项被从面板移除后调用。