您的位置:首页 > Web前端 > JavaScript

ExtJS 4 组件详解

2014-12-25 22:39 225 查看
ExtJS4的应用界面是由很多小部件组合而成的,这些小部件被称作“组件(
Component
)”,所有组件都是
Ext.Component
的子类,
Ext.Component
提供了生命周期管理包括初始化、渲染、大小和尺寸管理、销毁等功能,这使得所有
Ext.Component
的子类都自动分享了这些能力。ExtJS提供了各式各样丰富的组件,每一个组件都很容易被扩展创建成自定义组件。

TheComponentHierarchy组件层级

容器(
Container
)是个可以容纳其他组件的特殊组件。通常的应用都是由很多嵌套的组件构成,这些组件有一个类似树状的结构,这就是组件的层级。容器负责管理子组件的生命周期包括始化、渲染、大小和尺寸管理、销毁。通常的应用最顶层的组件都是
Viewport
然后有其他容器或者组件嵌套在其中:

子组件通过容器的
items
加入到容器中,下面这个例子通过
Ext.create
方法创建了两个
Panel
,并把它们当作子组件添加到
Viewport
中:
varchildPanel1=Ext.create('Ext.panel.Panel',{
title:'ChildPanel1',
html:'APanel'
});

varchildPanel2=Ext.create('Ext.panel.Panel',{
title:'ChildPanel2',
html:'AnotherPanel'
});

Ext.create('Ext.container.Viewport',{
items:[childPanel1,childPanel2]
});

容器通过“布局管理器(
LayoutManager
)”管理子组件的尺寸和位置。关于布局和容器的详细内容可以查看《ExtJS4布局和容器》

XTypesandLazyInstantiationxtype和延迟加载

每个组件都有一个代称叫做
xtype
,例如
Ext.panel.Panel
xtype
panel
。所有组件的
xtype
APIDocsforComponent中有列出。上面那个例子说明了如何添加已经初始化的组件到一个容器中。在大的应用中,这种方式是不可行的,因为这种方法需要每个组件都是初始化过的,但是大的应用中,由于使用的方式有些组件可能根本没有被用到(比如有100个页面的应用,一个用户登录进来只操作了两个页面,这时把其他98个页面的组件全部初始化是不合理的),例如一个用到
TabPanel
的应用,
TabPanel
的每个tab只当用户点击它的时候再渲染即可。这就是引入
xtype
机制的原因,有了
xtype
可以让一个容器的子组件事先定义好,到真正需要的时候再初始化。
下面的示例代码通过
TabPanel
展示了延迟加载和延迟渲染,每个tab都有个响应函数监听tab的渲染
render
事件,渲染的时候会弹出一个对话框告知当前tab已经渲染
Ext.create('Ext.tab.Panel',{
renderTo:Ext.getBody(),
height:100,
width:200,
items:[
{
//ExplicitlydefinethextypeofthisComponentconfiguration.
//ThistellstheContainer(thetabpanelinthiscase)
//toinstantiateaExt.panel.Panelwhenitdeemsnecessary
xtype:'panel',
title:'TabOne',
html:'Thefirsttab',
listeners:{
render:function(){
Ext.MessageBox.alert('RenderedOne','TabOnewasrendered.');
}
}
},
{
//thiscomponentconfigurationdoesnothaveanxtypesince'panel'isthedefault
//xtypeforallComponentconfigurationsinaContainer
title:'TabTwo',
html:'Thesecondtab',
listeners:{
render:function(){
Ext.MessageBox.alert('RenderedOne','TabTwowasrendered.');
}
}
}
]
});

运行一下代码,第一个tab的渲染对话框马上就弹出了,因为默认激活第一个tab,所以它的父容器
TabPanel
立即初始化和渲染了它



不点击第二个tab,它的对话跨就一直不会弹出,这说明tab不会被渲染直到被用到,tab的
render
事件不会触发直到tab被激活



ShowingandHiding显示和隐藏

所有组件都有内置的
show
hide
方法。默认的css样式应用的是
display:none
,可以通过
hideMode
改变
varpanel=Ext.create('Ext.panel.Panel',{
renderTo:Ext.getBody(),
title:'Test',
html:'TestPanel',
hideMode:'visibility'//usetheCSSvisibilitypropertytoshowandhidethiscomponent
});

panel.hide();//hidethecomponent

panel.show();//showthecomponent

FloatingComponents浮动组件

