Bộ sưu tập

Sử dụng JsRender, JsViews và JsObservable để xây dựng trang web có tính tương tác


https://codersontrang.com/2013/11/24/su-dung-jsrender-jsviews-va-jsobservable-de-xay-dung-trang-web-co-tinh-tuong-tac/
Nói đến các trang web có tính tương tác, ta nghĩ ngay đến các trang web mà ở đó có một lượng code Javascript một cách đáng kể để xử lý các chuỗi sự kiện mà người dùng tương tác với trang web đó. Việc thêm vào, xóa đi, làm hiện lên, hay làm ẩn đi, thay đổi cấu trúc của một thành phần trong trang web… là các công việc mà một trang web có tính tương tác người dùng thường hay gặp phải. Và những công việc này thường được làm bằng Javascript. Lượng javascript lớn cũng có nghĩa làm cho trang web trở nên phức tạp hơn, khó bảo trì hơn, và có ít thành phần có thể sử dụng lại hơn. Bài viết này sẽ giới thiệu bộ ba thư viện Javascript là JsRender, JsViews và JsObservable, với sự góp mặt của 3 thư viện này người lập trình viên sẽ có một cách thức mới để tạo nên một trang web có tính tương tác cao nhưng ít phức tạp hơn, dễ bảo trì hơn và có tính sử dụng lại cao hơn so với việc chỉ sử dụng Javascript một cách đơn thuần.

JsRender: là bộ thư viện javascript cho phép tạo ra các mẫu (template) dạng HTML và sau đó trang web có thể sử dụng lại các mẫu này để sinh ra các thành phần HTML bên trong nội dung của nó.

JsViews : là bộ thư viện javascript được xây dựng để mang đến tính năng data-binding vào trong các mẫu tạo ra từ JsRender. Data-binding là cơ chế mà ở đó dữ liệu trong model và dữ liệu trên giao diện có một sự đồng bộ với nhau về sự thay đổi. Cụ thể hơn, khi dữ liệu ở trong một model thay đổi thì dữ liệu tương ứng hiển thị trên giao diện cũng tự động thay đổi theo và ngược lại.

JsObservable: là một phần của JsViews, cung cấp các API cho việc thực hiện cơ chế thông báo (notification) khi dữ liệu trong model bị thay đổi, góp phần tạo nên tính năng data-binding đã nói ở trên.

Trong bài viết này, ta sẽ làm một ví dụ về một trang cho phép người dùng có thể thao tác với một danh sách sinh viên. Các thao tác bao gồm thêm một sinh viên vào danh sách, cập nhật thông tin của một sinh viên trong danh sách và xóa một sinh viên khỏi danh sách. Danh sách sinh viên sẽ được hiển thị như hình dưới đây:

jsRender_jsViews_jsObservable_1

Thêm mới một sinh viên bằng cách nhập mã sinh viên, tên và điểm cho sinh viên rồi bấm Save. Sinh viên có mã S004 được thêm mới vào danh sách như trong hình dưới đây

jsRender_jsViews_jsObservable_2

Cập nhật thông tin cho một sinh viên bằng cách nhập đúng mã của sinh viên đó và cập nhật các thông tin khác như tên, điểm rồi bấm Save. Chẳng hạn ta cập nhật cho sinh viên có mã S003, chuyển tên của sinh viên này thành Nguyen Van E và điểm là 10 sẽ giống như hình dưới đây:

jsRender_jsViews_jsObservable_3

Xóa một sinh viên ra khỏi danh sách bằng cách bấm Remove bên cạnh thông tin sinh viên tương ứng. Chẳng hạn kết quả của việc xóa sinh viên có mã S002 ra khỏi danh sách sẽ giống như hình dưới đây:

jsRender_jsViews_jsObservable_4

Trang HTML và bộ ba thư viện JsRender, JsViews, JsObservable có thể được download tại đây. Hoặc ta có thể download các thư viện từ website chính của chúng.

student-management.html

 <html>  
      <head>  
           <title>Student Management</title>  
           http://code.jquery.com/jquery.js  
           http://lib/jsrender.js  
           http://lib/jsobservable.js  
           http://lib/jsviews.js  
      </head>  
      <body>  
           
