Ext JS 6组件,容器, 布局

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/cexo425/article/details/52222471

组件

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管理器中注册我们新创建的组件,以及其它的一些工作。以下是这个阶段的所有步聚

这里写图片描述

以下是每一步详细的解释

  1. 第一步,我们将配置属性应用到我们正在创建的实例上。在上面的代码中,title, width, height, renderTo属性,会被复制到panel实例中,以及任何我们其它定义的属性
  2. 第二步定义常见的事件,比如enable, disable, show等。每个组件都拥有这些事件
  3. 为这个实例分配一个唯一标识符。如果我在配置中有定义id(bad practice), 则使用配置中的ID
  4. 验证我们是否有在配置中指定plugins, 如果指定,则创建这些插件的实例。插件既为我们创建的组件的一个额外功能。在上面我们没有定义任何插件,所有这一步跳过
  5. 执行initComponent函数,它是一个模板方法,会在constructor中调用, 如果我们想在实例创建时,执行自定义的代码,应该在subclasses中重写这个方法, 当然Component在不同的阶段,都提供了template方法,能让我们添加额外的功能
  6. 在这个步聚,我们将新创建好的实例添加到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实例数组

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

  8. 如果我们有定义plugins, 在上面的步聚中我们创建了这些插件的实例,现在,调用每个插件的init()方法,并用传递当前组件给它们,进行初始化。
  9. 如果在配置中有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中, 样式和事件监听器将被应用,所以我们能够看到新的组件外观,并且与它交互(事件).

这里写图片描述

  1. 触发beforeRender事件,如果它的监听器返回false, 则停止渲染
  2. 确认组件是否floating组件,即在配置中指定floating为true. 常见的组件有menu, window. 如果是,分配z-index属性。
  3. 创建一个container属性,并且将一个DOM元素赋值给它,表示组件将在哪里被渲染, container属性是一个Ext.dom.Element实例
  4. 组件的模板方法onRender被执行,创建一个el属性,它表示组件的主节点元素。我们可以为组件定义一个html模板,然后会被创建,并且添加到主节点中。我们可以重写onRender方法,添加指定的节点到DOM中。
  5. 设置显示模式,既根据配置中的hideMode, 它的值可以为(display, visibility or offset)
  6. 如果有设置overClas属性,则监听鼠标移入和移出事件,用来添加和删除这个css类。
  7. 在第七步,触发render事件,组件实例将作为参数传递给事件处理器
  8. 第八步用来初始化内容。有以下三个方法来设置组件的内容

    • 可以在组件的属性中定义一个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>"]
    });
  9. 返回到render阶段,下一步执行afterRender模板方法. 如果一个组件包含了子组件,子组件将在这一步渲染。我们在container之后讨论.

  10. 在上一步,afterRender事件触发。我们可以在subclass中监听这个事件,在所有的节点都被渲染到DOM后,执行一此动作。
  11. 在上一步,注册鼠标,键盘,大小等监听器
  12. 最后一步,如果有设置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属性可以用来销毁。(默认情况下,已经设置过了),销毁阶段将在用户关闭窗口后被调用

这里写图片描述

  1. 销毁阶段在开始时,会先触发beforeDestroy事件,如果事件处理器返回false, 则停止销毁。如果继续,并且组件是floating类型的,则从floating manager中取消注册。
  2. 执行模板方法beforeDestroy,所有subclasses通过使用这个方法来删除它们的子元素或者清理内存
  3. 在第三步,如果将被销毁的组件是其它组件的子组件,那么在父组件中,这个组件的引用,将被删除
  4. onDestroy方法将被执行,这是一个模板方法,执行这个方法是为了销毁我们组件的属性,并且确保它的子组件(已经被添加到当前组件)也被销毁, 同时,也清理我们自己创建的自定义监听器
  5. 第五步,销毁所有的插件
  6. 如果组件已经被渲染了,则从DOM中删除所有的组件节点,和节点所对应的监听器
  7. 触发destroy事件,我们可以监听这个事件,执行相应的动作
  8. 在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使用多个组件作为容器使用,它们每一个有它自已的功能。以下是常用容器列表

Container Description
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的内容.

猜你喜欢

转载自blog.csdn.net/cexo425/article/details/52222471
今日推荐