ExtJS5.x的组件讲解

一个ExtJS应用程序的UI用户界面是由一个或者多个叫做组件的部件构成的。所有的组件都是Ext.Component类的子类,它参与组件的自动化生命周期管理包括实例化、渲染、大小、位置、销毁。ExtJS提供了一系列非常有用的组件,任何组件可轻松的扩展到一个自定义的组件。

组件的继承关系

容器是一个特殊的组件类型,它可以包含其他组件。一个典型的应用程序是由类似于如下树状结构的许多嵌套组件组成的。容器负责管理他们的子组件的生命周期,包含创建,渲染,大小还有位置和销毁。一个典型的应用程序的组件继承从顶层的Viewport开始,它里面还嵌套有其它的容器。


组件继承关系

子组件通过使用容器的items配置增加到容器中,下面的例子使用Ext.create()创建初始化了两个面板,然后作为Viewport的子组件增加到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 ]

});

容器使用布局管理器来管理组件的大小还有位置,想要获取关于布局的更多信息,可以参考上一章节关于容器和布局的内容。

XTypes和懒加载

每一个组件都有一个符号名叫做xtype。像Ext.panel.Panel就有一个xtype名叫做‘panel’。上面的例子展示了如何增加一个已经存在的组件到一个容器中。然而,在大型的应用程序中,这不是最理想的,因为不是所有的组件都要立即被实例化,一些组件可能永远不需要被初始化,这取决于你的应用程序。例如,一个使用tab panel的应用程序只需要获取用户点击之后的那个tab的内容。这就是xtypes派上用处的地方,通过允许容器的子组件在前置进行配置,需要的时候才进行实例化。

下面的示例代码演示了懒实例化和呈现一个使用选项卡面板的容器的子组件。每个标签都有一个事件侦听器,标签渲染时显示一个警告框。

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的alert弹出框。这个发生是因为这是默认的tab页签,所以容器就立即调用并实例化容器的Tab Panel。


第一个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是默认的浮动,所有的组件都可以通过配置来实现浮动:

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 the floating panel

下面有一些对浮动组件来说可以注意的配置方法

1.draggable:可以拖动屏幕周围浮动组件。

2.shadow:定制的外观浮动组件的影子。

3.alignTo:将浮动组件指定到特定的元素。

4.center() 将组件浮动到组件的中心

创建自定义组件

组合或者扩展

当创建一个新的UI类的时候,该类必须做出决定是否应该拥有一个组件的一个实例,或扩展那个组件。

推荐根据功能需要继承一个最接近的基类。ExtJS提供的自动生命周期管理包含了当需要的时候自动渲染,自动大小,有布局管理的时候也会自动调整位置,也会自动的从容器中移除和销毁。

通过组件创建新类,该组件能替换组件继承为最佳,不推荐使用一个拥有ExtJS组件的新类在外部去渲染和管理。

子类

类系统让扩展ExtJS框架变得非常容易。

Ext.Base是所有ExtJS类的构建块,这个类的原型方法和这个类的静态成员均被其他类继承。

尽管你可以再低层次的情况下增加一个方法,但是开发人员在通常情况下会想通过高一点的继承链来开始。

下面这个例子创建了一个Ext.Component的子类:

Ext.define('My.custom.Component', {

    extend: 'Ext.Component',

    newMethod : function() {

      //...

    }

});

这个例子创建了一个新的类‘My.custom.Component’,它继承了Ext.Component的所有方法属性等。

模板方法

ExtJS使用模板方法模式委派子类行为,该行为只指定给该子类。

这意味着,在继承链中的每一个类在一定阶段都可能拥有特定的逻辑生命周期。每一个类实现自己的独有的行为,继承链上的其它类则继续拥有自己的逻辑。

典型例子就是渲染方法,render是定义在Component类中的一个方法,它对组件初始化的呈现阶段生命周期负责。render不能被重写,但是在处理子类实现的时候可以调用onRender方法来实现自己的特有属性方法。每一个onRender方法在贡献自己额外逻辑的时候必须调用父类的onRender方法。

下面的表格展示了onRender模板方法的功能。

render方法被调用(通过布局管理器来实现的)。这个方法也是通过Ext基类实现的,不能被重写。当前类如果重写了this.onRender这个方法,它就会被调用。这会导致调用父类的实现版本,以此类推。最终,每个类贡献了自己的功能,返回到render方法。


