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

Ext JS 6组件,容器, 布局

2016-08-16 17:47 274 查看

组件

Ext JS应用的UI是由一个或者多个widgets组称, 我们称之为Components. 所有的组件都是Ext.Component的子类,允许组件自动管理生命周期, 包括instantiation, rendering, sizing and positioning, 以及destruction. Ext JS提供了很多直接可以使用的组件,

能过简单继承,可以创建自定义组件。

The component life cycle

在我们讲布局系统和窗口部件之前,我们需要先知道组件是如何工作的

在Ext JS框中,所有的组件都是继承于Ext.Conponent类。Ext.Conponent的的别名为Ext.AbstractComponent, 它为整个框架的组件提供了共享的方法。

当我们创建组件, 比如panels, windows, grids, trees, 或其它,它有一个生命周期。

在生命周期的每一个阶段要做什么,知道这些对我们来说非常重要。这对我们创建一个自定义组件,或者扩展一个组件非常有帮助。

在组件的生命周期中有三个主要阶段:初始化处理过程,渲染过程, 以及销毁过程。

在初始化阶段,创建一个新的实例,并且在组件管理器中注册;接着在渲染阶段,会在DOM树中,创建所需要的节点。而在销毁阶段,销毁组件,删除监听器,并且从DOM中删除node节点.



为了更好的理解上面的三个阶段,让我们创建一个panel组件, 然后看看到底发生了什么

var panel = Ext.create("Ext.panel.Panel",{
title: "My First panel",
width: 400,
height: 250,
renderTo: Ext.getBody()
});


初始化阶段

这个阶段的主要任务是,根据配置,创建组件的实例。它也会在component管理器中注册我们新创建的组件,以及其它的一些工作。以下是这个阶段的所有步聚



以下是每一步详细的解释

第一步,我们将配置属性应用到我们正在创建的实例上。在上面的代码中,title, width, height, renderTo属性,会被复制到panel实例中,以及任何我们其它定义的属性

第二步定义常见的事件,比如enable, disable, show等。每个组件都拥有这些事件

为这个实例分配一个唯一标识符。如果我在配置中有定义id(bad practice), 则使用配置中的ID

验证我们是否有在配置中指定plugins, 如果指定,则创建这些插件的实例。插件既为我们创建的组件的一个额外功能。在上面我们没有定义任何插件,所有这一步跳过

执行initComponent函数,它是一个模板方法,会在constructor中调用, 如果我们想在实例创建时,执行自定义的代码,应该在subclasses中重写这个方法, 当然Component在不同的阶段,都提供了template方法,能让我们添加额外的功能

在这个步聚,我们将新创建好的实例添加到Ext.ComponentManager对像。这意味着我们创建的组件都保存在组件管理器中,允许我们通过Ext.getCmp方法,并且传递ID,就能获取这个组件

//getting a component by its ID
var panel = Ext.getCmp("panel-1234");
console.log(panel);


getCmp方法在调试应用时非常有用,我们可以在DOM元素中获得任何组件的ID. 通过这个ID, 我们可以获得这个实例, 并且检查我们对像的状态,但不推荐在我们的代码中使用这种方法,我们可以使用Ext.ComponentQuery.query方法。Ext.ComponentQuery.query(‘panel’),它返回一个使用了Ext.panel.Panel实例数组

Component包含两个mixins类, 一个是事件管理器,另一个我们组件的状态。

如果我们有定义plugins, 在上面的步聚中我们创建了这些插件的实例,现在,调用每个插件的init()方法,并用传递当前组件给它们,进行初始化。

如果在配置中有renderTo属性,那么在这一步开始渲染,那么表示我们组件的虚拟节点会被插入到DOM中。如果没有定义这个属性,则什么也不发生。我们可以在其它的任何地方,渲染我们的组件

var panel = Ext.create("Ext.panel.Panel",{
title: "My First panel",
width: 400,
height: 250
});
panel.render(Ext.getBody());


如果我们想之后渲染组件,可以调用这个组件的render方法,并且传递要渲染组件的位置作为参数。在上面的代码中,我们将它插入到document中。我们也可以设置为节点的ID
panel.render("some-div-id");


*注意:如果组件是在另一个container中, 则不需要调用render方法,它们会在container被创建/渲染时,自动渲染

The rendering phase

渲染阶段只在组件还没有被渲染时发生。在这个阶段,所有的节点将被插入到DOM中, 样式和事件监听器将被应用,所以我们能够看到新的组件外观,并且与它交互(事件).