浮动组件定位于文档流之外,使用的是CSS的绝对定位属性,不受父容器的布局控制。有些组件,例如
Window
,默认就是浮动的,任何组件都可以通过
floating
属性配置成浮动的。
varpanel=Ext.create('Ext.panel.Panel',{
width:200,
height:100,
floating:true,//makethispanelanabsolutely-positionedfloatingcomponent
title:'Test',
html:'TestPanel'
});

上面的代码实例化了一个
Panel
对象,但是并没有渲染它。通常一个组件要么有
renderTo
属性配置渲染到什么位置,要么就作为容器的一个子组件由容器负责渲染,但是这个例子的浮动组件即不需要
renderTo
也不需要作为子组件。浮动组件第一次调用
show
方法时,会被自动渲染到DOM的document.body中:
1
panel.show();//renderandshowthefloatingpanel

有几个属性是使用浮动组件时值得注意的:

draggable
-让浮动组件可以拖拽
shadow
-定制浮动组件的阴影效果
alignTo()
-让浮动组件对齐到一个特定元素
center()
-让浮动组件相对于容器居中

CreatingCustomComponents自定义组件

CompositionorExtension组合还是继承
当创建一个新的UI类时,必须决定这个类是引用其他组件的实例还是扩展这个组件。
推荐扩展最接近的组件添加自己需要的功能,这是因为使用继承的方式可以自动获得组件原有的自动生命周期管理能力,包括按须渲染、布局管理器提供的尺寸位置管理、从容器中删除的自动析构等。
扩展一个原有组件会让扩展出的组件隶属于组件层级中,这样比一个外部类引用并掌管ExtJS组件的实例要容易的多。
Subclassing子类化
ExtJS的类系统使得扩展一个现有组件变的很简单。接下来的代码演示了创建一个
Ext.Component
的子类,不添加任何功能:
12
3
Ext.define('My.custom.Component',{
extend:'Ext.Component'
});

TemplateMethods模板方法
ExtJS使用了模板方法模式把特殊行为委托给子类。
这意味着每一个继承链上的类都可以“贡献”一段包含特定逻辑的片段到组件的生命周期中。每一个类实现自己特殊行为的同时,还允许其他在继承链上的类继续贡献它们自己的逻辑。
一个例子就是
render
方法。
render
是个私有方法,是在
Component
的父类
AbstractComponent
中定义的,
render
在组件生命周期中负责渲染阶段的初始化。
render
不能被重写,但是
render
过程中会调用
onRender
,这个
onRender
方法是允许子类去实现的,子类可以在这里增加专属于子类的逻辑,每一个
onRender
方法必须在自己的额外逻辑之前调用父类的
onRender

下图指示了
onRender
模板方法是如何工作的。
render
方法首先被调用(这是布局管理器完成的),
render
方法不能被覆盖,它是ExtJS中的相关基类实现的,它会调用当前子类的
this.onRender
(如果当前子类有实现
onRender
),这会使得继续调用父类的
onRender
,父类的
onRender
会继续调用父类的,最后每个继承链上的类都贡献了自己的功能片段,并且控制回到
render
方法中。



这有一个实现
onRender
的例子
Ext.define('My.custom.Component',{
extend:'Ext.Component',
onRender:function(){
this.callParent(arguments);//callthesuperclassonRendermethod

//performadditionalrenderingtaskshere.
}
});

需要注意的是很多模板方法都有一个对应的事件。例如组件
render
完成之后会触发
render
事件。创建子类的时候最好是使用模板方法,而不是使用事件,因为模板方法一定会被执行,事件是可以被中断的(这里是我的补充:什么时候用事件,什么时候用模板方法?模板方法是面向你创建的子类的所有实例的,如果你需要增加的功能片段确实被所有实例需要,那一定要放在模板方法中,如果是某一个实例特殊需要的功能,请用事件实现,事件是可以对单独某个对象实现的)
下面列举的是
Component
的子类中可以实现的模板方法:

