EXTJS4官方文档翻译系列二:组件(components)

从八月开始不用加班了,应该会有比较多的时间来把这些文档翻译完了.之所以翻译这些文档,是因为我觉得这几篇是比较重要的(我翻译的顺序是按我自己认为要看的顺序翻译的),自己在看,就顺便翻译了(其实英语很差,google翻译不停的查的...),加强一下记忆,怕以后又忘记了.英语好的还是看原文吧,不要被我误导了:官方原文

用Extjs4一个月了,中间遇到许多问题,也解决了很多问题,积累了一些心得,总体来说4是很大的进步!等这几篇比较基础的文档都翻译好了,再来写一些使用过程的心得和一些实例教程.

一个Ext JS 应用程序是由一或多个称为组件(components)的部件(widgets)组成的.所有的组件都是Ext.Component的子类,Ext.Component包含自动化生命周期管理的功能,如创建、渲染、尺寸控制、定位和销毁等,因此所有组件都自动拥有这些功能。ExtJS 提供了非常多可直接使用的组件,并且从这些组件继承并扩展自己的自定义组件也是很容易的一件事情。

组件层次结构


容器(Container)是一种可以包含其他组件的特殊组件。一个典型的应用程序是由许多按树状机构嵌套在一起的组件组成的,称之为组件层次结构。容器负责管理子组件的生命周期,包括子组件的创建、渲染、尺寸控制、定位和销毁。一个典型的应用程序组件层次结构一般使用Viewport作为顶级容器,它的子元素可以包含其他嵌套的容器或者非容器组件:

Component Hierarchy

子组件使用容器的items属性配置添加到容器中。以下这个例子使用Ext.create创建了两个Panel,然后将这两个Panel作为子组件添加到一个Viewport中:

   1:  var childPanel1 = Ext.create('Ext.panel.Panel', {
   2:      title: 'Child Panel 1',
   3:      html: 'A Panel'
   4:  });
   5:   
   6:  var childPanel2 = Ext.create('Ext.panel.Panel', {
   7:      title: 'Child Panel 2',
   8:      html: 'Another Panel'
   9:  });
  10:   
  11:  Ext.create('Ext.container.Viewport', {
  12:      items: [ childPanel1, childPanel2 ]
  13:  });

Viewport Container

容器使用布局管理器(Layout Managers)来管理容器中子组件的尺寸和定位。更多有关于布局管理和容器的知识请看:Layouts and Containers Guide(下一篇就翻译这篇)。关于布局管理和容器的demo:Container Example

XType(组件类型)和延迟实例化


每个组件都有一个用来表示类型的名字,称为xtype。例如,组件Ext.panel.Panel的xtype为'panel'。所有组件的xtype在这里:API Docs for Component。上面那个例子展示了如何添加已经创建好的组件到容器中。 然而,在比较大的应用程序中,这样做不是很理想,因为并不是所有的组件都需要在声明时立即创建,甚至一些组件可能永远都不会被创建,这取决于你的程序中是如何使用这些组件的。例如:一个使用到Tab Panel(选项卡)的应用程序,选项卡的每一tab页只有在用户单击的时候才会去渲染。这时候xtype就派上用场了,在容器的子元素(items)配置中,为子元素指定xtype,子元素不会马上实例化,而是由容器决定子元素何时该实例化。

下面的示例代码演示如何延迟实例化和渲染容器中的子元素(使用Tab Panel)。每个tab页都会监听各自的渲染完成(rendered,在渲染结束后触发)事件,在渲染后弹出提示。

   1:  Ext.create('Ext.tab.Panel', {
   2:      renderTo: Ext.getBody(),
   3:      height: 100,
   4:      width: 200,
   5:      items: [
   6:          {
   7:              // Explicitly define the xtype of this Component configuration.
   8:              // This tells the Container (the tab panel in this case)
   9:              // to instantiate a Ext.panel.Panel when it deems necessary
  10:              xtype: 'panel',
  11:              title: 'Tab One',
  12:              html: 'The first tab',
  13:              listeners: {
  14:                  render: function() {
  15:                      Ext.MessageBox.alert('Rendered One', 'Tab One was rendered.');
  16:                  }
  17:              }
  18:          },
  19:          {
  20:              // this component configuration does not have an xtype since 'panel' is the default
  21:              // xtype for all Component configurations in a Container
  22:              title: 'Tab Two',
  23:              html: 'The second tab',
  24:              listeners: {
  25:                  render: function() {
  26:                      Ext.MessageBox.alert('Rendered One', 'Tab Two was rendered.');
  27:                  }
  28:              }
  29:          }
  30:      ]
  31:  });
 