触发beforeRender事件,如果它的监听器返回false, 则停止渲染

确认组件是否floating组件,即在配置中指定floating为true. 常见的组件有menu, window. 如果是,分配z-index属性。

创建一个container属性,并且将一个DOM元素赋值给它,表示组件将在哪里被渲染, container属性是一个Ext.dom.Element实例

组件的模板方法onRender被执行,创建一个el属性,它表示组件的主节点元素。我们可以为组件定义一个html模板,然后会被创建,并且添加到主节点中。我们可以重写onRender方法,添加指定的节点到DOM中。

设置显示模式,既根据配置中的hideMode, 它的值可以为(display, visibility or offset)

如果有设置overClas属性,则监听鼠标移入和移出事件,用来添加和删除这个css类。

在第七步,触发render事件,组件实例将作为参数传递给事件处理器

第八步用来初始化内容。有以下三个方法来设置组件的内容

可以在组件的属性中定义一个html属性

contentEl属性,它的值为已存在的DOM元素的ID.

tpl属性,同时定义data属性对像,以替换为我们模板中点位符

以下代码显示了上面的三种方式,我们应该在组件中只使用其中一种方式

//Using the HTML property
Ext.create("Ext.Component",{
width: 300,
height: 150,
renderTo: Ext.getBody(),
html: "<h1>Hello!</h1><p>This is an <strong>example
</strong> of content</p>"
});

//Using an existing DOM element with an ID content
Ext.create("Ext.Component",{
width: 300,
height: 150,
renderTo: Ext.getBody(),
contentEl: "content"
});
//Using a template with data
Ext.create("Ext.Component",{
width: 300,
height: 150,
renderTo: Ext.getBody(),
data: {name:"Veronica", lastName:"Sanchez"},
tpl: ["<h1>Content</h1><p>Hello {name} {lastName}!</p>"]
});


返回到render阶段,下一步执行afterRender模板方法. 如果一个组件包含了子组件,子组件将在这一步渲染。我们在container之后讨论.

在上一步,afterRender事件触发。我们可以在subclass中监听这个事件,在所有的节点都被渲染到DOM后,执行一此动作。

在上一步,注册鼠标,键盘,大小等监听器

最后一步,如果有设置hidden属性,则隐藏主组件。同样,如果设置了disabled为true. 则组件执行disable方法,它会为组件的主节点添加css类,使得组件的外观表现为disabled, 并且在DOM上面的html标签为disable标志
<input name="test" disable>


以下的代码显示了渲染阶段是如何工作的,我们整个的处理都是从调用render方法开始

var mycmp = Ext.create("Ext.Component",{
width: 300,
height: 150,
data: {
name:"Veronica",
lastName:"Sanchez"
},
tpl:["<h1>Content</h1><p>Hello {name} {lastName}!</p>"]
});
//The rendering phase starts for this component
mycmp.render(Ext.getBody());


通过以上的学习,我们知道,可以在定义我们自己的类时,重写onRender, afterRender方法。

The destruction phase

这个阶段主要是清除DOM, 删除监听器,并且通过删除对像和数组,清理被使用的内存。当我们不想在使用一个组件时,销毁组件非常重要。销毁阶段将在我们使用组件完成任务后进行。比如,我们创建了一个窗口,它有一个closeAction属性可以用来销毁。(默认情况下,已经设置过了),销毁阶段将在用户关闭窗口后被调用



销毁阶段在开始时,会先触发beforeDestroy事件,如果事件处理器返回false, 则停止销毁。如果继续,并且组件是floating类型的,则从floating manager中取消注册。

执行模板方法beforeDestroy,所有subclasses通过使用这个方法来删除它们的子元素或者清理内存

在第三步,如果将被销毁的组件是其它组件的子组件,那么在父组件中,这个组件的引用,将被删除

onDestroy方法将被执行,这是一个模板方法,执行这个方法是为了销毁我们组件的属性,并且确保它的子组件(已经被添加到当前组件)也被销毁, 同时,也清理我们自己创建的自定义监听器

第五步,销毁所有的插件

如果组件已经被渲染了,则从DOM中删除所有的组件节点,和节点所对应的监听器

触发destroy事件,我们可以监听这个事件,执行相应的动作

在component manager中取消组件实例的注册,清理所有的事件。

有一件非常重要的事情需要记住,我们应当删除和清理在组件中使用的内存,以及我们在添加节点到DOM之前使用的内存。我们应该重写相应的方法,来正确的销毁我们的组件。