initComponent
这个方法被构造器调用,它被用来初始化数据,设置配置,添加事件
beforeShow
这个方法在组件显示前调用
onShow
允许组件显示的时候附加行为,调用父类的
onShow
之后,组件变为可见
afterShow
这个方法在组件显示完成后调用
onShowComplete
这个方法在
afterShow
执行完毕的时候调用
onHide
允许组件隐藏的时候附加行为,调用父类的
onHide
之后,组件变为不可见
afterHide
这个放在组件隐藏完成后调用
onRender
允许在渲染阶段附加行为
afterRender
允许在渲染完毕时附加行为,在这个阶段,组件的
Element
已经赋予了样式,该加的css类都已经加上,显示和启用与否的状态都标记完成
onEnable
允许启用操作时附加行为,调用父类的
onEnable
之后,组件变为启用
onDisable
允许禁用操作时附加行为,调用父类的
onDisable
之后,组件变为禁用
onAdded
允许一个组件被添加到一个容器的时候附加行为,在这个阶段,组件已经在父容器的items中,调用父类的
onAdded
之后,ownerCt引用被设置,如果配置了ref,refOwner这时也被设置
onRemoved
允许一个组件从父容器中删除的时候附加行为,在这个阶段,组件已经从父容器的items中移除,但是还没有被销毁(如果父容器的autoDestroy设置为true,或者如果删除操作remove函数的第二个参数设置为true,组件就会被销毁),调用父类的
onRemoved
之后,ownerCt和refOwner就会移除
onResize
允许改变大小的时候附加行为
onPosition
允许设置位置的时候附加行为
onDestroy
允许销毁时附加行为,调用父类的
onDestroy
之后,组件被销毁
beforeDestroy
组件销毁前调用
afterSetPosition
组件位置设置之后调用
afterComponentLayout
组件布局完成时调用
beforeComponentLayout
组件布局之前调用

WhichClassToExtend扩展那个类
选择最合适的类继承是个影响效率的问题,基类能提供的功能也是个问题。现在有种倾向是无论什么样的UI组件都从
Ext.Panel
继承
Panel类有很多能力:

Border
Header
Headertools
Footer
Footerbuttons
Toptoolbar
Bottomtoolbar
ContainingandmanagingchildComponents

如果不需要这些功能,继承
Ext.Panel
是浪费资源。
Component组件
如果创建的UI组件不需要包含任何其他组件,就比如只是对HTML片段的封装就可以满足需求的组件,推荐扩展
Ext.Component
,例如下面的例子封装了HTMLimage元素,允许设置image的src,并且图片加载完成会触发
load
事件:
Ext.define('Ext.ux.Image',{
extend:'Ext.Component',//subclassExt.Component
alias:'widget.managedimage',//thiscomponentwillhaveanxtypeof'managedimage'
autoEl:{
tag:'img',
src:Ext.BLANK_IMAGE_URL,
cls:'my-managed-image'
},

//AddcustomprocessingtotheonRenderphase.
//Adda‘load’listenertotheelement.
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){
returnthis.el.dom.src||this.src;
}
});

用法:
varimage=Ext.create('Ext.ux.Image');

Ext.create('Ext.panel.Panel',{
title:'ImagePanel',
height:200,
renderTo:Ext.getBody(),
items:[image]
})

image.on('load',function(){
console.log('imageloaded:',image.getSrc());
});

image.setSrc('http://www.sencha.com/img/sencha-large.png');

这个例子仅作为例子,
Ext.Img
可以在真实应用中使用。
Container容器
如果UI组件需要包含其他组件的能力,但是又不需要
Panel
那么多功能
Ext.container.Container
是个很好的选择。使用它有一点需要注意,记得使用
Layout
管理子组件。
Ext.container.Container
有如下模板方法:

onBeforeAdd
这个方法在添加一个子组件之前调用,方法会被传入添加进来的子组件,你可能会对子组件进行一些修改,或者对容器自身做一些准备工作,返回false会中断添加操作。
onAdd
这个方法在新组件被添加完成时调用,方法会传入新添加的组件,这个方法可以用来更新依赖子组件状态的内部结构
onRemove
子组件被删除之后调用,用处跟
onAdd
类似
beforeLayout
组件布局它的子组件之前调用的方法
afterLayout
组件布局它的子组件之后调用的方法

Panel面板
如果需求的UI组件必须有header,footer或者toolbar,
Ext.panel.Panel
比较适合
注意
Panel
也是个容器,也需要
Layout
管理子组件
Panel
扩展出的组件一般都是跟具体应用相关的,通常都聚合了其他组件在其中,一般都提供了操作内部组件的方法,比如在toolbar上有操作内部组件的按钮等
Panel
有如下附加的模板方法:

afterCollapse
组件折叠起来之后调用
afterExpand
组件展开之后调用,与afterCollapse相对应
onDockedAdd
工具条上有子组件添加之后调用
onDockedRemove
工具条上有子组件被移除后调用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: