在上一次課程中,Component的成員還有四個沒有涉及到。
- beginUpdate方法:開始更新控件的屬性,有點類似與線程鎖一樣,爲了保證控件屬性值的一致性
- isUpdating只讀屬性:判斷控件是否在更新中
- endUpdate方法:完成更新控件的屬性,類似與釋放線程鎖。
- updated方法:提交組件的更新信息。
這裏涉及到控件的Update狀態。表示組件正在更新狀態, 處於更新狀態時組件的數據可能處於不一致的狀態, 處於更新狀態的組件,應該儘量避免與外界交互(尤其是與DOM元素有關的交互)。 有時候,合理利用Update狀態也能夠在一定程度上提高性能
如何使用Update狀態
批量修改組件屬性(在非Update狀態下)
- 調用組件的beginUpdate方法
- 設置組件屬性
- 調用組件的endUpdate方法
在調用endUpdate方法後,組件會自動調用Updated方法。
下面是一個timer的例子,具體的註釋已經被添加。
Type.registerNamespace("Demo");
//構造函數
Demo.Timer = function()
{
//調用基類的構造函數
Demo.Timer.initializeBase(this);
this._interval = 1000;
this._enable = false;
this._timer = null;
}
Demo.Timer.prototype =
{
//組件的tick事件
add_tick : function(handler)
{
this.get_events().addHandler("tick", handler);
},
remove_tick : function(handler)
{
this.get_events().removeHandler("tick", handler);
},
_timerCallback : function()
{
//得到組件tick事件的事件處理器
var handler = this.get_events().getHandler("tick");
if(handler)
{
//調用tick的事件處理器,並給空參數
handler(this, Sys.EventArgs.Empty);
}
},
_startTimer : function()
{
this._timer = window.setInterval(
//創建一個timerCallback的委託, 通過這個委託調用組件的tick事件處理器
Function.createDelegate(this, this._timerCallback),
this._interval
);
},
_stopTimer : function()
{
window.clearInterval(this._timer);
this._timer = null;
},
get_interval : function()
{
return this._interval;
},
//這個屬性的改變,會導致DOM元素在頁面的表現形式
set_interval : function(value)
{
if(this._interval != value)
{
this._interval = value;
this.raisePropertyChanged("interval");
//判斷組件當前是否在Update狀態中,如果在就不做操作
if(!this.get_isUpdating() && this._timer != null)
{
this._stopTimer();
this._startTimer();
}
}
},
//當endUpdate()函數被調用後,此方法被自動調用
updated : function()
{
Demo.Timer.callBaseMethod(this, "updated");
this._stopTimer();
if(this._enabled)
{
this._startTimer();
}
},
get_enabled : function()
{
return this._enabled;
},
set_enabled : function(value)
{
if(value != this._enabled)
{
this._enabled = value;
this.raisePropertyChanged("enabled");
if(!this.get_isUpdating())
{
if(value)
{
this._startTimer();
}
else
{
this._stopTimer();
}
}
}
},
dispose : function()
{
this.set_enabled(false);
this._stopTimer();
Demo.Timer.callBaseMethod(this, "dispose");
}
}
Demo.Timer.registerClass("Demo.Timer", Sys.Component);
調用方面主要提供修改組件屬性時的樣例
{
var timer = $find("timer");
timer.beginUpdate();
timer.set_interval(parseInt($get("txtInterval").value, 10));
timer.set_enabled($get("chkEnabled").checked);
timer.endUpdate();
}
可以看到,我們在修改組件的屬性時,首先beginUpdate,然後修改組件的屬性,最後調用組件的endUpdate,在調用endUpdate之後,會自動調用組件的updated方法
Control模型(可視化組件模型)
- 封裝一個DOM元素
- 提供統一的開發模型
- 可以用於開發複雜組件
- 構造函數接受一個element參數,表示這個組件封裝的DOM元素
element只讀屬性 | Control類封裝的DOM元素 |
visibilityMode屬性 | 顯示模式屬性,隱藏,相當於操作style.visibilityhide: 0,這相當與在頁面上面隱藏,但是佔位,收縮,相當於操作style.displaycollapse: 1,在頁面上隱藏,但是不佔位 |
visible | 是否可視 |
addCssClass/removeCssClass/toggleCssClass | 添加/移除/翻轉Css樣式表 |
parent | 當前組件的容器元素 |
onBubbleEvent | 處理由raiseBubbleEvent激發的事件。如果這個方法返回true,則該事件將不會被上傳到其父元素中進行處理。注意,如果你不處理該事件的話,你應該把此事件交由父級來作默認處理。 |
raiseBubbleEvent | 引發一個事件並交由父控件處理。總的來看,當你創建複雜的控件(經常是包含一個或多個子控件並且想把一個事件從子控件上交由其父控件來處理時)時往往要使用onBubbleEvent與這個方法。 |
下面來看一個簡單的例子
首先是組件的組件的js文件
Type.registerNamespace("Demo");
//構造函數
Demo.TextBox = function(element)
{
//調用基類的構造函數,並且把一個DOM元素作爲參數向上傳遞
Demo.TextBox.initializeBase(this, [element]);
//保存TextBox修改前的文本
this._originalText = "";
}
Demo.TextBox.prototype =
{
initialize : function()
{
Demo.TextBox.callBaseMethod(this, "initialize");
//在組件初始化時將文本框的change事件綁定一個事件處理器_onTextChange
//並且把當前組件作爲this上下文傳遞到_onTextChange函數中
$addHandler(this.get_element(), "change",
Function.createDelegate(this, this._onTextChange));
},
_onTextChange : function(e)
{
//爲了避免在將當前屬性值(文本)修改回去造成遞歸調用,
//判斷組件當前的更新狀態.
if(this.get_isUpdating())
{
return;
}
//得到組件的textChange事件的處理器
var handler = this.get_events().getHandler("textChange");
if(handler)
{
var args = new Sys.CancelEventArgs();
//調用事件處理器,並把當前組件作爲sender和CancelEventArgs類型傳遞到處理函數中
handler(this, args);
//得到由外部處理的args參數,並依據此來判斷
if(args.get_cancel())
{
//更新當前組件的屬性,間接的更新textbox
this.beginUpdate();
this.set_text(this._originalText);
this.endUpdate();
}
}
this._originalText = this.get_element().value;
},
//給組件添加一個textChange事件處理器
add_textChange : function(handler)
{
this.get_events().addHandler("textChange", handler);
},
remove_textChange : function(handler)
{
this.get_events().removeHandler("textChange", handler);
},
get_text : function()
{
return this.get_element().value;
},
set_text : function(value)
{
this.get_element().value = value;
}
}
Demo.TextBox.registerClass("Demo.TextBox", Sys.UI.Control);
再來看如何調用
Sys.Application.add_init(function()
{
//創建當前TextBox組件,並給textChange事件添加onTextChange事件處理器
//並且把DOM元素textBox作爲參數傳遞給構造函數
$create(Demo.TextBox, null, {"textChange" : onTextChange}, null,
$get("textBox"));
});
function onTextChange(sender, args)
{
var message = String.format("The text would be changed to '{0}', are you sure?", sender.get_text());
//根據用戶確定的消息來設置args的cancel屬性
if(!confirm(message))
{
args.set_cancel(true);
}
}
</script>