</body> Student ID: data-link="newId"/> Name: data-link="newName"/> Mark: data-link="newMark"/> {^{for students}}{{/for}}
Student ID Student Name Mark
{^{:id}} {^{:name}} {^{if mark >= 5}}{^{:mark}}{{else}}{^{:mark}}{{/if}} {{:id}}'/>
var data= { students : [ {id:'S001', name:'Nguyen Van A', mark:8}, {id:'S002', name:'Nguyen Van B', mark:4}, {id:'S003', name:'Nguyen Van C', mark:5} ], newId : '', newName : '', newMark : '' }; $(function(){ var template = $.templates('#studentTableTemplate'); template.link('#studentTable', data); var newStudentTemplate = $.templates('#newStudentTemplate'); newStudentTemplate.link('#newStudentPanel', data); wireEvents(); }); function wireEvents(){ $('#btn_saveStudent').click(saveStudent); $('.btn_removeStudent').toArray().forEach(function(btn){ var sid = $(btn).attr('sid'); $(btn).click(removeStudent.bind(this, sid)); }); } function removeStudent(sid){ data.students.forEach(function(student, index){ if(student.id == sid){ $.observable(data.students).remove(index); return; } }); } function saveStudent(){ var newId = data.newId; var newName = data.newName; var newMark = data.newMark; var inserted = true; data.students.forEach(function(student, index){ if(student.id == newId){ inserted = false; $.observable(data.students[index]).setProperty('name', newName); $.observable(data.students[index]).setProperty('mark', newMark); return; } }); if(inserted){ var newStudent = {id: newId, name: newName, mark: newMark}; $.observable(data.students).insert(newStudent); $('.btn_removeStudent').toArray().forEach(function(btn){ var sid = $(btn).attr('sid'); $(btn).click(removeStudent.bind(this, sid)); }); } } </html>

Như chúng ta có thể thấy, trong trang web có sử dụng đến jQuery để dễ dàng hơn khi thao tác với javascript. Kế tiếp là các thư viện jsrender.js, jsviews.js và jsobservable.js lần lượt được tham chiếu đến. Chú ý một điều là việc đảo lộn thứ tự tham chiếu đến các thư viện không giống với thứ tự ở trên sẽ có thể là nguyên nhân gây ra lỗi javascript.

Kế tiếp là phần thân của trang web, rất đơn giản khi trong ví dụ này ta chỉ nhìn thấy hai thẻ <div> lần lượt có id là studentTablenewStudentPanel. Đây chính là hai container sẽ chứa các nội dung sinh ra từ template của jsRender ngay sau khi việc tải trang web được hoàn thành.

Toàn bộ các thông tin như danh sách sinh viên, và thông tin cho một sinh viên mới được nhập vào từ màn hình sẽ được lưu trong một object của javascript là data như sau

 var data= {  
                students : [  
                     {id:'S001', name:'Nguyen Van A', mark:8},  
                     {id:'S002', name:'Nguyen Van B', mark:4},  
                     {id:'S003', name:'Nguyen Van C', mark:5}  
                ],  
                newId : '',  
                newName : '',  
                newMark : ''  
           };  

Trong đối tượng data, students là một mảng của các đối tượng sinh viên. Một đối tượng sinh viên bao gồm id, namemark. Cũng trong đối tượng data này newId, newName, newMark sẽ là các thuộc tính để lưu trữ thông tin của người dùng nhập vào cho một sinh viên mới.

Mỗi một jsRender template sẽ được gói ở trong thẻ <script type="text/x-jsrender">. Chú ý đến thuộc tính type trong thẻ này nếu không muốn nhầm nó với thẻ <script type="text/javascript"> được dùng để viết các đoạn mã thực thi javascript. Mỗi một jsRender template sẽ có một id duy nhất, và trong ví dụ này ta có hai template có id lần lượt là newStudentTemplate, studentTableTemplate. Nội dung HTML được sinh ra từ hai template này sẽ được chèn vào nội dung của hai container (hai thẻ <div>) newStudentPanel, studentTable tương ứng mà ta đã nói ở trên.

Template có id là newStudentTemplate là template sinh ra nội dung cho phần thêm thông tin cho một sinh viên mới. Phần nội dung sẽ bao gồm 3 thẻ <input> để nhập thông tin về id, name, mark và một nút Save. Để ý là trong mỗi thẻ <input> có một thuộc tính là data-link. Đây là một thuộc tính của jsViews cho phép thực hiện data-binding. Giá trị của thuộc tính này là tên của trường trong đối tượng data tương ứng. Với data-binding, một sự thay đổi về giá trị của một trường newId, newName, newMark trong đối tượng data sẽ được phản ánh lên thành phần <input> tương ứng. Và ngược lại khi ta thay đổi giá trị trong các ô <input> thì sự thay đổi cũng được tự động cập nhật vào trường tương ứng trong đối tượng data.