如果我们要清除一个组件,可以调用它的destroy方法,这个方法将会触发上面的销毁阶段,以上所有的步骤将会被执行

//The destroy phase starts for this component
cmp.destroy();


Lifecycle的作用

现在我们已经知道创建一个组件需要经理哪些步骤,我们可以利用lifecycle来自定义我们的组件。下面的例子显示了,在生命周期的某一个阶段,我们可以通过重写某些方法,实现额外的功能

Ext.define('Myapp.sample.CustomComponent',{
extend: 'Ext.Component',
initComponent: function(){
var me = this;
me.width = 200;
me.height = 100;
me.html = {
tag: 'div',
html: 'X',
style: { // this can be replaced by a CSS rule
'float': 'right',
'padding': '10px',
'background-color': '#e00',
'color': '#fff',
'font-weight': 'bold',
'cursor': 'pointer'
}
};
me.myOwnProperty = [1,2,3,4];
me.callParent();
console.log('Step 1. initComponent');
},
beforeRender: function(){
console.log('Step 2. beforeRender');
this.callParent(arguments);
},
onRender: function(){
console.log('Step 3. onRender');
this.callParent(arguments);
this.el.setStyle('background-color','#ccc');
},
afterRender : function(){
console.log('4. afterRender');
this.el.down('div').on('click',this.myCallback,this);
this.callParent(arguments);
},
beforeDestroy : function(){
console.log('5. beforeDestroy');
this.callParent(arguments);
},
onDestroy : function(){
console.log('6. onDestroy');
delete this.myOwnProperty;
this.el.down('div').un('click',this.myCallback);
this.callParent(arguments);
},
myCallback : function(){
var me = this;
Ext.Msg.confirm('Confirmation','Are you sure you want to close
this panel?',function(btn){
if(btn === 'yes'){
me.destroy();
}
});
}
});


Ext.onReady(function(){
Ext.create('Myapp.sample.CustomComponent',{
renderTo : Ext.getBody()
});
});




我们可以看到以上的方法都是基于组件的生命周期进行执行,如果我们想要销毁一个组件,我们需要点击右上角的按纽。它会调用destroy方法,将节点从DOM中删除,删除事件以及从内存中删除对像。

在Ext JS中,理解组件的生命周期对于添加自动义事件和监听器来说非常重要,这样我们才能在我们的应用程序中提供适当的功能和自定义的代码。

The Component Hierarchy

一个Container是一个特殊类型的组件,它可以包含其它的组件。一个标准的application是许多嵌套的组件,类似于树的结构组成,我们称之为组件层级。Containers负责管理组件的子组件的组件生命周期,这包括,创建,渲染,大小和位置,以及destruction. 一个标准应用

的组件层级是从Viewport开始。然后在Viewport嵌套其它Container or Component



子组件被添加到容器中,是通过在创建容器对像时,传入items属性。如下所示

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 ]
});


Containers 使用Layout Managers来确定子组件的大小和位置, 更多关于布局信息可以查看Layout and Container Guide

XTypes and Lazy Instantiation

每一个组件都有一个像征性的名字,称为xtype, 比如, Ext.panel.Panel的xtype为panel. 在上面的代码中,我们演示了如何初始化一个组件实例,并且将它们添加到容器中。在一个大型的应用中,这不是一种好的方法,因为不是所有的组件初始化之后在使用。有的组件可以不会被初始化,这取决于应用程序是如何使用的。

比如,一个应用中的Tab Panel, 每个面板的内容只在这个tab被点击后在渲染。这就是为什么要使用xtype的原因为,它允许子组件可以容器中预先配置,但它不会被初始化,除非容器决定需要它时,才会被初始化。

下面的例子,演示了Tab Panel中lazy instantiation以及渲染组件。每一个panel注册了一个事件render(只触发一次)监听器,当一个panel被渲染时,显示一个警告。

@example
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.');
}
}
}
]
});


Showing and Hiding

所有的组件都有show 和 hide方法。 默认是修改组件的css为”display:none”, 但也可以改变它的hideMode为 visibility.

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


Floating Components

Floating Component是能过css的绝对定位(absolute positioning) 将组件从文档流中独立出来。它不在受父容器的布局影响。有些组件,比如Windows,默认就是float. 但任何其它的组件都可以通过floating为true进行设置

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进行配置,或者作为一个子组件,添加到一个容器中。但对于浮动组件来说,他们都不适用。 Floating 组件会在第一次调用show方法时,自动的在document body中渲染。

panel.show(); // render and show the floating panel


