Tìm hiểu các cơ sở trong việc phát triển các widget HTML bằng bộ công cụ Dojo JavaScript. Bài viết này chỉ giới thiệu và cung cấp một số ví dụ nhằm giúp bạn trong quá trình xây dựng – khởi đầu bằng một số widget mẫu ví dụ và tiến dần tới các widget phức tạp hơn, đồng thời nêu bật và giải quyết các vấn đề phổ biến mà bạn có thể gặp trong giai đoạn phát triển.

Marco Lerro, Kỹ sư, IBM

Photo of Marco LerroMarco 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 Longobardi, Thiết kế giải pháp, IBM Tivoli Software

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, Kỹ sư, IBM Tivoli Software

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, Kỹ sư phần mềm, IBM

Photo of Alessandro ScottiAlessandro 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.



23 10 2009

Giới thiệu

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.


Bộ công cụ Dojo là gì?

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ạn bắt đầu

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:

  1. 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ỉ)
  2. 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.
Một cấu trúc thư mục chỉ ra thư mục gốc, có thư mục con dojo1.x.x, thư mục con sau nữa dijit, dojo, dojox và util.

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.


Các widgets Dojo

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.

Nhập khẩu bộ công cụ Dojo

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 parseOnLoadisDebug 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>

Hệ thống gói của Dojo

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.

Khai báo một widget

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._Widgetdijit._Templated là các lớp cha mẹ bậc trên (superclasses) của widget TextBox.
  • templatePath, inputNode, và label là các thuộc tính của widget.
  • onKeyUp() 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 textBoxinputOk 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
Hộp hiển thị hai hộp văn bản nhỏ hơn; một có đường viền mầu xanh với ok, một có đường viền mầu đỏ với lỗi (error)

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.

Pha tạo lập widget

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:

  1. 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.
  2. 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"}
),

Các phương thức khuôn mẫu

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:

  1. 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.
  2. 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ộn dijit._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.
  3. 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.
  4. 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.
  5. 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();

Pha hủy bỏ widget

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ức destroyRecursive.
  • 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);
}

Quản lý sự kiện

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.

Khuôn mẫu widget

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.

  1. 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.
  2. 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ính templateString thì thuộc tính templatePath sẽ 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">&nbsp;
 </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)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ể.

Sử dụng Dojo

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
Ảnh chụp màn hình của luồng công việc cho PAYROLL hiển thị tên PAYROLL và trạm làm việc là LAB132079_DM1_85. Valid từ trường dữ liệu đang được chỉnh sửa, có một lịch công tác bật ra. Các trường Valid To và comments để trống.

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
Một ảnh chụp màn hình của bảng AUIML hiển thị cáctrường nhập vào còn trống Name, Workstation, Valid From, Valid to, Description, Variable Table, Monitored, Draft và Commnents

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
Một ảnh lặp lại ảnh chụp màn hình từ Hình 3, bây giờ đang hiển thị giá trị cài đặt của trường Valid From tới 15.05.1971

Kết luận

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.

Tài nguyên

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Công nghệ Java, Nguồn mở
ArticleID=438755
ArticleTitle=Phát triển các widget bằng Dojo 1.x
publish-date=10232009