Template có id là studentTableTemplate là template sinh ra nội dung một bảng danh sách sinh viên. Trong template này có sử dụng các thành phần {{}} chính là cú pháp cho việc xử lý và hiển thị dữ liệu trong jsRender. {{for students}} làm nhiệm vụ lặp qua một mảng các đối tượng sinh viên. {{:id}}, {{:name}}, {{:mark}} là để hiển thị mã, tên và điểm trong từng đối tượng sinh viên. {{if}}{{else}} dùng trong trường hợp hiển thị có điều kiện mà như trong ví dụ này ta có thể thấy điểm số của sinh viên lớn hơn hoặc bằng 5 thì sẽ hiển thị có màu xanh da trời, còn lại sẽ có màu đỏ. Khi ta thay đổi thông tin của một đối tượng sinh viên, thêm hay xóa bớt một đối tượng sinh viên từ mảng các đối tượng sinh viên trong đối tượng data. Ta yêu cầu sự thay đổi đó phải được phản ánh ngay lên phần hiển thị. Để phần hiển thị lắng nghe được và nhận biết được sự thay đổi này ta chèn thêm ^ vào trong thành phần {{}}. Quay trở lại ví dụ, để phần hiển thị có thể tự động cập nhật khi thêm hay bớt một sinh viên ta dùng {^{for students}}, hay để tự động cập nhật thông tin thay đổi cho một sinh viên ta dùng {^{:id}}, {^{:name}}, {^{:mark}}, {^{if}}{{else}}.

Như vậy ở bên trên, một cơ chế lắng nghe đã được thiết lập cho các thành phần hiển thị để nhận biết sự thay đổi về thông tin liên quan đến danh sách sinh viên. Điều đó cũng có nghĩa rằng cũng phải tồn tại một một cơ chế thông báo một khi có sự thay đổi xảy ra trên danh sách sinh viên đó. Cơ chế thông báo sẽ được thiết lập nhờ các API của jsObservable. Với các API này, khi dữ liệu thay đổi, ngoài việc giúp cập nhật sự thay đổi vào trong đối tượng lưu trữ dữ liệu, chúng còn giúp thông báo đến các thành phần hiển thị đang lắng nghe và từ đó các thành phần hiển thị sẽ có thể nhận biết và cập nhật sự thay đổi tương ứng. Cụ thể trong ví dụ về API của jsObservable, khi thêm một sinh viên mới ta sẽ dùng $.observable(data.students).insert(newStudent);, khi xóa một sinh viên khỏi danh sách thì dùng $.observable(data.students).remove(index); và khi cập nhật thông tin về tên một sinh viên trong danh sách thì dùng $.observable(data.students[index]).setProperty('name', newName);

Như đã nói ở trên, như vậy sẽ có một sự liên hệ giữa các jsTemplate và đối tượng data. Sự liên hệ chính là việc các template sẽ hiển thị dữ liệu trong đối tượng data và sự thay đổi về dữ liệu sẽ được cập nhật theo 2 chiều (thay đổi từ hiển thị sẽ cập nhật đến data, thay đổi từ data sẽ cập nhật lên phần hiển thị). Và sự liên hệ này được thiết lập bằng các API của jsRender và jsViews. Trong ví dụ này, var template = $.templates('#studentTableTemplate'); là API của jsRender giúp biên dịch jsRender template có id là studentTableTemplate thành một đối tượng của javascript có tên là template. Sau đó từ đối tượng template này ta gọi API của jsViews là template.link('#studentTable', data); để tạo nên sự liên hệ mà ta nói ở phía trên giữa template và đối tượng data được truyền vào (tham số thứ 2). Sự kết hợp giữa templatedata sẽ sinh ra đoạn mã HTML tương ứng và đoạn mã HTML này sẽ được gán đến nội dung của thành phần <div> có id là studentTable. Từ đó mà ta có thể thấy một danh sách sinh viên dưới dạng bảng được hiển thị trên trang web.

Như vậy ta có thể thấy việc sử dụng jsRender, jsViews và jsObservable đem lại cho ta một cách thức khá hay để tạo nên các trang web đòi hỏi tính tương tác cao. Cách thức này dựa trên cơ chế thông báo và lắng nghe giữa các thành phần hay có một thuật ngữ để gọi tên đó là event-driven. Với cách thức trên các thành phần hiển thị và dữ liệu hoàn toàn có thể tách biệt nhau, sáng sủa hơn, ít phức tạp hơn và có tính tái sử dụng cao hơn. Một cơ chế lắng nghe và thông báo được dựng lên nhờ các API có sẵn và lập trình viên giờ đây chỉ cần quan tâm và thao tác với dữ liệu mà không cần lo lắng và bỏ công sức cho việc cập nhật hiển thị một cách tương ứng.

Good luck!

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất /  Thay đổi )

Google photo

Bạn đang bình luận bằng tài khoản Google Đăng xuất /  Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất /  Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất /  Thay đổi )

Connecting to %s