HTML 和 ExtJS 组件(二)

栏目: Html · 发布时间: 6年前

内容简介:当你要创建一个网页时, 经常会发现,你知道怎样用原生的html来创建,却不知道如何使用浩如烟海的ext组件来实现。本文将介绍一些使用ext组件创建html的一些技巧。接着前面两节, 我们来开始第三节事件 Events

当你要创建一个网页时, 经常会发现,你知道怎样用原生的html来创建,却不知道如何使用浩如烟海的ext组件来实现。本文将介绍一些使用ext组件创建html的一些技巧。

接着前面两节, 我们来开始第三节

事件 Events

让我们给组件添加一个click事件。下面这个例子是添加事件的基本方式,很容易理解:

var clickCmp = new Ext.Component({ 
    ... 
    count: 0, 
    html: 'Click Me', 
    listeners: { 
        // 给组件的主元素el添加一个事件监听
        el: { 
            click: function() { 
                clickCmp.count++; 
                clickCmp.update('Click count: ' + clickCmp.count); 
            } 
        } 
    } 
});

例子中关键的一点是:listener被注册到了Ext组件对象的el上而不是组件对象本身。组件没有绑定click监听事件所以上面的例子还不能正常工作。例子中有一个关键问题是,这个click事件只是映射到了Ext组件对象上,但在这个闭包证并没有真正被使用。

注册监听事件的规范方式是重写initEvents来注册事件。下面这个例子中我们自定义了一个Ext组件,这个组件支持用handler属性注册click事件(类似button的handler--译者注):

Ext.define('ClickComponent', { 
    extend: 'Ext.Component', 
    initEvents: function() { 
        this.callParent(); 
        // Listen for click events on the component's el 
        this.el.on('click', this.onClick, this); 
    }, 
    onClick: function() { 
        // Fire a click event on the component 
        this.fireEvent('click', this); 
        // Call the handler function if it exists 
        Ext.callback(this.handler, this); 
    }
});

我们可以像下面的方式来使用事件:

new ClickComponent({ 
    ... 
    count: 0, 
    html: 'Click Me', 
    handler: function() { 
        this.count++; 
        this.update('Click count: ' + this.count); 
    } 
});

我们在自定义组件的onClick方法中还触发了click事件,这是Ext组件的click事件反作用到了el上, 我们在例子中没有用到;理解这两种事件的区别很重要。

事件委派 Event Delegation

使用事件委派和一些css, 我们就很容易为一个列表添加一些交互动作。下面这个例子有一些Ext组件的高级应用,我们要一点点消化:

new Ext.Component({ 
    ... 
    autoEl: 'ul', 
    data: ['London', 'Paris', 'Moscow', 'New York', 'Tokyo'], 
    listeners: { 
        // Add the listener to the component's main el 
        el: { 
            // Use a CSS class to filter the propagated clicks 
            delegate: '.list-row', 
            click: function(ev, li) { 
                // Toggle a CSS class on the li when it is clicked 
                Ext.fly(li).toggleCls('list-row-selected'); 
            } 
        } 
    }, 
    tpl: [ 
        '<tpl for=".">', 
            '<li class="list-row">{.}</li>', 
        '</tpl>' 
    ] 
});

Html代码

.list-row { 
    border: 1px solid #f90; 
    border-radius: 5px; 
    cursor: pointer; 
    list-style: none outside; 
    padding: 5px; 
} 
 
.list-row-selected { 
    background-color: #f90; 
}

显示效果如下:

HTML 和 ExtJS 组件(二)

生成的html如下:

<ul id="..." ...> 
    <li class="list-row">London</li> 
    <li class="list-row">Paris</li> 
    <li class="list-row">Moscow</li> 
    <li class="list-row">New York</li> 
    <li class="list-row">Tokyo</li> 
</ul>