模板方法调用链

下面是一个组件子类实现onRender的例子:

Ext.define('My.custom.Component', {

    extend: 'Ext.Component',

    onRender: function() {

        this.callParent(arguments); // call the superclass onRender method

        // perform additional rendering tasks here.

    }

});

需要值得注意的是,许多模板方法都有自己对应的事件,例如渲染事件,是在渲染事件触发的时候执行。然而,有些时候,在重要声明周期阶段执行的,必须使用模板方法的逻辑而不能事件,事件可以通过编程来停止或者暂停。

下面是一些子类可以实现的组件的模板方法:

initComponent:这个方法被类构造方法唤醒,用来初始化数据,设置属性,附加事件处理。

beforeShow:在组件显示之前被调用

onShow: 允许添加行为操作,在超类的onShow方法被调用之后就组件就可见了。

afterShow: 方法在组件被显示之后调用。

onShowComplete:这个方法在afterShow完成之后被调用

onHide:在隐藏的时候可以增加一些操作,调用超类的onHide方法之后,组件就看不见了。

afterHide:组件隐藏之后的事件处理

onRender:渲染的时候执行的事件

afterRender:渲染完成之后可以增加的额外操作。在你这个阶段,组件的元素已经根据配置或者css雷鸣增加了类型,并且将会被配置可见性和启用状态。

onEable:组件可用性,调用超类的这个事件的时候,组件就可以使用了

onDisable:组件不可用处理的时候的事件。

onAdded:当组件被增加的时候,组件被增加到容器的时候。在这个阶段,组件在父类容器的子条目集合中,当调用超类的这个方法之后,容器就是被展现,如果有引用,引用也会被设置。

onRemoved:被移除的时候的事件。这时候,组件以及从父容器之中移除了,但是还没有销毁。调用超类的这个方法之后,容器就不会被展现了。

onResize:大小变化的时候的调用事件

onPosition:位置变化的时候调用的事件

onDestroy:销毁的时候的事件

beforeDestroy:销毁之前

afterSetPosiotion:设置位置之后

afterComponentLayout:组件布局之后

beforeComponentLayout:组件布局之前

我们该扩展哪个类

选择一个好的继承类,且该基类必须要提供基本功能的,关系到效率的问题。有一种趋势就是当你设置UI组件的,并且这些组件需要被渲染和管理的时候,总是继承Ext.panel.Panel。

Panel类有如下能力:

Boder

Header

Header Tools

Footer

Footer Buttons

Top toolbar

Buttom toolbar

Containig and managing child Components

如果这些不需要,那么使用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;

    }

});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类。

Container容器

如果组件包含其他组件但是又不需要panel的额外功能,那么就选容器比较适合了。在面板级别,需要重点关注的是有个类似的名字的,Ext.layout.container.Container是用来被渲染和管理子组件的。

容器拥有下面这些模板方法:

onBeforeAdd:子组件增加的时候这个方法被调用。通过了新组件,可以用来修改组件,或准备容器。返回false中止添加操作。

onAdd:组件被增加完成的时候调用。它是通过组件已被添加。这种方法可以用于更新任何内部结构可能依赖于状态的子元素。

onRemove:它是通过组件已被添加。这种方法可以用于更新任何内部结构可能依赖于状态的子元素。

beiforeLayout:这个方法被调用之前容器已经制定了(如果需要)并呈现它的子组件。

afterLayout:调用该方法后,集装箱已经制定了(如果需要)并呈现它的子组件。

Panel面板

如果UI界面需要头信息,底部信息,工具条,那么Ext.panel.Panel就是一个合适的选择。

重要的是:一个面板是一个容器。重要的是要记住,布局是用于呈现和管理子组件的。

扩展Ext.panel的子类面板通常非常特定于应用程序,一般用于聚合其他UI组件(通常是容器,或表单字段),配置布局,并提供所包含的组件通过控制tbar bbar的操作方法。

面板拥有如下的模板方法:

afterCollapse:当折叠的时候被调用

afterExpand:当展开的时候被调用

onDockedAdd:当停靠的时候调用

ondockedRemove当停靠移除的时候调用。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容