Lời nói đầu
Trải qua 2 bài viết trước, chúng ta đã hiểu cách vận hành của mô hình MVC trong backbone và sử dụng backbone để tạo ra ứng dụng đầu tiên là app TODO List trên AWS, ở bài viết thứ 3 này chúng ta sẽ tìm hiểu thêm về backbone và một bộ thư viện thú vị tên là Marionette.js, bài viết số 3 được dịch từ Marionette.jsでBackboneをもっと便利に! - AWS上で構築するRESTfulアプリ勉強会~Web開発ワークショップ~【第3回】マニュアル của tác giả @k_shimoji .
Ở bài viết này, chúng ta sẽ sử dụng thêm thư viện Marionette.js.
Backbone khá là rắc rối, bây giờ chúng ta sẽ nhờ sự trợ giúp của thư viện Marionette để giảm bớt sự cồng kềnh của chương trình và xử lý chúng.
Tổng quan về Marionette
Marionette.js là gì ?
Nó sẽ hỗ trợ backbone trong quá trình cấu trúc hệ thống, như tên gọi của nó vậy ( Marionette = Con rối ), Mô hình MVC Model trong app client chỉ như một xương sống, và chúng ta cần viết rất nhiêu đoạn code cho ứng dụng, tuy nhiên sẽ xảy ra những trường hợp gọi là "Mọi lần đều dùng một đoạn code" như trong chương trình mà chúng ta đã tạo ở bài trước, Ví dụ:
- Một phần của đoạn code chuyển đổi View
- Phần quản lý các View lồng nhau
- Đoạn code trong việc lấy data từ server để Render ra View
Và như vậy, khi bạn sử dụng Marionette, chúng ta sẽ quản lý tốt hơn và tránh việc sử dụng các đoạn code tương tự nhau.
Thêm vào đó, khi quy mô ứng dụng trở nên lớn hơn, với chỉ backbone thì bạn phải quản lý quá nhiều View: View định nghĩa cho các layout tổng thể, định nghĩa các event, hiển thị collection, hiển thị các model...
Marionete đã hỗ trợ, cấu trúc View bằng cách sử dụng nhiều View và phân chia các hàm vào các khu vực tùy theo vai trò để dễ đọc, sẽ giúp bảo trì tốt hơn sau này.
Một đặc điểm thuận tiện nữa là UI
, Thời gian này mình chưa đụng đến nó, đó là những Triggers
và Behaviors
với những hàm hữu ích trong đó.
Có rất nhiều plug-in cho Backbone, tuy nhiên cái đầu tiên các bạn nên sử dụng có lẽ là Marionette.
Giới thiệu về các Object trong Marionette
Marionette được gọi trong MVC, nó có chức năng của controller và view.
Những Object được cung cấp trong backbone chắc chắn phải thay thế để có khả năng chạy được trong Marionette. Object của Marionette để sử dụng hiện tại sẽ giống như trong bảng dưới đây, chi tiết mỗi hàm của object tôi sẽ nói sau, đầu tiên chúng ta chỉ cần quan tâm đến:
Hệ thống Controller
Marionette Object | Tổng quan |
---|---|
Marionette.Application | Đây là điểm bắt đầu cho một ứng dụng sử dụng Marionette |
Marionette.AppRouter | Làm công việc Routing |
Marionette.Controller | Controller |
Hệ thống View
Marionette Object | Tổng quan |
---|---|
Marionette.Application | Chứa những định nghĩa, xác định các khu vực (region) |
Marionette.Region | Mỗi đối tượng dại diện cho một region. |
Marionette.LayoutView | Tập hợp các view của một layout |
Marionette.CollectionView | View để hiển thị các Collection thích hợp |
Marionette.CompositeView | Hiển thị collection |
Marionette.ItemView | Hiển thị từng Model |
Vị trí của các hàm trong mỗi view
View | Hàm region | Tác động đến template |
---|---|---|
Marionette.Application | Có | Không |
Marionette.LayoutView | Không | Có |
Marionette.CollectionView | Không | Không |
Marionette.CompositeView | Không | Có |
Marionette.ItemView | Không | Có |
Mối quan hệ giữa các object trong backbone
Mối quan hệ giữa Backbone object và Marionette object tương ứng như sau
Backbone | Marionette |
---|
-
|Marionette.Application|
Backbone.Router |Marionette.AppRouter
Marionette.Controller|
Backbone.View |Marionette.Region
Marionette.LayoutView
Marionette.CollectionView
Marionette.CompositeView
Marionette.ItemView
|
Nội dung phần này
Chương trình chúng ta đã làm ở bài trước bây giờ mình sẽ viết lại và sử dụng các object của Marionette.js.
Kết quả sẽ không khác gì cả, mình chỉ thay đổi cấu trúc code bên trong.
Đầu tiên là phần chuẩn bị:
Chuẩn bị
Login bằng SSH
Các bạn đăng nhập qua putty hoặc gõ lệnh sau vào terminal.
-
ssh -i [đường dẫn đến file private key] study@[serverのPublicIP]
Chạy lệnh cd
để vào thư mục mình đang làm việc rest-study
.
-
cd /var/www/study/rest-study
Bắt đầu thôi !
Sao lưu branch của git
Ở chương trước, chúng ta làm việc ở branch vol/02
. Sau khi hoàn thành bài học , branch vol/02
được push lên Github, vì vậy việc đầu tiên bạn cần làm là gộp branch này với branch master
để nhận kết quả cuối cùng. Để giữ phiên bản cũ hoạt động, bạn nên sửa đường dẫn của nó bằng cách tạo một folder mới và lưu nó vào đó.
Nội dung của phần 1 là bạn phải hoàn thành hết các điều kiện trên, hãy ghi nhớ.
- Đầu tiên bạn cần phải vào branch
master
.
git checkout master
Gộp branch master
với branch vol/02
để nhận kết quả của bài viết trước.
git merge vol/02
Đã gộp xong !
Tạo branch cho phiên làm việc này.
Vẫn là những thao tác như cũ.
Chúng ta tạo branch vol/03
và checkout nó.
: warning: Một khi đã tạo branch tên vol/03
từ branch master
, đầu tiên hãy chắc chắn bạn đang ở branch master
.
-
git branch
để chắc chắn bạn đang ở branchmaster
-
Nếu bạn đang không ở
master
, nhập lệnhgit checkout
để checkout vàomaster
Tạo branch vol/03
bằng các lệnh dưới đây
-
git branch vol/03
Tạo branch -
git checkout vol/03
chuyển đến branchvol/03
vừa tạo
Xác nhận
Có một lệnh giúp kiểm tra log các comit
-
hãy kiểm tra log các commit bằng
git log
để xác nhận các thay đổi
Kết quả sẽ ok nếu hiển thị commit ngay trước đó.
Sự chuẩn bị về branch đã xong.
git branch -a
và bạn xem tất cả các branch sẽ thấy hiện vol/03-finish
chứa tất cả các source của các giai đoạn nội dung đã hoàn thành, bạn hãy tham khảo để xem sự commit của mỗi bài học qua URL sau
Commits · suzukishouten-study/rest-study
Tải thư viện Marionette
Tải về từ trang chủ của Marionette Marionette.js – The Backbone Framework。
Ở dưới sẽ có link download, bạn đăng nhập vào server bằng SSH và sẽ thấy lệnh wget.
Hướng dẫn chạy wget bạn có thể tham khảo ở Bài viết trước.
-
Mình sẽ tải nó vào
/var/www/study/rest-study/app/webroot/js/lib
cd /var/www/study/rest-study/app/webroot/js/lib
wget http://marionettejs.com/downloads/backbone.marionette.min.js
Bây giờ thì bạn đã sẵn sàng!
Để hiểu chức năng của mỗi object trong Marionette, hãy xem kỹ hơn một tí.
Đầu tiên là bài 1, mình sẽ đặt tên là "Entry point - routing - controller", chương trình mà chúng ta đã tạo ở bài trước.
- Marionette.Application
- Marionette.AppRouter
- Marionette.Controller
Chúng ta cần xử lý cả 3 file trên
Bài 1 "entry point - routing - controller"
Lợi ích đầu tiên của việc sử dụng Marionette mà tôi thấy là phần view, nhưng tạm thời giữ không thay đổi những Entry point-routing-controller trước đây vì
Đây không phải là điểm mạnh của Marionette, chúng ta cần làm như sau:
-
Thay đổi entry point
-
Tách biệt router và controller
Chúng ta sẽ sửa đổi chương trình hiện tại và sử dụng những Marionette object dưới đây, -
Marionette.Application
-
Marionette.AppRouter
-
Marionette.Controller
###Những file cần sửa nội dung
Thao tác | file | tổng quan |
---|---|---|
Sửa | app/View/Layouts/default.ctp | HTML template |
Thêm | app/webroot/js/main.js | Nơi bắt đầu ứng dụng - entry point |
Sửa | app/webroot/js/app.js | Ứng dụng (Marionettte.Application) |
Sửa | app/webroot/js/routers/router.js | Router (Marionette.AppRouter) |
Thêm | app/webroot/js/routers/controller.js | Controller |
default.ctp
Đầu tiên là file này
<script src="js/lib/jquery-2.1.3.min.js" type="text/javascript"></script>
<script src="js/lib/underscore-min.js" type="text/javascript"></script>
<script src="js/lib/backbone-min.js" type="text/javascript"></script>
+ <script src="js/lib/backbone.marionette.min.js" type="text/javascript"></script>
<!-- js(application) -->
<script src="js/views/todo-item-view.js" type="text/javascript"></script>
<script src="js/views/todo-detail-view.js" type="text/javascript"></script>
<script src="js/views/todo-collection-view.js" type="text/javascript"></script>
+ <!-- controller -->
+ <script src="js/routers/controller.js" type="text/javascript"></script>
<!-- router -->
<script src="js/routers/router.js" type="text/javascript"></script>
- <!-- entry point -->
+ <!-- application -->
<script src="js/app.js" type="text/javascript"></script>
+ <!-- entry point -->
+ <script src="js/main.js" type="text/javascript"></script>
</body>
</html>
- Đây là phần đọc các file js mới được thêm vào, gồm có:
-
js/lib/backbone.marionette.min.js
- Thư viện Marionette. -
js/routers/controller.js
- Nó được hỗ trợ bởi Marionette, controller của chương trình, file này mình sẽ tạo sau. -
js/app.js
- Được hỗ trợ bởi Marionette, sự thực thi của các object trong ứng dụng.- Trước khi sửa chúng ta nên nhớ đây là entry point, chúng ta cần sửa sau khi chuyển main.js trở thành entry point đã.
-
js/main.js
- Entry Point, mình sẽ tạo sau đây.
-
main.js
Trước đây, app.js
là entry point. Với bài học mới này, mình sẽ chia file này thành 2 phần.
- main.js - Nó sẽ trở thành entry point.
- app.js - Nó sẽ trở thành object thừa kế
Marionette.Application
var app = app || {};
//Bắt đầu
(function(app) {
app.application = new app.Application();
app.application.start();
})(app);
Biến app.Application (như được định nghĩa ở app.js), chỉ dùng để chạy hàm start
.
app.js
Những gì chúng ta sẽ làm với app.js
được tóm tắt bằng 2 dòng dưới đây:
- Các điều hướng của router
- Chạy
Backbone.history.start()
để giám sát các sự kiện「hashchange」
var app = app || {};
//Bắt đầu
(function(app) {
var todoRouter = new app.TodoRouter();
Backbone.history.start();
})(app);
Khi bạn sử dụng Marionette, những quá trình được thực hiện trong các object được thừa kế từ Marionette.Application
, vì vậy chỉ có 1 định nghĩa của object trong app.js
, khai báo và thực hiện là sự biên dịch từ main.js
.
Những object trong các mô tả trên của file main.js
( những entry point và các file ) sẽ được khai báo và thực hiện.
//Bắt đầu
(function(app) {
- var todoRouter = new app.TodoRouter();
- Backbone.history.start();
+ app.Application = Backbone.Marionette.Application.extend({
+ initialize : function(){
+ new app.TodoRouter();
+ },
+
+ onStart : function(){
+ Backbone.history.start();
+ },
+ });
})(app);
Bạn sẽ làm khác trước một chút.
- Khởi tạo - Chạy Hàm
initialize
.- Tại đây router bắt đầu điều hướng.
- Hàm bắt đầu - Chạy hàm
onStart
.- Hàm này giúp mình giám sát sự kiện
hashchange
.
- Hàm này giúp mình giám sát sự kiện
router.js
Thay đổi router để kế thừa Marionette.AppRouter
.
Viết 1 chuẩn duy nhất của routing trong router, thực thi các hàm mà bạn đã viết trong Controller.
//router
(function(app) {
- app.TodoRouter = Backbone.Router.extend({
- routes : {
+ app.TodoRouter = Backbone.Marionette.AppRouter.extend({
+ //Tạo Controller
+ controller: new app.TodoController(),
+ //Cấu hình Routing
+ appRoutes : {
'' : 'todoLists',
'todo-lists' : 'todoLists',
'todo-lists/:id' : 'todoDetail'
},
-
- currentView : false,
-
- todoLists : function() {
- //Chuyển đến View của danh sách TODO
- this.removeCurrentView();
- this.nextView(app.TodoCollectionView);
- },
-
- todoDetail : function(id) {
- this.removeCurrentView();
- this.nextView(app.TodoDetailView, id);
- },
-
- nextView : function(View, option) {
- if (document.getElementById('#content') === null) {
- $('#main').append('<div id="content"/>');
- }
- this.currentView = new View(option);
- },
- removeCurrentView : function() {
- if (this.currentView) {
- this.currentView.remove();
- }
- }
-
});
})(app);
- Tất cả sự thực thi của 1 hàm để chạy sau khi routing nhằm chuyển controller và hủy router.
-
controller: new app.TodoController()
- Khởi tạo Controller
controller.js
Chúng ta phải di chuyển hàm cần thực thi sau khi routing vào đây, nội dung như ở dưới
var app = app || {};
//controller
(function(app) {
app.TodoController = Backbone.Marionette.Controller.extend({
currentView : false,
todoLists : function() {
//Thêm routing cho view của TODO list
this.removeCurrentView();
this.nextView(app.TodoCollectionView);
},
todoDetail : function(id) {
this.removeCurrentView();
this.nextView(app.TodoDetailView, id);
},
nextView : function(View, option) {
if (document.getElementById('#content') === null) {
$('#main').append('<div id="content"/>');
}
this.currentView = new View(option);
},
removeCurrentView : function() {
if (this.currentView) {
this.currentView.remove();
}
}
});
})(app);
Tóm tắt nội dung
-
app/View/Layouts/default.ctp
sửa như trên. -
app/webroot/js/main.js
tạo mới với nội dung như trên. -
app/webroot/js/app.js
sửa như trên. -
app/webroot/js/routers/router.js
sửa như trên. -
app/webroot/js/routers/controller.js
tạo mới với nội dung như trên. - Kiểm tra kết quả.
- Commit lên Git.
Link đến source hoàn chỉnh ( Github )
Chuyển sang bài 2 thôi.
Marionette sẽ xử lý phần view của mình cho sáng sủa dễ đọc hơn.
Bài 2: Áp dụng view của Marionette vào màn hình TODO list.
Marionette mạnh nhất trong việckhởi tạo View.
Đầu tiên mình sẽ sửa giao diện TODO list.
Như trên mình đã nói thì Marionette hỗ trợ 4 loại view, nhưng ở đây mình sẽ chỉ áp dụng những loại sau.
- LayoutView
- CompositeView
- ItemView
Qua đó mình sẽ sử dụng hàm Region mà Marionette.Application
và LayoutView
có sẵn.
Bây giờ mình sẽ xem thêm chi tiết về vai trò của từng View.
Mối quan hệ giữa Region và View
View
「View」được biết đến là là đơn vị nhỏ nhất hiển thị một dữ liệu nào đó, VD hiện tại khi hoàn thành bài trước chúng ta có các view sau.
- View hiển thị TODO 1(Model 1)
- View hiển thị TODO list
Điều đó có nghĩa là, Để hiển thị Model 1 thì cần có 1 view tương ứng được điều khiển ở Collection
.
図Khi nào thì điều này xảy ra.
Region
"Region" là một khu vực sẽ hiển thị view.
Với hàm Region, chúng ta có thể thay thế view để hiển thị ( chuyển giao diện )
Mặc dù ở lần trước mình đã phá hủy View trên chính nó, nhưng với hàm region, bạn có thể chuyển view qua lại trên Marionette.
Đặc trưng này mình sẽ sử dụng khi chuyển đổi qua lại giữa TODO list view và TODO Detail view.
Chúng ta sẽ xem bài 3 với việc chuyển đổi ( switching )
Ảnh minh họa về sự thay đổi
Tôi chưa thêm gì vào ứng dụng TODO list này cả, nhưng khi object cần hiển thị có số lượng ngày càng lớn, lúc đó bạn cần phải chuẩn bị rất nhiều vùng để hiển thị view của mỗi vai trò.
Sử dụng nhiều region
Đặc điểm của Region là có thể được tạo ra từ Marionette.Application trong object và Marionette.LayoutView.
Một mô hình điển hình như sau
- Marionette.Application sẽ tạo một region bao bên ngoài hầu hết các thành phần của ứng dụng.
- Đặt LayoutView vào region
- View mà hiển thị dữ liệu và các button hay những thứ tương tự sẽ là LayoutView
- Cũng tạo thêm nhiều Region ở LayoutView
- View mà hiển thị dữ liệu và các button hay những thứ tương tự sẽ ở LayoutView
- Cũng tạo thêm nhiều Region ở LayoutView
- Đặt LayoutView vào region
Đây gọi là cấu trúc lồng nhau.
View được đặt vào region sẽ luôn được viết để sử dụng LayoutView, tuy nhiên sẽ có vài trang không cần sử dụng region này nên sẽ tốt hơn nếu đặt nó trực tiếp vào CompositeView và ItemView chứ không phải LayoutView. Cái này gọi là cơ sở case-by-case.
Cụ thể hơn, bạn hãy xem hình ảnh dưới đây.
- Để quản lý các region dưới bằng Marionette.Application object.
- Region hiển thị Header
- Nó sẽ thêm vào một View hiển thị header.
- Region hiển thị content
- Để chuyển đổi view có thể dùng được cả bên trong (LayoutView Hiển thị content X / LayoutView Hiển thị content Y )
- Region hiển thị footer
- Nó sẽ thêm vào một View hiển thị footer.
- Region hiển thị Header
- LayoutView Hiển thị content X
- Region hiển thị menu button
- Nó sẽ thêm những menu botton để hiển thị trên LayoutView
- Region hiển thị danh sách dữ liệu
- Nó sẽ chèn danh sách dữ liệu vào và hiển thị trên LayoutView
- Region hiển thị button chuyển trang
- Nó sẽ chèn vào một button chuyển trang để hiển thị trong LayoutView
- Region hiển thị menu button
〜Dưới đây tương tự như vậy〜
Chú ý
Chú ý này để giúp bạn hiểu hơn vai trò của mỗi View trong Marionette như dưới đây.
- Region
- LayoutView
- CompositeView
- ItemView
Dưới đây sẽ là một cấu trúc lồng nhau với thứ tự như trên.
Bây giờ mình sẽ bắt đầu sửa các file trong chương trình với các hướng dẫn dưới đây
###Những file cần sửa nội dung
Thao tác | file | Mô tả |
---|---|---|
Sửa | app/View/Layouts/default.ctp | HTML template |
Sửa | app/webroot/js/app.js | Ứng dụng (Marionettte.Application) |
Sửa | app/webroot/js/routers/controller.js | Controller |
Thêm mới | app/webroot/js/views/todo-layout-view.js | View(LayoutView:Todo-list Layout) |
Thêm mới | app/webroot/js/views/todo-composite-view.js | View(CompositeView: Danh sách Todo-list) |
Sửa | app/webroot/js/views/todo-item-view.js | View (ItemView:Hiển thị mỗi TODO trong TODO-List) |
Xóa | app/webroot/js/views/todo-collection-view.js | View (Danh sách Todo list) |
default.ctp
Nơi các template của LayoutView tăng lên.
<body>
<!-- Nội dung -->
<div id="main"></div>
- <!-- Template của TODO-list -->
- <script type="text/template" id="list-template">
+
+ <!-- TODO list layout templates -->
+ <script type="text/template" id="todo-layout-template">
<h1>TODO List</h1>
+ <div id="todo-lists"></div>
+ </script>
+
+ <!-- Template của TODO list -->
+ <script type="text/template" id="todo-composite-template">
<textarea style="width:300px;height:50px"id="new-todo" placeholder="Todo?" autofocus></textarea>
<input type="button" id="addTodo" value="Add Todo">
<hr>
<div>
<table border="1" width="350px">
- <tbody id="todo-lists"></tbody>
+ <tbody></tbody>
</table>
</div>
</script>
<!-- template của Mỗi dòng TODO (Cái được thêm vào phần tbody trên kia) -->
- <script type="text/template" id="item-template">
+ <script type="text/template" id="todo-item-template">
<td><input type="checkbox" class="toggle" <%- status === '1' ? 'checked' : '' %>></td>
<td style="margin:0px">
<span class="todo-edit" style="margin:0px"><%- todo %></span>
<a class="detail-link" href="#todo-lists/<%- id %>">Detail</a>
</td>
</script>
<!-- Màn hình detail -->
<script type="text/template" id="detail-template">
<h2>Todo #<%- id %></h2>
<!-- view -->
<script src="js/views/todo-item-view.js" type="text/javascript"></script>
<script src="js/views/todo-detail-view.js" type="text/javascript"></script>
- <script src="js/views/todo-collection-view.js" type="text/javascript"></script>
+ <script src="js/views/todo-composite-view.js" type="text/javascript"></script>
+ <script src="js/views/todo-layout-view.js" type="text/javascript"></script>
<!-- controller -->
<script src="js/routers/controller.js" type="text/javascript"></script>
<!-- router -->
-
Template của TODO list được chia ra thành các phần nhỏ để sử dụng một phần của CompositeView và cả để sử dụng trong LayoutView.
- Tất cả template cho TODO list, cho layout, cho list, sẽ chỉ có 1 và ID thay đổi tương tương ứng như dưới đây.
- Cho Layout->
todo-layout-template
- Cho list->
todo-composite-template
- Cho mỗi việc trong list ->
todo-item-template
- Cho Layout->
- Tất cả template cho TODO list, cho layout, cho list, sẽ chỉ có 1 và ID thay đổi tương tương ứng như dưới đây.
-
LayoutView chứa thẻ của các phần trong region, đó sẽ là
todo-lists
id (view cho danh sách hiển thị sẽ được load ). -
View cho danh sách TODO list (
todo-collection-view.js
) sẽ được sửa thànhtodo-composite-view.js
. -
View cho layout, mình sẽ tạo file mới
js/views/todo-layout-view.js
.
app.js
onStart : function(){
Backbone.history.start();
},
+
+ regions : {
+ mainRegion : '#main'
+ }
+
});
-
Marionette.Applicaton
đã có, chúng ta đã thêm cấu hình của hàm region. Cụ thể hơn thẻ<= div id "main"></ div>
củadefault.ctp
đã sẵn sàng để xử lý các region.
controller.js
Khi vẽ hoặc hủy 1 view sử dụng hàm region của Marionette thì code của phần đó sẽ bị xóa trong chương trình để không chạy nữa.
(function(app) {
app.TodoController = Backbone.Marionette.Controller.extend({
- currentView : false,
-
todoLists : function() {
- //chuyển đến view của danh sách TODO
- this.removeCurrentView();
- this.nextView(app.TodoCollectionView);
+ //routing view cho TODO layout
+ this.nextView(app.TodoLayoutView);
},
todoDetail : function(id) {
- this.removeCurrentView();
this.nextView(app.TodoDetailView, id);
},
nextView : function(View, option) {
- if (document.getElementById('#content') === null) {
- $('#main').append('<div id="content"/>');
- }
- this.currentView = new View(option);
+ app.application.mainRegion.show(new View(option));
},
- removeCurrentView : function() {
- if (this.currentView) {
- this.currentView.remove();
- }
- }
});
})(app);
- Hàm removeCurrentView(Xóa)
- Bằng hàm region của Marionette, bạn sẽ quản lý được sự gia tăng của view bằng cách xóa những view không sử dụng nữa, nó sẽ xóa các định nghĩa và phần gọi của view mà bạn đang làm việc trên nó bằng hàm
removeCurrentView
.
- Bằng hàm region của Marionette, bạn sẽ quản lý được sự gia tăng của view bằng cách xóa những view không sử dụng nữa, nó sẽ xóa các định nghĩa và phần gọi của view mà bạn đang làm việc trên nó bằng hàm
- Biến nextView
- Mặc dù từ đầu mình chỉ làm việc tạo view sử dụng đặc điểm Region, giờ mình sẽ thay đổi thành "tạo 1 view, region view ấy như một argument để show ra"
todo-layout-view.js
Đây là là layout cho view, vai trò của nó như sau
- Định nghĩa Region
- Đọc collection
- Việc đọc Collection đã được thực hiện ở
todo-collection-view.js
gốc, vì vậy bạn sẽ làm việc ở file này.
- Việc đọc Collection đã được thực hiện ở
var app = app || {};
//View layout cho TODO list
(function(app) {
app.TodoLayoutView = Backbone.Marionette.LayoutView.extend({
//template
template: '#todo-layout-template',
regions : {
listRegion : '#todo-lists',
},
onRender : function(){
var todoCollection = new app.TodoCollection();
this.listenTo(todoCollection , 'reset', this.showTodoList, this);
todoCollection.fetch({reset : true});
},
showTodoList : function(todoCollection){
this.listRegion.show( new app.TodoCompositeView({
collection : todoCollection
}));
},
});
})(app);
- Biến template
- Biến
Template
giữ cài đặt của ID cho Template, nó sẽ tự động được áp dụng một template mà Marionette được trỏ đến bởi ID trong thời gian vẽ.
- Biến
- Biến regions
- Giúp xác định ID của các tag để sử dụng như một region. Tại đây bạn sẽ không thể xác định được vùng hiển thị danh sách
# todo-lists
.
- Giúp xác định ID của các tag để sử dụng như một region. Tại đây bạn sẽ không thể xác định được vùng hiển thị danh sách
- Hàm onRender
- Nó sẽ thực thi ở tất cả các view của Marionette và tự động chạy khi view được sinh ra.
- Tại đây chúng ta sẽ get dữ liệu của collection, mình phải chạy hàm
showTodoList
tại thời điểm lấy xong dữ liệu.
- Hàm showTodoList
-
This.listenTo
được xác định trongonRender
qua hàm (todoCollection, 'reset', this.showTodoList, this);, nó được thực thi tại thời điểm sự kiện
reset` của collection được thực hiện ( nội dung của collection được làm mới). - Biến
regions
được set bởilistRegion
, và được trỏ đến bởi hàmshow
. Chạy qua object mới củaapp.TodoCompositeView
biểu thức của hàmshow
, nó được set như là 1 view ởlistRegion
.
-
todo-composite-view.js
Có một view mà được hiển thị ở region trong listRegion
ở todo-layout-view.js
bên trên.
Bây giờ mình sẽ sử dụng CompositeView.
Mình cần chú ý đến các thành phần childView
,childViewContainer
và ui
.
button "Add Todo" và các hàm xử lý sự kiện đều được viết ở todo-collection-view.js
.
var app = app || {};
//View cho TODO list
(function(app) {
app.TodoCompositeView = Backbone.Marionette.CompositeView.extend({
template: '#todo-composite-template',
childView : app.TodoItemView,
childViewContainer : 'tbody',
ui : {
addTodo : '#addTodo',
newTodo : '#new-todo'
},
events : {
'click @ui.addTodo' : 'onCreateTodo',
},
initialize: function(){
_.bindAll( this, 'onCreatedSuccess' );
},
onCreateTodo : function(e) {
this.collection.create(this.newAttributes(), {
silent: true ,
success: this.onCreatedSuccess
});
this.ui.newTodo.val('');
},
newAttributes : function() {
return {
todo : this.ui.newTodo.val().trim(),
status : 0
};
},
onCreatedSuccess : function(){
this.collection.fetch({ reset : true });
},
});
})(app);
-
Biến childView
- Trong
CompositeView
mình xác địnhItemView
như là một view con. Ở backbone nguyên thủy mình sẽ diễn tả việc vẽ các view con trong view cha, trong khi với việc sử dụngCompositeView
mình sẽ không cần viết như vậy quá lộn xộn mà chương trình sẽ chạy tốt đơn giản chỉ cần xác định ItemView là view con.
- Trong
-
Biến childViewContainer
- Đây là container để vẽ những ItemView thành những view con, bạn sẽ xác định ID của các thành phần HTML trên template. Chúng ta đã xác định được thẻ
tbody
.
- Đây là container để vẽ những ItemView thành những view con, bạn sẽ xác định ID của các thành phần HTML trên template. Chúng ta đã xác định được thẻ
-
Biến
ui
- Biến
UI
là một biến có sẵn trong Marionette. Cú pháp của nó là "tên-biến: ID trên HTML DOM", với view này bạn có thể dễ dàng quản lý tên biến mà bạn chỉ định.- Nếu bạn muốn chỉ định lựa chọn của biến trong sự kiện, bạn sẽ có thể xác định được định dạng của nó bằng
@ui.tên-biến
. Tại đây tôi sử dụng'click @ui.addTodo': 'onCreateTodo'
.
- Nếu bạn muốn chỉ định lựa chọn của biến trong sự kiện, bạn sẽ có thể xác định được định dạng của nó bằng
- Biến
-
Todo-collection-view.js
đã được mô tả, Các hàm xử lý sự kiệnonCreateTodo
,newAttributes
,onCreatedSuccess
sẽ được viết ngay sau đây.
todo-item-view.js
Chúng ta áp dụng vào ItemView
Tương tự thì bạn có thể sử dụng biến ui
ở Todo-composite-view.js
.
Mình sẽ xóa phần vẽ template ItemView
hiện tại.
//Hiển thị mỗi view của TODO List
(function(app) {
- app.TodoItemView = Backbone.View.extend({
+ app.TodoItemView = Backbone.Marionette.ItemView.extend({
//Thẻ html của phần tử được thêm vào DOM
tagName : 'tr',
//template
- template : _.template($('#item-template').html()),
+ template : '#todo-item-template',
+
+ ui : {
+ checkBox : '.toggle',
+ removeLink : '.remove-link'
+ },
//Xử lý các sự kiện trong DOM
events : {
//Click vào checkbox
- 'click .toggle' : 'onStatusToggleClick',
+ 'click @ui.checkBox' : 'onStatusToggleClick',
//Khi click vào nút Xóa
- 'click .remove-link' : 'onRemoveClick',
+ 'click @ui.removeLink' : 'onRemoveClick',
},
- initialize : function() {
- this.listenTo(this.model, 'destroy', this.remove);
- },
- render : function() {
- this.$el.html(this.template(this.model.toJSON()));
- return this;
- },
onStatusToggleClick : function(e) {
this.model.toggle();
},
onRemoveClick : function(e) {
this.model.destroy({
wait : true
});
},
});
})(app);
- Biến UI, Biến events
- Mình đã viết lại
todo-composite-view.js
cho tốt hơn.
- Mình đã viết lại
- Hàm initialize, Hàm render
- ItemView sẽ làm công việc vẽ.
todo-collection-view.js
Để phù hợp với chỉnh sửa, tên file của view cần phù hợp với tên view của Marionette. vì vậy để sử dụng CompositeView
,
thì Todo-collection-view.js
phải bị xóa.
Tóm tắt bài học
Các bước
-
app/View/Layouts/default.ctp
sửa như hướng dẫn trên. -
app/webroot/js/app.js
sửa như hướng dẫn trên. -
app/webroot/js/routers/controller.js
sửa như hướng dẫn trên. -
app/webroot/js/views/todo-layout-view.js
thêm mới với nội dungn như trên. -
app/webroot/js/views/todo-composite-view.js
thêm mới với nội dungn như trên. -
app/webroot/js/views/todo-item-view.js
sửa như hướng dẫn trên. -
app/webroot/js/views/todo-collection-view.js
xóa đi。 - Kiểm tra kết quả
- Commit lên Git
Link tham khảo source sau khi đã hoàn thành
Chuyển sang bài 3
Bài 3: Áp dụng view của Marionette vào màn hình TODO detail
Ở màn hình TODO detail, bạn có thể sửa bằng cách áp dụng view của Marionette.
Tôi nghĩ rằng nó có thể sửa gần như toàn bộ nội dung của bài 2.
###Những file cần sửa nội dung
Thao tác | file | Ý nghĩa |
---|---|---|
Sửa | app/View/Layouts/default.ctp | HTM template |
Sửa | app/webroot/js/routers/controller.js | Controller |
Thêm mới | app/webroot/js/views/todo-detail-layout-view.js | View (LayoutView: Layout của màn hình chi tiết mỗi công việc TODO) |
Thêm mới | app/webroot/js/views/todo-detail-item-view.js | View(ItemView: view từng TODO) |
Xóa | app/webroot/js/views/todo-detail-view.js | View(trang detal của mỗi TODO) |
default.ctp
Màn hình detail đã được thực hiện ở View, nhưng tôi sẽ chia nó thành LayoutView và ItemView
</td>
</script>
- <!-- màn hình detail -->
- <script type="text/template" id="detail-template">
+ <!-- layout template của màn hình detail -->
+ <script type="text/template" id="todo-detail-layout-template">
+ <div id="todo-item"></div>
+ </script>
+ <!-- Nội dung trang detail -->
+ <script type="text/template" id="todo-detail-item-template">
<h2>Todo #<%- id %></h2>
<div>
<textarea style="width:300px;height:50px" id="edit-todo" autofocus placeholder="Todo?"><%- todo %></textarea>
<script src="js/collections/todo-collection.js" type="text/javascript"></script>
<!-- view -->
<script src="js/views/todo-item-view.js" type="text/javascript"></script>
- <script src="js/views/todo-detail-view.js" type="text/javascript"></script>
+ <script src="js/views/todo-detail-item-view.js" type="text/javascript"></script>
+ <script src="js/views/todo-detail-layout-view.js" type="text/javascript"></script>
<script src="js/views/todo-composite-view.js" type="text/javascript"></script>
<script src="js/views/todo-layout-view.js" type="text/javascript"></script>
<!-- controller -->
-template của trang detail sẽ được chia thành các phần để sử dụng và ItemView sẽ được sử dụng trongLayoutView.
- Dùng LayoutView -> todo-detail-layout-template
- Dùng ItemView -> todo-detail-item-template
- View dùng cho layout, thêm tag của các phần trong region, ID của
todo-item
( Tại đây thì view của trang detail (ItemView) sẽ được load). - View cho trang detail (
todo-detail-view.js
) đã được thay đổi thànhtodo-detail-item-view.js
. - View cho layout, bạn cần thêm file
js/views/todo-detail-layout-view.js
.
controller.js
Mình sẽ thay đổi phương pháp thông qua ID của view.
},
todoDetail : function(id) {
- this.nextView(app.TodoDetailView, id);
+ this.nextView(app.TodoDetailLayoutView, {modelId : id});
},
nextView : function(View, option) {
app.application.mainRegion.show(new View(option));
},
});
})(app);
- Hàm
todoDetail
- Bạn đang chạy hàm
nextView
, nhưng nó đã thông qua ID từ trước và đây là biểu thức thứ 2. Thay đổi thành{modelId: id}
và bạn thông qua object. Nó đã được sử dụng như là một biểu thức của hàm khởi tạo gốc ở phần View, nó được thay đổi để trỏ đến View của biếnoptions
để sử dụng.
- Bạn đang chạy hàm
todo-detail-layout-view.js
Đây là view dành cho layout.
Vai trò của nó như sau
- Định nghĩa Region
- Đọc model
- Việc đọc model được thự hiện ở file
todo-detail-view.js
gốc, vì vậy mình làm việc ở đây.
- Việc đọc model được thự hiện ở file
var app = app || {};
//Layout View của trang Detail
(function(app) {
app.TodoDetailLayoutView = Backbone.Marionette.LayoutView.extend({
//Template
template : '#todo-detail-layout-template',
regions : {
itemRegion : '#todo-item',
},
onRender : function() {
var todoModel = new app.TodoModel({
id : this.options.modelId
});
//Tại thời điểm nhận hết data từ server sẽ bắt đầu hiển thị các item
this.listenTo(todoModel, 'sync', this.showItem, this);
//Tổng hợp dữ liệu từ server
todoModel.fetch({
wait : true
});
},
showItem : function(todoModel) {
this.itemRegion.show( new app.TodoDetailItemView({
model : todoModel
}));
},
});
})(app);
- Biến regions
- Bạn sẽ xác định
#todo-item
như là một region.
- Bạn sẽ xác định
- Hàm onRender
-
Xác định
this.options.modelId, bạn lấy ID của model được xác định trong
controller.js`.- Object được xác định tại thời điểm sinh ra View , trong View, có thể trỏ đến như là
this.options
.
- Object được xác định tại thời điểm sinh ra View , trong View, có thể trỏ đến như là
-
- Việc gọi các view con từ database khác với giữa
collection
vàmodel
, nhưng tôi sẽ viết theo cùng một cách.
todo-detail-item-view.js
Áp dụng vào ItemView
var app = app || {};
//View trang detail
(function(app) {
app.TodoDetailItemView = Backbone.Marionette.ItemView.extend({
//Template
template: "#todo-detail-item-template",
ui : {
todoStatus : '#edit-todo',
updateButton : '#updateTodo',
cancelButton : '#updateCancel'
},
//Xử lý các sự kiện trong DOM
events : {
//Click nút chỉnh sửa
'click @ui.updateButton' : 'onUpdateClick',
//Click nút Cancel
'click @ui.cancelButton' : 'onCancelClick',
},
//Khởi tạo
initialize: function(){
_.bindAll( this, 'onSaveSuccess' );
},
//Xử lý sự kiện click nút Update
onUpdateClick : function() {
//Lấy ký tự từ checkbox
var todoString = this.ui.todoStatus.val();
this.model.save({
todo : todoString
}, {
silent : true,
success : this.onSaveSuccess,
});
},
//Xử lý sự kiện click nút Cancel
onCancelClick : function() {
this.backTodoLists();
},
//Sửa thành công
onSaveSuccess : function() {
this.backTodoLists();
},
//Trở lại màn hình danh sách TODO
backTodoLists : function() {
Backbone.history.navigate('#todo-lists', true);
}
});
})(app);
Chỉ liệt kê những phần sửa vì những phần còn lại nội dung không khác gì bài 2 nên tôi sẽ bỏ qua.
todo-collection-view.js
Vì tên view cần khớp với tên view của Marionette, Mình sẽ xóa file todo-detail-view.js
.
Tóm tắt bài học
-
app/View/Layouts/default.ctp
Sửa theo hướng dẫn trên -
app/webroot/js/routers/controller.js
Sửa theo hướng dẫn trên -
app/webroot/js/views/todo-detail-layout-view.js
tạo file theo hướng dẫn trên. -
app/webroot/js/views/todo-detail-item-view.js
tạo file theo hướng dẫn trên. -
Xóa file
app/webroot/js/views/todo-detail-view.js
. - Xác nhận kết quả
- Commit lên Git
Tài nguyên tham khảo khi hoàn tất (GitHub
Chúng ta đã xong.
Chúc các bạn thành công
Hi vọng mọi thứ diễn ra suôn sẻ
Cuối cùng, chúng tôi tổng hợp lại những đặc điểm chính của Marionette mà mình đã sử dụng trong bài này, nếu bạn đã hiểu rồi cũng nên xem lại một lần nữa.
Tóm tắt những đặc điểm của Marionette đã sử dụng trong bài viết
- Object
- Marionette.Application
- Định nghĩa các region và là nơi bắt đầu ứng dụng
- Marionette.AppRouter
- Làm nhiệm vụ Routing
- Marionette.Controller
- Định nghĩa các hàm sẽ chạy sau khi routing.
- Marionette.Region
- Nơi quản lý từng view
- Marionette.LayoutView
- Nó sẽ là view bao trọn các view khác trong region, region cũng có thể được định nghĩa ở đây.
- Bạn có thể sử dụng template.
- Nó sẽ là view bao trọn các view khác trong region, region cũng có thể được định nghĩa ở đây.
- Marionette.CollectionView
- ※ Chúng ta không sử dụng trong bài này, nó chứa những thứ nằm ngoài hàm template từ CompositeView.
- Bạn có thể sẽ cần đến chúng nếu thực sự thích nó (Giới thiệu list box ) chỉ để xem cái gì bên trong ItemView.
- ※ Chúng ta không sử dụng trong bài này, nó chứa những thứ nằm ngoài hàm template từ CompositeView.
- Marionette.CompositeView
- Sử dụng để hiển thị collection. Kèm theo các ItemView cho mỗi hiển thị ở collection.
- Bạn có thể dử dụng template, đặt các button để quản lý collection ( button Thêm,... )
- Sử dụng để hiển thị collection. Kèm theo các ItemView cho mỗi hiển thị ở collection.
- Marionette.ItemView
- Sử dụng để hiển thị model.
- Bạn có thể sử dụng template, đặt các button để quản lý collection ( button Refrest,... )
- Sử dụng để hiển thị model.
- Marionette.Application
- View ở các hàm riêng biệt
- Biến
ui
- Các mô tả lựa chọn của HTML trong view được tập trung ở 1 nơi.
- Có thể đọc từ view với formart
@ui.〜
- Biến regions
- Định nghĩa region bạn muốn quản lý
- Biến template
- Định nghĩa bằng cách giữ ID của template, có có thể tự động render.
- Biến options
- Có thể trỏ đến object đã được thông qua trong quá trình khởi tạo view.
- Biến
Cái mình không sử dụng
triggers
、Behaviors
、etc...
Rất đa dạng, bạn hãy tự kiểm tra nhé
Đó là tất cả những gì tôi muốn nói trong bài viết này.
Tôi đang chờ đợi những bình luận và góp ý của các bạn
Cảm ơn các bạn đã đọc bài viết này.