以下的这些配置和方法,跟floating components有关:

draggable - 允许floating组件在屏幕内可拖动

shadow - 自动义 floating components的阴影

alignTo() - 将floating components组件,与指定的组件对齐

center() - 将floating component在它的container,居中对齐

创建自定义组件

Subclassing

Ext.Base 是所有类的父类,它的原型和静态方法都会被其它类所继承。

虽然你可以扩展最底层的 Ext.Base, 但在很多情况下,开发者想要在更高级的类开始扩展

下面的代码创建了一个Ext.Component的子类

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

newMethod : function() {
//...
}
});


上面的代码创建了一个新的类,My.custom.Component, 它继承了Ext.Component所有的功能(methods, properties etc).

Tempplate method

Ext JS使用 Template method pattern(一种面向对像的设计模式,在模板类-父类中定义抽像方法,而在具体的子类中具体的实现这些方法),将行为委托给子类。

这意味着,在继承链中的每个类,在组件生命周期的某个阶段(初始化,读取,sizing, positioning),都可以“贡献”额外的逻辑。每一个类都实现了自子的特殊行为,同时允许继承链中的其它类可以继续贡献它们的逻辑.

以render功能为例,render方法是定义在Component中。它负责组件生命周期中的渲染阶段的初始化。render函数不能被重写, 但是在render中,会调用onRender方法,所以允许子类通过添加onRender方法,添加自己的逻辑。每一个类的onRender方法,在实现自己的逻辑前,必须调用它父类的onRender方法。

下图演示了onRender这个模板方法原理

render方法被调用(通过这个组件的Container的layout manager调用). 这个方法在Ext.Component中定义,并且不能被子类重写。它会调用this.onRender, 如果有定义子类,则会调用子类的onRender方法。因为每个onRender方法必须调用父类的onRender,所以它依次向上调用,执行完父类的逻辑,然后在依次返回到当前代码,最后控制权返回到render方法.



以下是具体的代码

Sample Code

Ext.define('My.custom.Component', {
extend: 'Ext.Component',
onRender: function() {
this.callParent(arguments); // call the superclass onRender method

// perform additional rendering tasks here.
}
});


非常重要的是,许多的模板方法,也都有对应的事件名称。比如render event会在组件被渲染后触发。在定义子类时,是通过模板方法,而不是事件来实现它要添加的逻辑。这是因为,事件可以在监听器内被暂停或者停止。

以下是可以在Component子类中实现的模板方法

initComponent 这个方法在constructor中被调用。它可以用来初始化数据,调协配置,添加事件监听器

beforeShow 这个方法会在显示前调用

onShow 允许在show操作中添加额外的形为。在调用了 supperclass的onShow后,组件才会被显示

afterShow 这个方法在组件显示后调用

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

onHide 对组件在隐藏操作时,添加额外形为

afterHide 在组件隐藏后调用

onRender 在渲染阶段调用

afterRender 当渲染完成时,此阶段的组件已经依据配置,应用了样式,我们可以添加样式,配置组件的可见性。

onEnable 在enable操作时,添加额外的操作,并且调用父类的onEnable, 然后组件成为可用状态

onDisable, Allows addition of behavior to the disable operation. After calling the superclass’s onDisable, the Component will be disabled.

onAdded 组件被添加到容器时调用, 在当前阶段,组件已经在父容器的子组件items中。调用了superclass的onAdded后,然后ownerCt引用这个元素,如果有设置ref 配置,refOwner将被设置

onRemoved 从父容器移除时调用,当前阶段,组件从父容器的子组件items中移除,但还没有被destroyed(如果parent 容器的autoDestroy设置为true时,它将会被destroy, 或者在调用时传递的第二个参数为truthy.) . 在调用supperclass的onRemoved后, ownerCt和refOwner将不在有效

onResize 在resize操作时,添加额外的形为

onPosition 在position 操作时,添加额外的形为

onDestroy 在destroy操作时,添加额外的形为。在调用到superclass后,这个组件被摧毁

beforeDestroy 在组件被destroy之前调用

afterSetPosition 在组件的位置已经设置完成之后调用

afterComponentLayout 在组件被布局后调用

beforeComponentLayout 在组件被布局前调用

Which Class to Extend

选择最合适的类去扩展是非常重要的, 这个基础类必须提供符合我们要求的功能。通常我们选择Ext.panel.Panel, 它可以被渲染,也可以管理其它组件

Panel class有以下的功能

Border

Header

Header tools

Footer

Footer buttons

Top toolbar

