Mục tiêu của bài viết này là cung cấp cho bạn các cơ sở để phát triển widget HTML bằng bộ công cụ Dojo JavaScript, bắt đầu từ phiên bản 1.0. Bài viết này cũng mô tả nhiều ví dụ, bắt đầu từ các widget đơn giản và tiến dần tới các widget phức tạp hơn, trong khi giải quyết các vấn đề phổ biến mà bạn có thể gặp trong khi phát triển widget.
Dojo là bộ công cụ mã nguồn mở, dựa trên JavaScript, dành cho việc phát triển các ứng dụng Web HTML động. Bộ công cụ này cho phép bạn có thể xây dựng nhanh các widget phức tạp hơn các widget HTML tiêu chuẩn. Việc sử dụng các thành phần mà Dojo cung cấp sẽ giúp giao diện người dùng Web của bạn thêm hữu dụng, phản hồi nhanh chóng và thiết thực. API mức thấp và các tầng tương thích mà Dojo cung cấp sẽ giúp bạn viết ra những ứng dụng tương thích với nhiều trình duyệt.
Trước khi bắt tay vào, đầu tiên bạn cần phải thiết lập môi trường phát triển. Để làm điều này cần:
- Tải phiên bản phát hành mới nhất của bộ công cụ Dojo (dojo-release-1.x.x-src.zip hay là dojo-release-1.x.x-src.tar.gz) từ trang chủ của dự án Dojo (xem phần Tài nguyên để tìm địa chỉ)
- Giải nén nội dung của tệp lưu trữ vào một thư mục, chú ý đến vị trí nơi tệp dojo.js được giải nén. Hệ thống gói sẽ quản lý việc nạp tất cả các mô đun sau khi tệp dojo.js được nạp lên trang web.
Khi thực hiện xong, cấu trúc của các thư mục sẽ như trình bày trong Hình 1.
Hình 1. Cấu trúc thư mục sau khi bung tệp dojo.
Dijit là hệ thống widget được đặt trên tầng đỉnh trên cùng của dojo. Thông qua chủ đề (theme) riêng của mình, gọi là tundra, hệ thống này cung cấp thiết kế và lược đồ màu sắc chung cho tất cả các widget của nó. Dojox là gói phát triển các phần mở rộng của bộ công cụ Dojo. Nó dành cho những người cần đến những chức năng không có trong bộ tập hợp các chức năng chung.
Khi duyệt các trang Web, bạn sẽ bắt gặp hàng trăm widget trên màn hình làm việc của mình. Mỗi nút bấm trên trình duyệt Web của bạn là một widget. Mỗi hộp nhập văn bản cũng là một widget. HTML chuẩn cung cấp một tập giới hạn các widget: hộp nhập văn bản, nút bấm và siêu liên kết.
Các widget Dojo lấy một mục, ví dụ như hộp nhập văn bản, và thêm các chức năng dành cho các đối tượng thân thiện với người dùng hơn, ví dụ như thêm lịch dạng đồ họa để chọn ngày tháng chẳng hạn. Việc này được thực hiện mà không hề làm hỏng mục gốc mà trên đó có xây dựng thêm chức năng mới.
Một widget Dojo gói gọn các thành phần Web trực quan để dễ dàng tái sử dụng. Nó được định nghĩa nhờ 3 tệp:
- Tệp JavaScript chứa logic của widget.
- Tệp HTML (là tùy chọn) cung cấp một khuôn mẫu giống như HTML cho widget.
- Tệp CSS, thường dùng chung cho tất cả các widget (là kiểu dáng chủ đạo - theme), trong đó chứa các kiểu dáng trực quan được áp dụng cho các khuôn mẫu HTML của widget.
Liệt kê 2 cho thấy một khung sườn HTML cơ sở dùng để nhập khẩu một widget vào trang Web thông thường.
Liệt kê 1. Mã HTML để nhập khẩu một widget vào trang Web.
<html>
<head>
<title>Dojo Toolkit Test Page</title>
<style type="text/css">
/* CSS style code */
</style>
<script type="text/javascript" src="js/dojo1.2/dojo/dojo.js"
djConfig="parseOnLoad:true, isDebug:true"></script>
<script type="text/javascript">
/* Javascript code */
</script>
</head>
<body>
/* Widgets definition code */
</body>
</html>
|
Thẻ
script đầu tiên khởi tạo bộ công cụ Dojo chỉ đơn giản bằng cách nạp tệp
mồi dojo.js. Các thuộc tính parseOnLoad và
isDebug của đối tượng djConfig là
hai tùy chọn cấu hình phổ biến mà Dojo kiểm tra trong lúc chạy. parseOnLoad bật/tắt việc phân tích cú pháp các
thẻ đánh dấu trong lúc nạp lên, trong khi isDebug cho phép hay không cho phép các thông báo lỗi. Đối
tượng djConfig cũng có thể được thiết lập giống
một biến toàn cục trước khi nạp tệp
dojo.js:
Liệt kê 2. Mã thiết lập các biến toàn cục với djConfig
<script type="text/javascript">
var djConfig = {
isDebug:true, parseOnLoad:true
};
</script>
<script type="text/javascript" src="js/dojo1.2/dojo/dojo.js"></script>
|
Dojo
có hệ thống gói để cấu trúc các lớp của ứng dụng thành các tệp và nạp
chúng thông qua hàm dojo.require. Hàm này cho
phép nạp từng phần của bộ công cụ Dojo, còn chưa được cho sẵn trong tệp cơ
sở dojo.js.
Để tạo một widget, bạn phải nhập khẩu khai báo widget bằng cách thêm vào các dòng như trong Liệt kê 3.
Liệt kê 3. Mã để nhập khẩu một khai báo widget.
<script type="text/javascript">
dojo.require("widgets.Button");
</script>
|
Bây giờ, bạn có thể chèn mã sau đây vào phần thân:
<body>
<div dojoType="widgets.Button">My Button</div>
</body>
|
Thuộc
tính dojoType làm cho bộ công cụ Dojo quản lý
các thẻ theo một cách đặc biệt. Vào thời điểm nạp trang, trình phân tích
cú pháp Dojo sẽ tìm kiếm khai báo widget được xác định rõ trong thuộc tính
dojoType , khởi tạo một cá thể widget này
và thay thế thẻ đó bằng một nút DOM chứa widget đã nhận được.
Bây giờ,
ta hãy xét một ví dụ widget TextBox, gồm một
tệp JavaScript, một tệp khuôn mẫu và một tệp kiểu dáng CSS.
Đầu tiên, bạn phải tạo một tệp JavaScript, TextBox.js, trong đó có định nghĩa và logic của widget (xem Liệt kê 4)
Liệt kê 4. Nội dung của tệp JavaScript TextBox.js
dojo.provide("widgets.TextBox");
dojo.require("dijit._Widget");
dojo.require("dijit._Templated");
dojo.declare(
"widgets.TextBox",
[dijit._Widget, dijit._Templated],
{
/** the template path */
templatePath: dojo.moduleUrl("widgets", "templates/TextBox.html"),
/** the input DOM node */
inputNode: null,
/** the label */
label: "",
/** onkeyup handler */
onKeyUp: function() {
// give a chance to the browser to update the DOM
setTimeout(dojo.hitch(this, this.validate), 0);
},
/** validate function */
validate: function() {
if ( this.inputNode.value === "Ok" ) {
// the text is correct
dojo.addClass(this.inputNode, "inputOk");
dojo.removeClass(this.inputNode, "inputError");
} else {
// the text is incorrect
dojo.removeClass(this.inputNode, "inputOk");
dojo.addClass(this.inputNode, "inputError");
}
}
}
);
|
dojo.provide() định nghĩa tên của widget mới và
đăng ký khai báo lớp. Lưu ý rằng:
-
dijit._Widgetvàdijit._Templatedlà các lớp cha mẹ bậc trên (superclasses) của widget TextBox. -
templatePath,inputNode, vàlabellà các thuộc tính của widget. -
onKeyUp()vàvalidate()là các hàm định nghĩa logic của widget.
Bây giờ, bạn có thể định nghĩa tệp khuôn mẫu TextBox.html như biểu diễn trong Liệt kê 5.
Liệt kê 5. Nội dung tệp TextBox.html
<span class="textBox">
${label}:
<input
type="text"
class="inputOk"
dojoAttachPoint="inputNode"
dojoAttachEvent="onkeyup: onKeyUp">
</input>
</span>
|
Cụm
${label} sẽ được thay thế bằng thuộc tính
label của cá thể widget đó.
Khai báo dojoAttachPoint sẽ khiến cho thuộc tính InputNode của widget
được đặt thành nút DOM tương ứng với thẻ input.
Khai báo dojoAttachEvent sẽ khiến cho các sự kiện onKeyUp (nhận được từ nút input) kích hoạt lời
gọi đến phương thức onKeyUp của
widget.
Lớp textBox và inputOk chỉ tới tên của các lớp CSS được định
nghĩa trong tệp TextBox.css. Xem Liệt kê
6.
Liệt kê 6. Tên các lớp CSS trong tệp TextBox.css
.ibm .textBox {
margin: 5px;
padding: 5px;
background-color: #eee;
}
.ibm .inputOk {
border: 1px solid green;
}
.ibm .inputError {
border: 1px solid red;
}
|
Vì tên các lớp phải là duy nhất trong toàn bộ dự án, nên thường chúng sẽ có một định tố chọn CSS (ví dụ định tố chọn ở đây là ibm).
Cuối cùng, widget có thể được tạo ra trên một trang HTML, như chỉ ra trong liệt kê 7.
Liệt kê 7. Mã lệnh tạo widget trên trang HTML
<html>
<head>
<!-- page title -->
<title>TextBox Widget</title>
<!-- include the DOJO -->
<script type="text/javascript" src="../dojo-1.0.0/dojo/dojo.js"
djConfig="isDebug: true, parseOnLoad: true">
</script>
<!-- import DOJO base CSS, DIJIT theme, and widget CSS -->
<style type="text/css">
@import "../dojo-1.0.0/dojo/resources/dojo.css";
@import "../dojo-1.0.0/dijit/themes/tundra/tundra.css";
@import "templates/TextBox.css";
</style>
<!-- import DOJO stuff -->
<script type="text/javascript">
dojo.require("dojo.parser");
<!-- register our module path -->
dojo.registerModulePath("widgets", "../../widget");
<!-- import our stuff -->
dojo.require("widgets.TextBox");
</script>
</head>
<body class="tundra ibm" style="padding:5px">
<br/>
<!-- declare the DOJO widget -->
<div
dojoType="widgets.TextBox"
label="Name">
</div>
</body>
</html>
|
Hình
2 hiển thị widget TextBox.
Hình 2. Widget TextBox
Kiểu khai báo đối lập với kiểu chương trình
Dojo hỗ trợ hai mô hình lập trình, khai báo và chương trình. Cả hai mô hình đều có thể sử dụng trên cùng một trang nếu cần. Trong Liệt kê 7, widget được tạo ra theo mô hình khai báo.
<div dojoType="widgets.TextBox" label="Name"></div> |
Một widget tương tự cũng có thể được tạo ra theo mô hình chương trình, như trong Liệt kê 8.
Liệt kê 8. Tạo widget TextBox theo mô hình chương trình
<html>
<head>
<!-- page title -->
<title>TextBox Widget</title>
<!-- include the DOJO -->
<script type="text/javascript" src="../dojo-1.0.0/dojo/dojo.js"
djConfig="isDebug: true, parseOnLoad: true">
</script>
<!-- import DOJO base CSS, DIJIT theme, and widget CSS -->
<style type="text/css">
@import "../dojo-1.0.0/dojo/resources/dojo.css";
@import "../dojo-1.0.0/dijit/themes/tundra/tundra.css";
@import "templates/TextBox.css";
</style>
<!-- import DOJO stuff -->
<script type="text/javascript">
dojo.require("dojo.parser");
<!-- register our module path -->
dojo.registerModulePath("widgets", "../../widget");
<!-- import our stuff -->
dojo.require("widgets.TextBox");
</script>
<script type="text/javascript">
function createWidget()
{
var widget = new widgets.TextBox();
widget.label = "Name";
widget.startup();
}
</script>
</head>
<body class="tundra ibm" style="padding:5px">
<br/>
<!-- declare the DOJO widget -->
<script type="text/javascript">
createWidget();
</script>
</body>
</html>
|
Bổ sung thêm về các widget Dojo
Như đã đề cập từ đầu, một widget Dojo
là một khai báo lớp Dojo được thừa kế từ một lớp đặc biệt: lớp dijit._Widget. Lớp này định nghĩa các hành vi cơ
sở của một widget và cung cấp các chức năng thông dụng, được chia sẻ chung
cho tất cả các triển khai widget. Các nhiệm vụ quan trọng nhất do lớp
_Widget triển khai là:
- Định nghĩa phương thức
create, phương thức này sẽ được tự động gọi khi khởi tạo một cá thể widget; phương thức này sẽ thi hành tất cả những bước tạo lập cần thiết. - Định nghĩa một số phương thức khuôn mẫu (
template), chúng cung cấp các điểm móc nối (hook) đặc biệt dành cho các nhà triển khai widget, mang lại cho họ khả năng triển khai các hành động khởi tạo đặc biệt trong một pha kiến tạo cụ thể. - Định nghĩa một loạt các phương thức
destroyđể dọn sạch các tài nguyên widget đã được cấp phát. - Định nghĩa phương thức
event management, chúng sẽ nối buộc các phương thức widget với các sự kiện nút DOM/sự kiện gọi phương thức.
Phương thức create thực hiện
các bước cơ bản sau đây để khởi tạo widget:
Sinh ra ID duy nhất cho widget nếu như không được cung cấp
Tất cả các widget Dojo đều phải được xác định nhờ một ID duy nhất. Khi một đối tượng widget được khởi tạo ra, nó sẽ được cấp một ID widget. Nếu không có thì Dojo sẽ sinh một ID theo dạng sau:
packageName_className_number
Ví dụ, một nút nhấn có thể có ID tự động sinh ra là: dijit_form_Button_0.
packageName là gói widget mà dấu chấm (.) đã thay bằng dấu gạch dưới (_) (ví dụ, dijit_form), và số hiệu sẽ tăng dần theo từng widget.
Đăng ký widget tại sổ đăng ký widget toàn cục
Người ta có thể sử dụng phương thức dijit.byId để tìm kiếm các widget, phương thức này sẽ trả lại
đối tượng widget tương ứng với ID đã cho. Sổ đăng ký là một đối tượng nằm
trong không gian toàn cục ở dijit.registry_hash. Sử dụng các công cụ gỡ
lỗi, ví dụ như FireBug (xem Tài nguyên), thâm
nhập sổ đăng ký này có thể hữu dụng trong việc dò tìm, ví dụ, các widget
có lỗ hổng.
Ánh xạ các thuộc tính
Các thuộc tính widget có thể được tái ánh xạ đến các nút do người dùng
định nghĩa trong khuôn mẫu widget. Các thuộc tính để ánh xạ và đích tới
của chúng được liệt kê trong đối tượng attributeMap. Mỗi thuộc tính của những đối tượng này định
nghĩa một ánh xạ theo cách sau:
- Tên thuộc tính sẽ xác định tên thuộc tính widget, trong đó lưu giá trị để thiết đặt.
- Giá trị thuộc tính sẽ xác định tên thuộc tính widget lưu trữ nút DOM
đích. Nếu thấy xâu rỗng thì sử dụng
domNode.
Liệt kê 9 cho ta một ví dụ.
Liệt kê 9. Khai báo một widget dojo
dojo.declare(
"MyWidget",
dijit._Widget,
{
// the attribute to map (name and value)
title: "myTitle",
// the DOM node to be used to set the
// title attribute to the "myTitle" value
titleNode: null,
// the attribute map
// the title attribute is mapped to the titleNode
attributeMap: {title: "titleNode"},
// the template string
templateString: "<div><span dojoAttachPoint=\"titleNode\"></span></div>",
|
Kết quả khuôn mẫu widget sẽ là:
<div><span title="myTitle"></span></div> |
Lưu
ý rằng vì lớp base _Widget đã ánh xạ một vài
thuộc tính HTML cơ bản (như ID, lớp, kiểu), người triển khai thực hiện
không nên ghi đè lên thuộc tính attributeMap.
Thay vào đó, họ phải trộn lẫn thuộc tính attributeMap của lớp cơ sở với các giá trị của nó. Liệt kê 10
biểu diễn một ví dụ có được từ lớp _FormWidget.
Liệt kê 10. Trộn lẫn thuộc tính atrributeMap lớp cơ sở với các giá trị tùy biến
attributeMap:
dojo.mixin(
dojo.clone(dijit._Widget.prototype.attributeMap),
{id:"focusNode", tabIndex:"focusNode", alt:"focusNode"}
),
|
Trong pha tạo widget, một vài phương thức sẽ được gọi, cho phép những người cài đặt thực hiện một số hành động trong các pha khởi tạo cụ thể. Các phương thức sau đây được gọi:
-
postMixInProperties: phương thức này được gọi sau khi tất cả các tham số của widget đã được thiết lập (được trộn) thành các thuộc tính của cá thể widget. Đây là thời điểm thích hợp để tiếp tục khởi tạo thuộc tính, nếu dựa trên các thuộc tính mà việc khởi tạo cá thể đối tượng sẽ cung cấp. -
buildRendering: phương thức này được gọi vào thời điểm sinh ra biểu hiện của widget. Điểm móc nối này được triển khai trong lớp trộndijit._Templated, lớp này cũng triển khai các khung sườn widget bằng các khuôn mẫu HTML. Những người triển khai có thể cung cấp cách thực hiện của mình nhằm tinh chỉnh công việc của lớp_Templated. -
postCreate: phương thức này được gọi sau khi DOM của widget đã sẵn sàng và được chèn vào trang. Người triển khai có thể thao tác trên cấu trúc DOM của widget tại vị trí này. Các widget con chưa được khởi động. -
startup: cơ hội cuối để thao tác trên widget. Vào lúc này thì các wiget con đã được khởi động. -
uninitialize: được gọi sau khi widget đã bị hủy bỏ. Người triển khai có thể thực hiện một vài việc dọn dẹp làm sạch không được tự động hóa.
Các phương thức khuôn mẫu có thể do người triển khai cung cấp, và người triển khai phải nhớ rằng lớp cha mẹ bậc trên có thể đã triển khai thực hiện các phương thức khuôn mẫu của nó. Để đảm bảo việc khởi tạo chính xác tất cả các lớp trong chuỗi móc xích này, người triển khai phải mã hóa thủ công lời gọi phương thức của lớp bậc trên như được chỉ ra trong Liệt kê 11.
Liệt kê 11. Ví dụ về việc gọi phương thức của lớp cha mẹ bậc trên.
startup: function()
{
// call the superclass' method
this.inherited("startup", arguments);
// your code here
}
|
Như
quy ước chung, đối với tất cả các phương thức tạo sinh (tất cả các phương
thức khuôn mẫu ngoại trừ uninitialize), phương
thức của lớp bậc trên phải được gọi đầu tiên để đảm bảo rằng các mục dữ
liệu của lớp bậc trên được khởi tạo chính xác trước khi thao tác với
chúng.
Đối với phương thức uninitialize
thì lại áp dụng quy tắc ngược lại.
Một lưu ý cuối cùng là về phương
thức startup. Đừng quên rằng phương thức
startup được phương thức dojo.parser.parse tự
động gọi, trong trường hợp widget được tạo ra bởi một khai báo. Startup sẽ
không được thực hiện nếu widget được tạo ra bằng các lệnh chương trình và
sẽ phải được khởi động thủ công như trong ví dụ dưới
đây.
var w = new MyWidget({});
w.startup();
|
Sau khi được tạo ra, các widget sẽ tồn tại cho đến khi có một yêu cầu hủy bỏ tường minh được thi hành. Người triển khai chịu trách nhiệm quản lý toàn bộ vòng đời của widget, bao gồm cả việc hủy bỏ widget. Nếu khác đi, sẽ đưa tới kết quả là có một widget lỗ thủng cho đến khi toàn bộ trang được dọn dẹp sạch sẽ. Widget cung cấp 4 phương thức hủy bỏ là:
-
destroyRendering: phương thức này dọn sạch các mục biểu hiện của widget. Trừ phi người triển khai cần dọn dẹp từng phần, phương thức này thường được gọi bởi các phương thức hủy bỏ khác. -
destroyDescendants: hủy bỏ tất cả các widget con. Trừ phi người triển khai cần dọn dẹp từng phần, phương thức này được gọi bởi phương thứcdestroyRecursive. -
destroy: hủy bỏ các mục widget (chứ không phải là các widget con). -
destroyRecursive: hủy bỏ các mục của widget và các widget con. Nếu widget chứa các widget con bên trong thì bắt buộc phải gọi phương thức này.
Trong trường hợp widget có chứa các widget bên trong (ví dụ, có một vài widget trong khuôn mẫu), người triển khai phải nhớ kích hoạt các widget con, hủy bỏ chúng theo cách nào đó mà không bắt người dùng widget phải quyết định xem phải gọi phương thức hủy bỏ nào. Liệt kê 12 cho thấy một phương án khả thi để thực hiện điều này.
Liệt kê 12. Ví dụ về việc kích hoạt phương thức hủy bỏ của một widget.
uninitialize: function()
{
// destroy the descendants
this.destroyDescendants();
// call the superclass' method
this.inherited("uninitialize", arguments);
}
|
Một widget
dojo có thể phản ứng lại các sự kiện bên ngoài, xuất phát từ cả các nút
DOM lẫn các đối tượng. Những sự kiện này có thể được kết nối thủ công bằng
cách dùng phương thức connect của widget,
phương thức này có chữ ký như sau (hoàn toàn tương tự như phương thức
dojo.connect):
connect: function(/*Object|null*/ obj, /*String*/ event, /*String|Function*/ method); |
Hoặc
nó có thể tự động kết nối nhờ khai báo kết nối trong khuôn mẫu sử dụng
thuộc tính dojoAttachEvent. Trong cả hai trường
hợp, kết nối được theo vết bên trong bởi widget trong mảng _connects. Tất
cả các kết nối sẽ được ngắt tự động lúc thực hiện hủy bỏ. Theo cách này,
các tham chiếu giữa widget và nút DOM bị phá bỏ.
Nếu trong vòng đời
của widget, một số kết nối không cần đến nữa thì người triển khai có thể
ngắt kết nối một cách thủ công nhờ gọi phương thức disconnect để giảm bớt việc xử lý sự kiện.
Như đã thấy
trước đây, các widget phải thừa kế từ lớp dijit._Widget, lớp này định nghĩa và cung cấp các hành vi cơ
bản cho một widget Dojo. Một lớp cơ sở như vậy định nghĩa phương thức
buildRendering chịu trách nhiệm xây dựng
xây dựng các phần tử biểu hiện widget. Ví dụ, một người triển khai widget
có thể xây dựng trong phương thức này các thẻ đánh dấu widget và đặt nó
vào nút DOM widget. Một tùy chọn khác là có thể tạo cấu trúc widget bằng
API DOM. Trong cả hai trường hợp, người triển khai phải lập trình phương
thức buildRendering theo cách nào
đó.
Dojo cung cấp một mức trừu tượng hóa mạnh, tách riêng định nghĩa
việc biểu hiện widget ra khỏi việc triển khai thực hiện các hành vi của
widget: đó là lớp trộn dijit._Templated. Người
triển khai muốn khai thác việc trừu tượng hóa này thì cần phải thừa kế từ
lớp _Templated, như trong Liệt kê
13.
Liệt kê 13. Mã lệnh để thừa kế từ lớp _Templated
dojo.declare(
"widgets.MyWidget",
dijit._Widget, dijit._Templated
{
templatePath: dojo.moduleUrl("widgets", "templates/MyWidget.html"),
}
);
|
Lớp
_Templated cung cấp một cách triển khai
thực hiện phương thức buildRendering của riêng
nó, sử dụng định nghĩa kiểu như HTML. Định nghĩa này có thể có mặt ở hai
nơi khác nhau.
- Một tệp ngoài. Trong trường hợp này, tệp này được tham chiếu bởi thuộc tính templatePath templatePath.
- Thuộc tính xâu nội tại. Trong trường hợp này, khuôn mẫu sẽ được định
nghĩa trực tiếp trong widget ở thuộc tính
templateString. Nếu có chỉ rõ thuộc tínhtemplateStringthì thuộc tínhtemplatePathsẽ bị bỏ qua, thậm chí cả khi nó đã được xác định rõ.
Tùy chọn thứ nhất đại diện cho cách sạch sẽ nhất để tổ chức mã nguồn widget, vì các thẻ đánh dấu (mark-up) có thể được viết trong một tệp khác, mã nguồn này có thể được định dạng theo cách dễ đọc mà ta không phải lo lắng về việc nối ghép xâu, và sẽ không phải áp mã thoát các dấu phân cách xâu. Mặt khác, tùy chọn thứ hai lại là cách có hiệu năng tốt hơn, vì không cần phải nạp một tệp thứ hai từ trình duyệt. Tuy nhiện, bạn không nên lo lắng về điều đó vì Dojo sẽ cung cấp công cụ xây dựng để tiếp nhận và nội tại hóa các khuôn mẫu bên ngoài vào trong mã nguồn widget.
Khuôn mẫu
có thể được tham số hóa bằng cách tham chiếu đến các thuộc tính widget.
Điều này rất có ích khi widget trưng ra các tham số cấu hình có ảnh hưởng
trực tiếp đến các thẻ đánh dấu hoặc nếu các thẻ đánh dấu phải được sinh ra
tùy theo điều kiện phụ thuộc vào các lựa chọn ưu tiên ngoài. Các thuộc
tính widget được tham chiếu theo cú pháp sau: ${propertyName}.
Các khuôn mẫu có thể chứa các khai báo
widget khác nữa. Tuy nhiên, để chúng được xem xét, những người phát triển
widget phải đặt thuộc tính widgetsInTemplate là
true, mà bình thường thì mặc định đặt là false để bỏ qua việc xử lý không
cần thiết.
Các khuôn mẫu có thể có 2 khai báo thuộc tính đặc biệt sau đây:
- dojoAttachPoint: thuộc tính thẻ này, nếu được chỉ rõ, phải được đặt là một tên thuộc tính widget. DOM tương ứng với thẻ này sẽ được thiết đặt là thuộc tính của widget. Một vài thẻ khác trong khuôn mẫu có thể có một hoặc nhiều điểm gắn kết.
- dojoAttachEvent: thuộc tính thẻ này liệt kê ra các phương thức widget sẽ được gọi lại khi một sự kiện DOM được kích hoạt.
Liệt kê 14 chỉ ra một khuôn mẫu ví dụ.
Liệt kê 14. Khuôn mẫu ví dụ về các khai báo thuộc tính đặc biệt
<span
class="textInputDefault"
dojoAttachEvent="onclick:_onClick">
<img
class="textInputIcon"
src="${constants.DEFAULT_ICON}"
dojoAttachPoint="_iconNode"/>
<input
class="textInputNode"
size="${size}"
type="${type}"
value=""
dojoAttachPoint="_inputNode, _focusNode"
dojoAttachEvent="onkeyup:_onKeyUp, onkeypress:_onKeyPress, onchange:_onChange"
/>
<span
class="textInputRight">
</span>
</span>
|
Bảng điều khiển tải công việc động của Tivoli và Dojo
Bảng điều khiển tải công việc động của Tivoli (TDWC) là một giao diện người dùng đồ họa của trình lập lịch tải công việc của Tivoli (TWS). Phần trình bày này sẽ chỉ ra cách TDWC v.8.5 tận dụng ưu thế các khả năng của Dojo như thế nào.
Giới thiệu về trình lập lịch tải công việc của Tivoli
TWS là một giải pháp tự động hóa triển khai trong sản xuất được thiết kế để trợ giúp việc quản lý tải công việc được nạp trong những môi trường điều hành phức tạp hiện nay. Các thành phần lập lịch chính là công việc (job) và luồng công việc (job stream). Một công việc là một thể hiện của một tác vụ nào đó, như là một tệp chạy được, một chương trình hay một lệnh nào đó. Một luồng công việc biểu diễn một thùng chứa các công việc có liên quan và tổ chức chúng về mặt thời gian chạy, tuần tự hóa, các giới hạn tương tranh (concurrency limitations) và việc lặp lại nhiều lần. TWS gúp lập kế hoạch để thi hành các công việc, phân giải các phần phụ thuộc lẫn nhau, khởi chạy và theo vết từng công việc. Đó là vì các công việc được bắt đầu ngay khi những phụ thuộc của chúng được giải quyết; bởi vậy, sẽ giảm tối đa thời gian nhàn rỗi và thông lượng sẽ tăng đáng kể. Các công việc không bao giờ chạy sai so với chuỗi tuần tự và nếu một công việc thất bại thì TWS sẽ xử lý tiến trình tái lập với ít can thiệp nhất có thể.
TDWC phiên bản 8.5 bao gồm một trình soạn thảo tải công việc có đầy đủ tính năng được viết hoàn toàn bằng JavaScript sử dụng bộ công cụ Dojo. Kiểu triển khai thực hiện này cho phép di chuyển “thông minh” và hành xử gần gũi hơn với người sử dụng, bằng cách sử dụng mã lệnh chạy trong trình duyệt web ở mức tối đa có thể.
Ta hãy xét một ví dụ về bảng (panel) thuộc tính của một luồng công việc TWS được trình bày bởi TDWC. Hình 3 cho thấy một bảng Web các thuộc tính chung đã định nghĩa của luồng công việc PAYROLL.
Hình 3. Bảng Web của luồng công việc PAYROLL
Trong bài viết trước đây (“Bộ công cụ Web ngôn ngữ đánh dấu giao diện người dùng trừu tượng: một trình biểu hiện AUIML dành cho JavaScript và Dojo”, xem Tài nguyên), chúng tôi đã mô tả cách thiết kế bảng dùng bộ công cụ AUIML như thế nào và cách triển khai mã logic trong JavaScript dùng bộ công cụ Web AUIML (AWT). Hình 4 biểu diễn bảng AUIML:
Hình 4. bảng AUIML
Ta hãy để ý đến trường Valid from. Nó được định nghĩa giống như Edit Box, Date trong trình soạn thảo AUIML; AWT kết hợp thành phần này với mã HTML hiển thị trong liệt kê 15.
Liệt kê 15. Mã HTML kết hợp với phần tử
<span type='text' dojoType='ajaxcommon.widgets.DateInputBox' id='validFrom'>
<script type='dojo/method'event='onValueChanged'>AWT.dispatchOnChange(this.id);</script>
</span>
|
Widget ajaxcommon.widgets.DateInputBox được định nghĩa trong Liệt kê 16:
Liệt kê 16. Mã lệnh định nghĩa ajaxcommon.widgets.DateInputBox
dojo.provide("ajaxcommon.widgets.DateInputBox");
dojo.require("ajaxcommon.resources.Images");
dojo.require("ajaxcommon.widgets._DateTimePicker");
dojo.require("ajaxcommon.widgets.picker.PickerInputBox");
dojo.require("ajaxcommon.widgets.picker.DatePicker");
dojo.declare(
"ajaxcommon.widgets.DateInputBox",
[ajaxcommon.widgets.picker.PickerInputBox,
ajaxcommon.widgets._DateTimePicker],
{
/** the picker icon */
pickerIcon: ajaxcommon.resources.Images.get()["CALENDAR_ICON"],
/** the picker disabled icon */
pickerDisabledIcon: ajaxcommon.resources.Images.get()["DISABLED_CALENDAR_ICON"],
/** the picker icon title */
pickerIconTitle: ajaxcommon.resources.Labels.get()["PICK_DATE"],
/** constraints */
constraints: {selector: "date", formatLength: "short", wideYear: true},
/**
* Constructor.
*/
constructor: function()
{
// even if the format length is short, ensure the wide year (yyyy)
// by overriding the datePattern
if (this.constraints.formatLength === "short" && this.constraints.wideYear) {
this.constraints.datePattern = this._getWideDatePattern();
}
// set the regex for the text
this.textRegExp = "^(" + dojo.date.locale.regexp(this.constraints) + “){0,1}$";
// set the regex message for the text
this.textRegExpMessage =
this._labels.format("DATE_INVALID_VALUE", {example: this._getExampleValue()});
// set the picker class
this.pickerClass = "ajaxcommon.widgets.picker.DatePicker";
},
/**
* Returns the date pattern with the wide-year (yyyy)
*/
_getWideDatePattern: function()
{
// get the bundle
var bundle = dojo.date.locale._getGregorianBundle();
// get the pattern
var pattern = bundle["dateFormat-short"];
// replace the yy to yyyy if not yet yyyy
if ( pattern.search(/yyyy/gi) === -1 ) {
// the year is not in the wide form
pattern = pattern.replace(/yy/gi, "yyyy");
}
return pattern;
},
/**
* Returns a string containing an example of accepted value.
*/
_getExampleValue: function()
{
// get a sample date object (my birthday!!!)
var d = new Date();
d.setDate(15);
d.setMonth(4);
d.setYear(1971);
// format the date in the current locale and return
return dojo.date.locale.format(d, this.constraints);
}
}
);
|
Hàm
tạo cho phép khởi tạo các thuộc tính widget được khai báo trong widget lớp
bậc trên ajaxcommon.widgets.picker.PickerInputBox, lớp bậc trên này
được định nghĩa trong Liệt kê 17.
Liệt kê 18 hiển thị khuôn mẫu widget có liên quan.
Liệt kê 18. Mẫu widget cho ajaxcommon.widgets.picker.PickerInputbox
<span
class="picker"
><span
tabindex="${tabindex}"
dojoType="${_textBoxWidget}"
dojoAttachPoint="_textNode,_focusNode"
required="${required}"
size="${size}"
minLength="${textMinLength}"
maxLength="${textMaxLength}"
regExp="${textRegExp}"
regExpReference="${textRegExpReference}"
regExpMessage="${textRegExpMessage}"
minWidth="${minWidth}"
></span
><span
dojoAttachPoint="_pickerButtonContainer">
<img
tabindex="${tabindex}"
src="${pickerIcon}"
dojoAttachPoint="_pickerButtonNode"
dojoAttachEvent="onclick:_onClick,onkeyup:_onKeyUp"
class="pickerButton"
title="${pickerIconTitle}"
/></span>
</span>
|
Hình 5 chỉ ra những ưu thế của việc chuyển giao logic của widget sang cho trình duyệt. Widget được triển khai để hiển thị những thông báo lỗi có liên quan và để thay đổi dáng vẻ bên ngoài khi có bất kỳ lỗi nào xảy ra trong quá trình thiết đặt giá trị.
Hình 5. Bảng cho thấy logic của widget ở phía trình duyệt
Bài viết này mô tả những khái niệm cơ bản của việc phát triển các widget HTML sử dụng bộ công cụ Dojo JavaScript, bắt đầu từ phiên bản 1.0 với những ví dụ đơn giản, tiến tới những ví dụ phức tạp hơn (như triển khai các widget trong TDWC phiên bản 8.5). Bài viết này cũng nhấn mạnh đến những điểm mạnh trong bộ công cụ Dojo.
Trong bài tiếp theo, chúng ta sẽ xem xét lại những khiếm khuyết về hiệu năng phổ biến thường gặp trong quá trình phát triển các ứng dụng Internet phong phú và sẽ chỉ ra cách giải quyết chúng bằng những cách làm thực tế tốt nhất mà chúng tôi đã xây dựng, chủ yếu dựa trên bộ công cụ Dojo.
- Hãy xem blog của developerWorks và tham gia vào cộng đồng developerWorks community.
- Tải về bản phát
hành mới nhất của bộ công cụ
Dojo (dojo-release-1.x.x-src.zip hoặc
dojo-release-1.x.x-src.tar.gz)
- Tải về FireBug từ
trang web của dự án
này.

Marco là kỹ sư biên chế ở phòng thí nghiệm Tivoli, IBM Rome, từ năm 1999. Marco hiện đang tham gia vào việc thiết kế và phát triển TDWC. Lĩnh vực Marco quan tâm là phát triển Web 2.0, đặc biệt là các công nghệ dựa trên nền Dojo và các mẫu thiết kế về trải nghiệm của người dùng
Roberto đã làm việc trong lĩnh vực phát triển phần mềm 12 năm, đảm nhiệm nhiều vai trò, từ người phát triển, nhà phân tích, nhà thiết kế giao diện Web đến nhà thiết kế tương tác. Ông chính là Longobardi hiện đang làm việc ở Tivoli với vai trò nhà thiết kế giao diện người dùng Web cho các sản phẩm Tivoli Workload Scheduler (Trình lập lịch tải công việc Tivoli) và Dynamic Workload Broker (Trình môi giới tải công việc động). Với vai trò này, Longobardi cũng thường làm những công việc có giao tiếp trực diện với người dùng như thu thập yêu cầu, xác nhận thiết kế và xúc tiến bán sản phẩm (sales enablement). Roberto đã công bố nhiều bài viết trên developerWorks, đã trình bày trong các hội nghị của IEEE và tham gia cộng tác nhiều về học thuật.
Gianluca Perreca là kỹ sư biên chế của phòng thí nghiệm Tivoli, IBM Rome. Gianluca là thành viên của đội phát triển Tivoli Perreca’s Workload Scheduler (Trình lập lịch tải công việc Perreca Tivoli) và hiện tham gia tích cực trong thiết kế và triển khai IBM Tivoli Dynamic Workload Console (bảng tải công việc động Tivoli IBM), giao diện Web cho IBM Tivoli Workload Scheduler (Trình lập lịch tải công việc Tivoli của IBM)

Alessandro Scotti là kiến trúc sư trưởng của IBM Tivoli Dynamic Workload Console (bảng tải công việc động Tivoli của IBM), một giao diện Web Alessandro dành cho IBM Tivoli Workload Scheduler (trình lập lịch tải công việc Tivoli của IBM), đã sử dụng bộ công cụ AUIML cũng như AUIML Scotti và các công nghệ Web 2.0 khác. Alessandro cũng là tác giả hay đồng tác giả của nhiều dự án mã nguồn mở, chủ yếu trong lĩnh vực giao diện người dùng đồ họa, hiệu năng cao và lập trình thời gian thực.