运行这个示例马上会弹出一个提示,那是因为运行后默认tab页被渲染并立即呈现在容器中了。
Lazy Render 1
 
第二tab页的提示不会弹出,是因为这时候第二个tab页还没实例化,只有你单击了第二页,它才会实例化并呈现。这说明了tab页会按需进行实例化和渲染。
Lazy Render 2
 
在线demo地址:Lazy Instantiation Example
 
显示和隐藏

 
所有组件都有内建的显示(show)和隐藏(hide)方法。组件的隐藏方法默认使用css属性“display:none“,但是你可以使用hideMode属性修改隐藏方法的配置:
   1:  var panel = Ext.create('Ext.panel.Panel', {
   2:      renderTo: Ext.getBody(),
   3:      title: 'Test',
   4:      html: 'Test Panel',
   5:      hideMode: 'visibility' // 使用 CSS visibility 属性 来显示和隐藏这个组件
   6:  });
   7:   
   8:  panel.hide(); // 隐藏组件
   9:   
  10:  panel.show(); // 显示组件

 

浮动组件


浮动组件,指的是使用css绝对定位(position:absolute)让组件脱离浏览器文档流,并且不参与其容器的布局。一些组件默认就是浮动的,如窗体组件(windows),并且所有组件都可以使用浮动(floating)属性设置为浮动。

   1:  var panel = Ext.create('Ext.panel.Panel', {
   2:      width: 200,
   3:      height: 100,
   4:      floating: true, // 使这个Panel成为一个使用绝对定位的浮动组件
   5:      title: 'Test',
   6:      html: 'Test Panel'
   7:  });
上面的代码实例化了一个Panel组件,但并没有马上呈现它。通常一个组件需要配置一个renderTo(呈现到哪里)属性,或者添加到容器中作为容器的一个子元素,但是如果配置了浮动属性,则不需要这些配置。浮动组件将在第一次显示(一般是调用show方法)的时候自动添加到body中:
 
   1:  panel.show(); // 渲染并呈现panel,panel将被添加到body中,并且css定位属性为postion:absolute;

如果你使用浮动组件,以下这些配置需要注意下:

  • draggable(拖动) 运行在浏览器窗口中拖动组件。
  • shadow(阴影) 配置浮动组件的阴影效果。
  • alignTo()(对齐) 将浮动组件对齐到指定的元素。
  • center() (居中) 让浮动组件在所处的容器中居中。

在线demo:Floating Panel Example

创建自定义组件


组合和扩展

     要创建一个实现自己所需功能的自定义组件,你必须决定你要定义的组件(一般是一个类)是要包含一个组件的实例(组合),还是扩展一个组件(继承)。

建议将自己要实现的功能最相近的现有组件作为基类来扩展(尽量复用已有的功能)。这是因为如果你继承了现有组件,则你的组件就拥有自动管理生命周期不同阶段行为的功能,如:按需呈现,自定控制尺寸,根据布局管理器(layout manager)管理组件定位,从容器中移除时自动销毁等。

    使用继承的方式去创建新的组件类比组合的方式更符合组件层次结构的要求,并且可以从类的外部去管理类本身。

子类

    Ext的类系统(详见第一篇译文)让你可以很容易从现有组件中扩展。下面这个例子创建一个Ext.Component的子类,并且没有添加任何的扩展功能。

   1:  Ext.define('My.custom.Component', {
   2:      extend: 'Ext.Component'
   3:  });

模板方法

Extjs用模版方法模式(Template method pattern)委托到子类,方法的具体行为实现由具体的子类实现.

这意味着在组件的生命周期中,继承链里的每一个类都可以添加自己特定的逻辑。每个类都实现自己的特定逻辑,同时允许继承链中的其它类继续添加自己的逻辑。

渲染(render)方法即是一个典型的例子。render是组件(Component)超类中的一个私有方法,抽象组件(AbstractCompnent)负责启动组件生命周期中的呈现阶段。render不可以重载,但是render会调用onRender方法以允许子类在onRender中添加特定逻辑。每个onRender方法都必须在添加自己的特定逻辑之前先调用父类的onRender方法。