Bottom toolbar

Containing and managing child Components

如果你定义的组件不需要上面的功能,则使用Panel就浪费了资源

Component

如果一个UI组件不需要包含其它组件,换言之,如果只是简单的封装一些HTML的表单,则 extending Ext.Component非常合适,比如,下面的组件,wrap一个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

Container

如果创建的组件只是用来包含其它的组件,而不需要我们在上面提及的Panel的功能。则可以使用 Ext.container.Container. 在Container级别,需要记住的是使用Ext.layout.container.Container 管渲染和管理子组件

Container还包含以下的额外template 方法:

* onBeforeAdd 这个方法在添加一个新的组件时调用,它传递了一个新的组件,我们可以修改这个组件,如果返回false, 则终此添加操作

* onAdd 在一个组件添加完成后调用。它传递已经添加好的组件。这个方法用来根据子组件items的状态,更新内部的结构。

* onRemove 在一个新的组件被删除后调用,传递这个被删除的组件。根据子组件的items的状态,更新内部的结构

* beforeLayout 在容器对它的子组件布局前调用

* afterLayout 在容器对它的子组件布局后调用

Panel

如果创建的组件,必须有header, footer, or toolbars, 则Ext.panel.Panel非常合适

一个Panel是一个容器,它可以使用layout来读取和管理它的子组件

继承Ext.panel.Panel类通常都是应用级的,并且用来聚合在layout配置中的其它的UI组件(Containers or form fields)。并且通过tbar和 bbar 提供对包含的组件的操作

Panel类有以下的template方法

afterCollapse 在面板收起时调用

afterExpand 在面板展开时调用

onDockedAdd 一个docked元素被添加时调用

onDockedRemove 一个docked元素被删除时调用

About containers

在当前我们知道了组件生命周期的所有阶段,其中有一个阶段就是组件的子组件也会被渲染。现在我们将学习什么是容器,并且如何为它添加子组件。

Ext.container.Container用于管理子组件,并且使用layouts来排列它的子组件。如果我们想我们的类包含其它类,那么创建的这个类应该继承Ext.container.Container. 值得注意的是,Ext.container.Container类也是扩展于Component, 所以它也拥有conponent lifecycle.



容器类能过items属性来添加子元素。或者使用add方法来添加一个新的组件作为它的子元素。

Ext.define("MyApp.sample.MyContainer",{
extend: "Ext.container.Container", //Step 1
border: true,
padding: 10,
initComponent: function(){
var me = this;
Ext.each(me.items,function(item){ //Step 2
item.style = {
backgroundColor:"#f4f4f4",
border:"1px solid #333"
};
item.padding = 10;
item.height = 100;
});
me.callParent();
},
onRender: function(){
var me = this;
me.callParent(arguments);
if( me.border ){ //Step 3
me.el.setStyle( "border" , "1px solid #333" );
}
}
});


当我们继承于Container类时,我们可以使用items属性来定义容器的子元素,我们遍历items属性(数组),并且给每一项添加基本的样式。因此我们使用initComponent方法,它们在创建这个类的实例时,自动执行。同时通过callParent方法,来调用父类的initComponent方法.

在最后一步,我们重写了onRender方法,在执行完callParent方法后,我们可以访问它的el属性,它引用了当前组件的在 DOM中的主节点。如果我们有在创建这个组件时,设置了 border属性,我们将为主节点添加一个边框。

一旦我们创建完类后,就可以使用它创建它的实例。

Ext.onReady(function(){
Ext.create("MyApp.sample.MyContainer",{
renderTo: Ext.getBody(),
items: [{
xtype: "component",
html: "Child Component one"
},{
xtype: "component",
html: "Child Component two"
}]
});
});


查看Ext js中的所有xtype定义枚举

以下是上面代码的效果图,它由一个主组件包含两个子组件。



当我们使用容器时,我们可以在主容器中使用defaults属性,为所有的子组件,应用相同的属性(default values/configurations). 让我们为上面的例子添加默认值

Ext.onReady(function(){
Ext.create("MyApp.sample.MyContainer",{
renderTo: Ext.getBody(),
defaults: {
xtype : "component",
width : 100
},
items :[{
html:"Child Component one" //xtype:"component",
},{
html:"Child Component two" //xtype:"component",
}]
});
});


defaults属性接受一个对像,这个对像包含我们想要对items数组中子组件的相同配置。在这里,我们只是就用了width和xtype属性。这样,我们就不需要在items中,为每个子组件,重复的使用xtype和width.

容器的类型

