Ext学习笔记03 - 事件
Ext中的事件机制
Ext中的事件机制是在 Ext.util.Observable 中定义的,举一个例子来说明事件机制,先看一下代码,然后慢慢说
person.js
- Ext.namespace("com.meizhi");
- /* 定义NameSpace的别名 */
- Mz = com.meizhi;
- Mz.Person = function() {
- /* 定义事件 */
- this.addEvents(
- "nameChange",
- "sexChange"
- );
- };
- Ext.extend(Mz.Person, Ext.util.Observable, {
- name:"",
- sex:"",
- setName:function(_name){
- if(this.name != _name) {
- /* 发布事件 */
- this.fireEvent("nameChange", this, this.name, _name);
- this.name = _name;
- }
- },
- setSex:function(_sex){
- if(this.sex != _sex){
- /* 发布事件 */
- this.fireEvent("sexChange", this, this.sex, _sex);
- this.sex = _sex;
- }
- }
- });
看JS文件中的定义
- 先定义了一个Person类,在类中只有一个属性addEvents,属性值是一个字符串数组,在这里是定义了两个 Event 事件的名字。
- 声明 Person 类继承自 Ext.util.Observable 类,并且定义了Person类的另外一些属性:name,sex,以及它们的写方法(这里把setName和setSex称为属性更合适一些)。
- 在属性的写方法 setName ,setSex 中,如果传入的值和实例化的 Person 对象的属性值不一致就会调用相应的事件,并且给属性赋值。
- 这样 Person 类的属性定义就完成了,并且现在 Person 是具有Ext事件机制的类,以后在 Person 类的实例中绑定和调用事件就非常方便了。
Ext.util.Observable 维护了一个events 对象的数组,并提供了更加方便的对于事件的封装和调用机制。(参考:http://www.cnblogs.com/meetrice/archive/2008/05/23/1206108.html )
addEvents():绑定事件,看一下它的源代码
- addEvents : function(o){
- if(!this.events){
- this.events = {};
- }
- if(typeof o == 'string'){
- for(var i = 0, a = arguments, v; v = a[i]; i++){
- if(!this.events[a[i]]){
- // 将传入的事件名称注册到事件列表中
- this.events[a[i]] = true;
- }
- }
- }else{
- Ext.applyIf(this.events, o);
- }
- }
该方法实际上就是在 Person 对象上绑定了两个没有任何实现的事件名 ,这样 Person 对象就具有了两个空的Event对象(绑定可以执行操作的Event对象使用 addlistener 方法)。
fireEvent():发布事件,也就是触发绑定的事件。源代码中的定义
- fireEvent : function(){
- if(this.eventsSuspended !== true){
- //通过addEvents()注册的事件会被封装成events对象
- var ce = this.events[arguments[0].toLowerCase()];
- if(typeof ce == "object"){
- //触发事件对象
- return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
- }
- }
- return true;
- }
现在Person类的结构就很清楚了,
- name属性
- sex属性
- setName属性(如果传入参数和name属性不一致,调用 “nameChange” 事件)
- setSex属性(如果传入参数和name属性不一致,调用 “sexChange” 事件)
在person.js中完成是事件的定义和发布,那事件是在什么时候被订阅的呢? 事件触发之后又要进行哪些操作呢?
person.html
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>Event</title>
- <link type="text/css" rel="stylesheet" href="http://www.cnblogs.com/ext/resources/css/ext-all.css">
- <script type="text/javascript" src="http://www.cnblogs.com/ext/adapter/ext/ext-base.js"></script>
- <script type="text/javascript" src="http://www.cnblogs.com/ext/ext-all.js"></script>
- <script type="text/javascript" src="person.js"></script>
- <script type="text/javascript">
- var _person = null;
- button_click = function() {
- _person.setName(prompt("请输入姓名", ""));
- _person.setSex(prompt("请输入性别", ""));
- }
- Ext.onReady(function(){
- var txt_name = Ext.get("txt_name");
- var txt_sex = Ext.get("txt_sex");
- /* 构建Person类 */
- _person = new Mz.Person();
- /* 订阅事件 */
- _person.on("nameChange",
- function(_person, _old, _new){
- txt_name.dom.value = _new;
- });
- /* 订阅事件 */
- _person.on("sexChange",
- function(_person, _old, _new){
- txt_sex.dom.value = _new;
- });
- /* 订阅事件 */
- _person.on("nameChange",
- function(_person, _old, _new){
- document.title = _new;
- });
- });
- </script>
- </head>
- <body>
- 姓名:<input type="text" id="txt_name" maxlength="10" /><br/>
- 性别:<input type="text" id="txt_sex" maxlength="10" /><br/>
- <input type="button" value="输入" onclick="button_click()"/>
- </body>
- </html>
HTML文件页面中定义了两个输入框,和一个按钮,通过Ext.onReady(),页面初始化后首先执行里面的代码
- Ext.get()取得文本框中的值
- 构造 Person 类实例
- 订阅事件,这个时候定义 Person 中的设值属性的操作具体执行的内容,给setName()和setSex()订阅的事件中添加内容,这一点非常灵活,我们一开始只要先定义一个事件,而这个事件只有一个名字,没有具体实现,我们在订阅这个事件的时候才告诉它如果事件发生我们需要进行什么样的操作。
在点击按钮的时候,调用 button_click 方法来给 person 实例属性赋值。
在订阅事件的时候,用到的是 Observable.on 方法,实际上它是 Observable.addListener 的缩写(Ext里面的缩写好多啊...),源代码中是这样定义的
- Ext.util.Observable.prototype.on = Ext.util.Observable.prototype.addListener;
绑定事件真正起作用的方法就是addListener()方法,源码中的定义
- addListener : function(fn, scope, options){
- scope = scope || this.obj;
- if(!this.isListening(fn, scope)){
- var l = this.createListener(fn, scope, options);
- if(!this.firing){
- this.listeners.push(l);
- }else{ // if we are currently firing this event, don't disturb the listener loop
- this.listeners = this.listeners.slice(0);
- this.listeners.push(l);
- }
- }
- }
最后看一下效果:
- 页面中有两个input框和一个button
- 点击“输入”button,然后弹出来一个对话框,要求输入姓名
- 点击确定,输入框的内容会自动赋值到 id 为 txt_name 的 input 框中,HTML文档的标题也会变成刚才输入的姓名的值,同时会弹出第二个对话框要求输入性别
- 输入性别确认,值被带到 id 为 txt_sex 的 input 框中
顺便说一下例子中涉及到的其他的方法:
- Ext.onReady():用法相当于 window.onload ,是在页面装载完成时调用的方法,Ext.onReady是一个简写,定义在Ext.core.EventManager中,源代码:
- onDocumentReady : function(fn, scope, options){
- if(!docReadyEvent){
- initDocReady();
- }
- if(docReadyState || Ext.isReady){ // if it already fired
- options || (options = {});
- fn.defer(options.delay||0, scope);
- }else{
- docReadyEvent.addListener(fn, scope, options);
- }
- }
- Ext.get()方法,它用来取得页面中的一个DOM对象。(参考:http://www.vifir.com/bbs/html/20080706/1703993.html )
- txt_name.dom.value, 用来给DOM对象赋值,相当于document.getElementById("txt_name").value
在这个例子中,我们订阅了两次 nameChange 事件,这也是Ext中很重要的一个机制 -- 事件列表 ,在整个应用当中,公布一个事件,这个事件可以被多次订阅,这一点是很常用的,实现了一个完善的应用体系。
通过上面的例子也可以看出Ext的一些缺点:完成这样一个 事件定义、发布、过程所用到的代码比较复杂,不够简洁,要是更加直观一些就更好了,可以让人更加容易理解一些。