下图说明onRender模版方法的整个流程:

render方法首先被调用(由布局管理器负责),这个方法不允许重载,并且它是在Ext基类实现的。render内部调用在当前子类中实现的this.onRender方法(如果有实现的话).onRender内部又会调用父类的onRender方法.最终,每个子类中的onRender都添加了自己的功能,并且控制返回给render方法的值。

Template Pattern

下面的例子是一个实现了onRender方法的组件子类。

   1: Ext.define('My.custom.Component', {
   2:     extend: 'Ext.Component',
   3:     onRender: function() {
   4:         this.callParent(arguments); // 调用父类的onRender方法
   5:  
   6:         // 这里执行你要添加的额外额渲染任务.
   7:     }
   8: });

值得注意的是,很多模版方法都拥有一个相应的事件.例如 render事件在组件渲染后触发.在子类中,很有必要使用模版方法来执行类生命周期中的各种逻辑,而不是使用事件.因为事件是可以被调用事件的代码暂停,或者被事件处理器停止的。

下面的列表是Component基类中已经实现的模版方法。

  • initComponent 这个方法会被构造器调用。用来初始化数据,设置配置,以及绑定事件处理程序。
  • beforeShow 这个方法在组件显示之前调用。
  • onShow 允许扩展显示(show)操作的行为。在调用父类的onShow方法后,组件将变为可见。
  • afterShow 这个方法在组件显示完后调用。
  • onShowComplete 这个方法在afterShow方法执行完毕后调用。
  • onHide 允许扩展隐藏(hide)操作的行为。在调用父类的onHide方法后,组件将隐藏。
  • afterHide 这个方法在组件隐藏之后调用。
  • onRender 这个方法允许扩展组件渲染阶段的行为。在调用父类的onRender方法后,组件的dom元素即创建。组件所需的对DOM的额外处理可能在这个阶段来完成。
  • afterRender 允许扩展渲染完成之后的行为。在这个阶段,组件DOM元素将根据配置来实现样式风格,组件添加配置的许多css类名,并且配置可见(visibility)和可用(enable)状态。
  • onEnable 允许扩展设置为可用(enable)操作的行为.在调用父类的onEnable方法后,组件将被设为可用.
  • onDisable 允许扩展设置为不可用(disable)操作的行为.在调用父类的onDisable方法后,组件将被设为不可用
  • onAdded 允许扩展组件被添加到一个容器中时的行为.在这个阶段,组件被包含在父容器的子项(items)集合中.在调用父类的onAdded方法后,ownerCt引用将被赋值,并且如果配置了一个引用,refOwner将会被赋值.
  • onRemoved 允许扩展组件从父容器中移除时的行为。在这个阶段,组件会从父容器的子项(items)集合中移除,但并不会被销毁(destroyed,如果父容器的autoDestroy配置为true,或者remove方法的第二个参数传递了一个true值,组件将会自动销毁)。在调用父类的onRemoved方法后,ownerCt 和refOwner 将被设置为null。
  • onResize 允许扩展改变尺寸(resize)操作的行为。
  • onPosition 允许扩展定位(position)操作的行为。
  • onDestroy 允许扩展销毁(destroy)操作的行为。在调用父类的onDestroy方法后,组件将被销毁。
  • beforeDestroy 这个方法在组件销毁之前被调用。
  • afterSetPosition 这个方法在这件位置被设置后调用。
  • afterComponentLayout 这个方法在组件设置布局后调用。
  • beforeComponentLayout 这个方法在组件设置布局之前调用。

选择继承哪个类

考虑到开发效率问题,应该选择一个最佳的类来扩展,并且要考虑基类能提供哪些已有的功能。如果你有一堆组件集需要渲染和管理(即你的自定义组件是作为容器),则应尽量从Ext.Panel类继承.

Panel类有很多现有的功能:

  • Border (边框)
  • Header (最顶上的标题栏)
  • Header tools (标题栏工具条,如关闭,最大化,最小化等一些常用按钮)
  • Footer (最下面的工具条)
  • Footer buttons (最下面工具条按钮)
  • Top toolbar (标题栏下的工具条)
  • Bottom toolbar (footer之上的工具条)
  • Containing and managing child Components(包含和管理子组件)