Ext JS使用多个组件作为容器使用,它们每一个有它自已的功能。以下是常用容器列表

ContainerDescription
Ext.panel.Panel它继承于Ext.container.Container, 它是Ext JS最常用的容器之一
Ext.window.Window它继承于Ext.panel.Panel. 主要用于应用程序的窗口。类型为floating类型的组件,可以被重置大小,并且可以被拖动。所以windows可以被最大化为整个viewport.
Ext.tab.Panel继承于Ext.panel.Panel, 它可以包含其它 Ext.pane.Panel组件,并且为每一个子panel创建一个tab标签。同时 tab panel使用card layout来管理子组件
Ext.form.Panel它继承于Ext.panel.Panel,为form提供了一个标准的容器。从本质上说,它是一个面板容器,用来创建基础的form来管理field组件
Ext.Viewport它表示整个应用区域(浏览器窗口). 它渲染自已到document body中。大小设置为浏览器窗口的尺寸
注间,每一个container都有一个layout属性,这个属性将让我们有能力来呈现容器的子组件,并以不同的方式来排列它们

Viewport

Viewport如我们上面说的,它表示的是整个应用程序的可视区域,并且我们在一个web page中,只创建一个Viewport.

Ext.onReady(function(){
Ext.create('Ext.container.Viewport',{
padding:'5px',
layout:'auto',
style : {
'background-color': '#fc9',
'color': '#000'
},
html:'This is application area'
});
});


建议,不管你创建的应用是纯代码或者MVC或者MVVM架构,都应使用Viewport组件

panel

panel组件是最常用的组件。一个panel可以包含其它panels,甚至其它组件

Ext.onReady(function(){
var MyPanel = Ext.create("Ext.panel.Panel",{
renderTo: Ext.getBody(),
title: 'My first panel...',
width: 300,
height: 220,
html:'<b>Here</b> goes some <i>content</i>..!'
});
});




Panels 对比 containers

如之前看到的,container创建了一个基础的HTML DOM元素,然后包含了子元素。而Panels的使用,则创建了其它的区域(header and tools), 并且比container有更多的功能.



Window component

一个Window就是一个浮动的面板,并且包含更多的功能。它继承于Panel类。这表示,我们可以使用Panel中的所有方法。同时,我们双可以拖动,关闭它等等。

var win = Ext.create("Ext.window.Window",{
title: 'My first window',
width: 300,
height: 200,
maximizable: true,
html: 'this is my first window'
});
win.show();

//或者
Ext.create("Ext.window.Window",{
title: 'My first window',
width: 300,
height: 200,
maximizable: true,
html: 'this is my first window'
}).show();


在上面我们没有使用renderTo以及 render方法,而是直接调用show方法显示,这是因为floating component会自动渲染到document body

Layouts

每一个容器都有一个layout来管理它子组件的大小和位置,我们可以使用相应的类来实现固定布局和流体布局。

Ext.create('Ext.panel.Panel', {
renderTo: Ext.getBody(),
width: 400,
height: 200,
title: 'Container Panel',
layout: 'column',
items: [
{
xtype: 'panel',
title: 'Child Panel 1',
height: 100,
columnWidth: 0.5
},
{
xtype: 'panel',
title: 'Child Panel 2',
height: 100,
columnWidth: 0.5
}
]
});


当前,你已经知道container是如何工作的了。我们可以设置一个layout来排列它的子元素。如果我们没有定义layout属性,默认的auto layout将会被使用。即一个子组件,在另一个子组件之后显示。

我们有许多不同的layout来排列我们的组件,比如accordions(折叠), cards, columns等等。

我们可以在 Ext.layout.container包中找到所有的layout. 在layouts 枚举页面http://docs.sencha.com/extjs/6.0.2-classic/Ext.enums.Layout.html可以查看所有的layout, 我们将看到许多的类,每一个表示一种layout. 常见的布局如下

The Border layout

The Fit layout

The Card layout

The Accordion layout

The Anchor layout

Layout系统原理

一个容器的Layout用来初始化所有子组件的大小和位置。当调用Container的updateLayout方法,它会触发Layout计算容器内所有组件的尺寸和位置。 updateLayout方法完全是一个递归的方法,所以容器的子组件也也会调用它们的updateLayout方法,直到组件层级的最底层。你通常不会在应用代码中调用updateLayout方法,因为框架为自动为你处理。