当点击

  • 时,样式list-row-selected就会添加到点击的
  • 上。这就类似于在Ext组件的el上(在本例中即
      )添加了一个click事件,但是我们不想监听
        的事件,于是我们使用事件冒泡的特性将click事件作用到
      • 上,为了实现这个目的,在Ext中我们使用delegate 配置项来实现。理论上我们可以用标签名字“li”作为delegate的选择器,但是css class名称是一种更好的实践。例子中的
      • 都有一个名为“list-row” 的css class,我们可以使用它。
  • 配置delegate: '.list-row' 有两方面作用:

    如果click事件不是发生在

  • 节点或
  • 的子孙节点,事件会被忽略。
  • 传递给监听函数的参数将会是

  • 节点,哪怕是
  • 还包含子节点,传递给监听函数的参数节点也是由
  • 环绕。
  • 和前面提到的例子一样,自定义组件的delegate事件可以通过重写initEvents函数来实现。

    数据视图 DataView

    如果我们想更深入地演化前面的list的例子,我们可以使用DataView来实现。Ext.view.View类常被称作DataView,它可以自动绑定到store。有关DataView的更详细介绍不在本文讨论的范围, 下面的例子演示了如何通过DataView实现列表:

    new Ext.view.View({ 
        ... 
        autoEl: 'ul', 
        itemSelector: '.list-row', 
        overItemCls: 'list-row-over', 
        selectedItemCls: 'list-row-selected', 
        simpleSelect: true, 
        // An ExtJS store or store config 
        store: { 
            data: [{city: 'London'}, ...], 
            fields: ['city'] 
        }, 
        tpl: [ 
            '<tpl for=".">', 
                '<li class="list-row">{city}</li>', 
            '</tpl>' 
        ] 
    });

    HTML 和 ExtJS 组件(二)

    这里有一个非常重要的设置项即itemSelector, 它的功能非常类似于我们前面提到的delegate; 它将DOM元素与store的数据记录对应起来,这样可以通过点击列表元素来选择数据。

    renderTpl and renderData

    renderTpl & renderData两个配置项功能类似 tpl & data,唯一不同的是renderTpl & renderData只在渲染组件时执行一次。renderTpl 和 tpl两个模板都能创建组件的html元素,通常用renderTpl创建的html元素被认为是组件内部标记(用来定义组件的框架或结构--译者注)而不是组件内容标记(组件显示的动态内容----译者注)。比如前面我们了解了panel组件中tpl的使用,tpl渲染的panel的动态内容部分;panel组件的类定义其实还有个 renderTpl, renderTpl定义了panel组件的框架,包括header、toolbars、body等等。tpl的定义既可以在Ext组件类中定义,也可以在组件实例中定义,但 renderTpl只能在组件类中定义。

    baseCls属性

    baseCls是Ext组件样式的基础,它既是添加在Ext组件外部el上的样式名称(class)又是Ext组件主要html元素上样式名称的前导符。创建组件主要html元素和他们的css class的工作通常由 renderTpl来完成。

    有些Ext组件定义了他们特有的baseCls样式, 比如button组件 使用x-btn, panel组件使用x-panel, window组件使用x-window等。这些样式在Ext组件库中被广泛使用,它们很难在应用程序代码中被恰当使用。如果需要自定义组件的样式,你可以设置 baseCls来防止样式冲突。比如如果你想完全清除button组件的默认样式,你可以设置button的 baseCls。

    设置baseCls一定要谨慎再谨慎。Ext组件的正常运行有可能依赖某些样式属性的特定值,如果这些值被改变有可能会带来麻烦,特别是你想升级Ext版本的时候。如果你只期望改变组件的一小部分样式,应该考虑别的方式。

    childEls

    Ext组件对象存储了一些自身子html元素引用,这就意味着你不用遍历html节点就可以直接使用这些html对象。组件渲染完成后el属性存储了组件对象的外部html节点的引用, childEls用来配置需要存储引用的子节点。比如panel组件对象就有两个这样的属性:el和body; button对象有btnIconEl和btnInnerEl分别用来修改button的图标和显示文本。配置了引用的组件对象不能随意的修改,因为被存储了引用的html元素不能被删除。因此在childEls配置引用的html元素通常由renderTpl创建而不是tpl创建(ext5中在tpl中创建会报错--译者注)。

    如果组件的html元素需要存储引用,在renderTpl中定义html元素的时候通常会给这些html元素指定id, id的值通常是 组件id + html元素名称, 这样做是方便组件渲染完成后能够快速遍历来创建对象属性。

    下面的组件对象将会创建一个名为body的对象属性。

    Ext.define('TestChildEls', {
        extend: 'Ext.container.Container',
        childEls: [
            'body'
        ],
        renderTpl: [
            '<div id="{id}-body" data-ref="body" ...>{bodyCls}</tpl>',
    	     ...,
            '</div>'
        ]
    });

    Putting It All Together

    下面这个例子有点复杂。我们将继续上面提到的面板的例子,继续完善,将看到如下效果:

    我们将Ext组件实现类定义分两部分:首先定义一个简单直观的基类,包含header和body两个区域和适当的样式定义,现在这个组件中还没有具体的人物简介信息。

    // Note: This is correct for ExtJS 4.1 and 4.2. This page 
    //       uses 4.0 so requires a slightly modified version. 
    Ext.define('TitledComponent', { 
        extend: 'Ext.Component', 
        baseCls: 'titled-component', 
        childEls: ['body', 'headerEl'], 
        renderTpl: [ 
            '<h4 id="{id}-headerEl" class="{baseCls}-header">{header:htmlEncode}</h4>', 
            '<div id="{id}-body" class="{baseCls}-body">{% this.renderContent(out, values) %}</div>' 
        ], 
        getTargetEl: function() { 
            return this.body; 
        }, 
        // Override the default implementation to add in the header text 
        initRenderData: function() { 
            var data = this.callParent(); 
     
            // Add the header property to the renderData 
            data.header = this.header; 
     
            return data; 
        }, 
     
        setHeader: function(header) { 
            this.header = header; 
            // The headerEl will only exist after rendering 
            if (this.headerEl) { 
                this.headerEl.update(Ext.util.Format.htmlEncode(header)); 
            } 
        } 
    });

    然后再定义一个子类,在子类中定制一个人物简介的模板:

    Ext.define('BiographyComponent', { 
        extend: 'TitledComponent', 
        xtype: 'biography', 
        header: 'Biography', 
        tpl: '{name} is {age:plural("year")} old and lives in {location}', 
        // Override update to automatically set the date in the header 
        update: function(data) { 
            this.callParent(arguments); 
            this.setHeader('Biography updated at ' + ...); 
        } 
    });

    下面我们创建组件实例时,只需要传递数据:

    var summary = new BiographyComponent({ 
        data: { 
            age: 26, 
            location: 'Italy', 
            name: 'Mario' 
        } 
    });

    添加一个按钮来更新人物简介信息:

    new Ext.button.Button({ 
        ... 
        handler: function() { 
            // Update the body content via the tpl 
            summary.update({ 
                age: ..., 
                location: ..., 
                name: ... 
            }); 
        } 
    });

    生成的html非常简单:

    <div id="biography-1035" class="titled-component ..." ...> 
        <h4 id="biography-1035-headerEl" class="titled-component-header">Biography</h4> 
        <div id="biography-1035-body" class="titled-component-body">Mario is ...</div> 
    </div>

    虽然样式不太令人满意,但组件的确是显示出来了。现在我们将样式绑定到在TitledComponent中定义的baseCls上:

    CSS样式

    .titled-component { 
        background-color: #fec; 
        border: 2px solid #5a7; 
        border-radius: 5px; 
        box-shadow: 2px 2px 2px #bbb; 
        display: inline-block; 
    } 
     
    .titled-component-body { 
        margin: 10px; 
    } 
     
    .titled-component-header { 
        background-color: #5a7; 
        color: #fff; 
        font-weight: 700; 
        margin: 0; 
        padding: 5px 10px; 
        text-align: center; 
    }

    现在我们完成了自定义组件的大部分工作,现在我们快速回顾一下:

    • xtype:'biography' xtype会作为组件实例的id(“biography-1035”)的前导符,这个组件的Id同时也会作为组件外部元素el的id,我们在renderTpl中也用了两次。
    • baseCls: 'titled-component' 这个样式被加在了组件外部的el上,注意一下这个样式是如何作为组件内部html元素的样式的前导符使用的。
    • childEls: ['body', 'headerEl'] chidlEls配置的两个引用在组件渲染后被创建, 对应的dom元素通过id查找。body元素在 getTargetEl方法中使用,headerEl元素在方法setHeader中被使用。
    • renderTpl的数据由initRenderData方法提供。initRenderData的默认实现(在Ext.util.Renderable中--译者注)已经包含了id和baseCls,我们只需要将 header属性追加上去。
    • 在renderTpl中有个奇怪的标记“{% this.renderContent(out, values) %}”,这个标记内容应该显示在targetEl中用来注入组件内容。在 ExtJS 4.0中targetEl可能会在模板(template)中留下空白,组件内容部分会被插入到已经渲染到dom的targetEl后面。这样会有严重的性能问题,从ExtJS 4.1开始组件内容部分完全交给模板(template)渲染。如果你继承自容器(container)那么就将这部分内容替换为“{% this.renderContainer(out, values) %}”。
    • getTargetEl 返回body元素的引用, 这个引用由 childEls 配置生成。只有有了这个方法,组件的update方法才可以更新到正确的dom元素。注意这个update方法不能与setHeader方法中调用的update方法混淆,后者是headerEl元素的方法而不是我们定义的组件的方法。
    • setHeader方法更新header的内容,这种重写方式即可以在组件渲染前调用也可以在组件渲染后调用。

    也许你不需要写这种底层的Ext组件,了解Ext组件的机制将有助于你更好的阅读ext源码。TitledComponent类的定义使用了Ext组件定义的典型风格,而BiographyComponent可以看做是应用程序类的典型。

    作者:Skirtle's Den

    原文: http://skirtlesden.com/articles/html-and-extjs-components

    译者:lic0112

    译文: http://lic0112.iteye.com/blog/2154771


    以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

    查看所有标签

    猜你喜欢:

    本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

    Blockchain Basics

    Blockchain Basics

    Daniel Drescher / Apress / 2017-3-16 / USD 20.99

    In 25 concise steps, you will learn the basics of blockchain technology. No mathematical formulas, program code, or computer science jargon are used. No previous knowledge in computer science, mathema......一起来看看 《Blockchain Basics》 这本书的介绍吧!

    HTML 压缩/解压工具
    HTML 压缩/解压工具

    在线压缩/解压 HTML 代码

    JS 压缩/解压工具
    JS 压缩/解压工具

    在线压缩/解压 JS 代码

    正则表达式在线测试
    正则表达式在线测试

    正则表达式在线测试