如果不需要这些功能,则从Panel继承会浪费资源(应该考虑继承其他更简单的类).

组件

如果你要创建的组件不需要包含其他组件(不是作为容器),也就是说,它只是按你的需要封装了某种形式的HTML,那么,继承Ext.Component以实现自己的组件是最恰当的。例如,下面这个类是一个包含了一个HTML图片元素的组件,并且允许获取和设置图片的src属性。并且在图片加载完毕后触发一个load事件:

   1: Ext.define('Ext.ux.Image', {
   2:     extend: 'Ext.Component', // 父类: Ext.Component
   3:     alias: 'widget.managedimage', // 这个组件的xtype为: 'managedimage'
   4:     autoEl: {
   5:         tag: 'img',
   6:         src: Ext.BLANK_IMAGE_URL,
   7:         cls: 'my-managed-image'
   8:     },
   9:  
  10:     // 在onRender时添加自定义的行为.
  11:     // 添加一个load事件监听。
  12:     onRender: function() {
  13:         this.autoEl = Ext.apply({}, this.initialConfig, this.autoEl);
  14:         this.callParent(arguments);
  15:         this.el.on('load', this.onLoad, this);
  16:     },
  17:  
  18:     onLoad: function() {
  19:         this.fireEvent('load', this);
  20:     },
  21:  
  22:     setSrc: function(src) {
  23:         if (this.rendered) {
  24:             this.el.dom.src = src;
  25:         } else {
  26:             this.src = src;
  27:         }
  28:     },
  29:  
  30:     getSrc: function(src) {
  31:         return this.el.dom.src || this.src;
  32:     }
  33: });

使用示例:

   1: var image = Ext.create('Ext.ux.Image');
   2:  
   3: Ext.create('Ext.panel.Panel', {
   4:     title: 'Image Panel',
   5:     height: 200,
   6:     renderTo: Ext.getBody(),
   7:     items: [ image ]
   8: })
   9:  
  10: image.on('load', function() {
  11:     console.log('image loaded: ', image.getSrc());
  12: });
  13:  
  14: image.setSrc('http://www.sencha.com/img/sencha-large.png');

在线示例:在线示例。这个示例只是演示用的。在真实的开发场景中,应该使用Ext.Img类.

容器

如果你要创建的组件需要包含其他组件(即作为容器),但是你并不需要前面提到的Panel的各种功能,那从Ext.container.Container继承是最恰当的.使用容器,应该注意用来渲染和管理子组件的布局(Layout)是哪一种.

容器拥有以下模版方法:

  • onBeforeAdd 这个方法在一个新的子组件被添加进容器的时候调用.新的组件被传递给这个方法,用来对这个新组件做一些额外的操作,或者对容器做一些其他的预处理.返回false将会终止组件添加到容器中.
  • onAdd 这个方法在新的子组件被添加到容器后调用。已被添加进来的组件被传递给这个方法。这个方法用来根据子组件的状态更新(容器的)内部结构。
  • onRemove 这个方法在一个子组件被从容器中移除后调用。移除的组件被传递给这个方法。这个方法也是用来根据子组件的状态更新(容器的)内部机构。
  • beforeLayout 这个方法在子组件在容器中布局完成(并且渲染,如果需要的话)后调用。
  • afterLayout 这个方法在子组件在容器中布局完成(并且渲染,如果需要的话)后调用。

Panel

如果你要创建的组件需要用到头部(header),页脚(footer)和工具条(toolbars),那从Ext.Panel扩展是最恰当的.

注意:Panel是一个容器,因此要记住使用哪种布局(Layout)来渲染和管理子组件。

继承Ext.Panel类以定义新组件通常是为应用程序高度定制的,并且通常使用布局配置用于聚合其他组件(一般是其他容器或者表单域),并且提供操作包含的组件和按钮的方法.

Panel拥有以下模版方法:

  • afterCollapse 这个方法在Panel折叠(Collapsed)后调用.
  • afterExpand 这个方法在panel展开后调用.
  • onDockedAdd 这个方法在往Panel中添加停靠项(docked item)后调用.
  • onDockedRemove 这个方法在Panel的停靠项(docked item)被移除后调用.
posted @ 2011-08-01 01:19  骑兵程序员  阅读(5840)  评论(4编辑  收藏  举报