当容器被重置大小是,或者子组件被添加或者移除时,触发re-layout. 通常我们可以依赖框架为我们处理布局的更新。但有的时候我们需要手动进行布局更新,这时我们可以使用suspendLayout 属性设置为 true。比如我们在添,删除元素时,正常的会触发布局,但我们想在整个添加和删除操作都完成后,更新整个布局,这时候可以将suspendLayout设置为false. 并且手动调用updateLayout方法

var containerPanel = Ext.create('Ext.panel.Panel', {
renderTo: Ext.getBody(),
width: 400,
height: 200,
title: 'Container Panel',
layout: 'column',
suspendLayout: true // Suspend automatic layouts while we do several different things that could trigger a layout on their own
});

// Add a couple of child items.  We could add these both at the same time by passing an array to add(),
// but lets pretend we needed to add them separately for some reason.

containerPanel.add({
xtype: 'panel',
title: 'Child Panel 1',
height: 100,
columnWidth: 0.5
});

containerPanel.add({
xtype: 'panel',
title: 'Child Panel 2',
height: 100,
columnWidth: 0.5
});

// Turn the suspendLayout flag off.
containerPanel.suspendLayout = false;
// Trigger a layout.
containerPanel.updateLayout();


Border layout

border layout将一个容器空间划分成五个区域,north, south, west, eash and center. 我们可以将我们的子组件放置在任意的区域。但通常,我们使用center区域

Ext.onReady(function(){
Ext.create('Ext.panel.Panel', {
width: 500, height: 300,
title: 'Border Layout',
layout: 'border',
items: [{
xtype: 'panel',
title: 'South Region is resizable',
region: 'south', // region
height: 100,
split: true // enable resizing
},{
xtype: 'panel',
title: 'West Region',
region:'west', // region
width: 200,
collapsible: true, //make panel/region collapsible
layout: 'fit',
split: true // enable resizing
},{
title: 'Center Region',
region: 'center',
layout: 'fit',
margin: '5 5 0 0',
html:'<b>Main content</b> goes here'
}],
renderTo: Ext.getBody()
});
});


我们在West区域,创建了一个可collapsible(收缩) panel. 当我们点击收缩按纽时,我们将看到面板将会收缩到左边。同样,我们定义South为split, 这允许我们能过拖动分隔条来重置 South 面板的大小.

The Fit layout

这种布局适用于只有一个子组件的容器。它许我们将容器内部的组件,占据整个容器的大小。当容器的大小发生改变,子组件的大小也会发生可变,以适合(fit)新的大小。

Ext.onReady(function(){
var win = Ext.create('Ext.window.Window', {
title: "My first window",
width: 300,
height: 200,
maximizable: true,
layout: "fit",
defaults: {
xtype: "panel",
height: 60,
border: false
},
items: [
{title: "Menu", html: "The main menu"},
{title: "Content", html: "The main content!"}
]
});
win.show();
})


如果不使用fit, 则在改变容器大小时,会出现如下情况



The Card layout

卡片布局可以用来管理多个子组件,所以如果我们需要创建一个向导(下一步下一步)或者一次只显示一个组件,我们应该使用这种布局。 这个布局继承于fit layout. 意味着任何时候任何时候只能显示一个组件,并且填充整个容器的空间。

我们设置items数据中显示组件的索引。移到下一个组件,我们只需用next, or prev方法。

Ext.onReady(function(){
var win = Ext.create("Ext.window.Window",{
title: "My first window",
width: 300,
height: 200,
maximizable: true,
layout: "card",//Step 1
defaults:{ xtype: "panel", height: 60, border: false },
items: [{
title: "Menu",
html: "The main menu"
},{
title: "Content",
html: "The main content!"
}]
});
win.show();
setTimeout(function(){
win.getLayout().setActiveItem(1); //Step 2
},3000);
})


在上面的第二步,我们能过getLayout获得layout的实例,并通过setActiveItem改变最初始元素, 显示第二个组件。我们也可以从layout实例中调用prev或者next方法,来显示上一张和下一张卡片.

The Accordion layout

跟Card layout, 它也是一次只能显示一个子组件。我们可以看到每一上内部组件的header部分,点击每个子组件的标题栏时,会后向下打开或者向上收起这个组件。

Ext.onReady(function(){
var win = Ext.create("Ext.window.Window",{
title: "My first window",
width: 300,
height: 200,
maximizable: true,
layout: "accordion",
defaults: { xtype: "panel" },
items:[
{title: "Menu", html: "The main menu" },
{title: "Content", html: "The main content!" },
{title: "3rd Panel", html: "Content here...!" }
]
});
win.show();
})


The Anchor layout

这个layout允许容器内的子组件,相对于容器的尺寸进行固定(Anchor). 如果父容器被重置大小,所有的子元素会依赖的规则进行大小的改变

默认的, AnchorLayout会基于容器自身大小,计算锚的尺寸。但如果一个container使用了AnchorLayout属性, 它将会使用AnchorLayout配置对像中anchorSize来设置, 如果指定了anchorSize属性,layout将使用一个虚拟的container来计算anchor的大小,而不是这个容器本身的大小

Ext.onReady(function(){
var win = Ext.create("Ext.window.Window",{
title: "My first window",
width: 300,
height: 300,
maximizable : true,
layout: "anchor",
defaults: {xtype: "panel", height: 60, border: false},
items: [
{
title: "Menu",
html: "panel at 100% - 10 px",
anchor:'-10'
},
{
title: "Content",
html: "panel at 70% of anchor",
anchor:'70%'
},
{
title: "3rd Panel",
html: "panel at 50% width and 40% heightof anchor",
anchor:'50% 40%',
bodyStyle:'background-color:#fc3;'
}]
});
win.show();
});




当我们使用anchor的属性只有一个值时, 它表示子组件的宽度,比如 anchor: “70%” 表示子组件的宽度为父容器的70%. anchor: ‘-10’ 表示父容器100% 减去10 px的宽度。 当有两个值是,第一个表示的是width, 第二个为height.

More layouts

更多的布局,比如HBox Layout, VBox Layout, Table Layout等等,你可以能过http://examples.sencha.com/extjs/6.2.0-ea/examples/kitchensink/#layouts.

Component Layout

跟容器的layout用来管理子组件元素的大小和位置,一个Component也可以有它的Layout 用来管理内部元素的大小和位置. Component的布局是通过componentLayout进行配置。

通常,你不需要使用这个配置,因为Ext JS所提供的组件都有它们自己的layout管理器,除非你自己写了一个自定义的组件。大部分组件使用的是Auto Layout. 但有的复杂组件需要自定义的组件布局,比如Panel组件(layout header, footer, toolbars)

Comments about using layouts

你可以通过使用组合容器和布局进行嵌套布局(多种不同的布局类型). 即你可以能过嵌套,组合玩转布局系统, 对于一个Ext JS新手来说,有一个很易犯的错误就是overnesting. 这有时会影响性能, 你需要提前进行规划,使用合适的容器和布局。

Ext.onReady(function(){
Ext.create('Ext.panel.Panel', {
width: 500, height: 300,
title: 'Border Layout',
layout: 'border',
items: [
{// Incorrect Nesting
xtype: 'panel',
title: 'West Region',
region:'west',
width: 200,
collapsible: true,
layout: 'fit'
items:[{
xtype: 'form',
url: 'myForm.php'
items[
// Fields here
]
}]
},{
title: 'Center Region',
region: 'center',
layout: 'fit',
margin: '5 5 0 0',
html:'<b>Main content</b> goes here'
}],
renderTo: Ext.getBody()
});
});


跟你看到的一样,在West 区域,我们设置了一个panel, 它包含一个Ext.form.Panel. 在这里,我们就有多余的嵌套(overnesting), 因为Ext.form.Panel是Panel组件的一个子类。overnesting 只会让我们的浏览器产生过多的 DOM节点。同时创建了两个组件,而不是一个,占用过多的内存。以下是纠正后的代码

{
xtype: 'form',
title: 'West Region',
region:'west',
width: 200,
collapsible: true,
url: 'myForm.php'
items[
// Fields here
]
}


组件与XTemplate

当在一个组件或者容器中使用XTemplate, 它的用法如下

Ext.create('Ext.container.Container', {
renderTo: Ext.getBody(),
data: ["aaabbbddd"],
renderTpl: ['<div>renderTpl</div>'],
html: "hello",
tpl: Ext.create('Ext.XTemplate',
'<i>{0}</i>',
{
compiled: true
})
});


上面的结果是输出renderTpl, 我们需要了解以下重要的知识

renderTpl用来描述整个组件的结构的模板,在Ext.container.Container的源文件中,默认为
{%this.renderContainer(out,values)%}
, 而模板方法renderContainer定义在Ext.layout.container.Container. 它会读取tpl属性中的内容

tpl属性用来显示组件的主要内容,如panel组件的body部分

在有tpl和data的时,忽略html.

对于Ext.button.Button来说,设置tpl和data无效,因为它的源文件里的renderTpl没有调用tpl的内容.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息