MVVM模式不但可用于Form表單,在復雜的管理頁面中也能大顯身手。例如,分頁顯示Blog的功能,我們先把后端代碼寫出來:
在`apis.py`中定義一個`Page`類用于存儲分頁信息:
~~~
class Page(object):
def __init__(self, item_count, page_index=1, page_size=10):
self.item_count = item_count
self.page_size = page_size
self.page_count = item_count // page_size + (1 if item_count % page_size > 0 else 0)
if (item_count == 0) or (page_index > self.page_count):
self.offset = 0
self.limit = 0
self.page_index = 1
else:
self.page_index = page_index
self.offset = self.page_size * (page_index - 1)
self.limit = self.page_size
self.has_next = self.page_index < self.page_count
self.has_previous = self.page_index > 1
def __str__(self):
return 'item_count: %s, page_count: %s, page_index: %s, page_size: %s, offset: %s, limit: %s' % (self.item_count, self.page_count, self.page_index, self.page_size, self.offset, self.limit)
__repr__ = __str__
~~~
在`handlers.py`中實現API:
~~~
@get('/api/blogs')
def api_blogs(*, page='1'):
page_index = get_page_index(page)
num = yield from Blog.findNumber('count(id)')
p = Page(num, page_index)
if num == 0:
return dict(page=p, blogs=())
blogs = yield from Blog.findAll(orderBy='created_at desc', limit=(p.offset, p.limit))
return dict(page=p, blogs=blogs)
~~~
管理頁面:
~~~
@get('/manage/blogs')
def manage_blogs(*, page='1'):
return {
'__template__': 'manage_blogs.html',
'page_index': get_page_index(page)
}
~~~
模板頁面首先通過API:`GET /api/blogs?page=?`拿到Model:
~~~
{
"page": {
"has_next": true,
"page_index": 1,
"page_count": 2,
"has_previous": false,
"item_count": 12
},
"blogs": [...]
}
~~~
然后,通過Vue初始化MVVM:
~~~
<script>
function initVM(data) {
var vm = new Vue({
el: '#vm',
data: {
blogs: data.blogs,
page: data.page
},
methods: {
edit_blog: function (blog) {
location.assign('/manage/blogs/edit?id=' + blog.id);
},
delete_blog: function (blog) {
if (confirm('確認要刪除“' + blog.name + '”?刪除后不可恢復!')) {
postJSON('/api/blogs/' + blog.id + '/delete', function (err, r) {
if (err) {
return alert(err.message || err.error || err);
}
refresh();
});
}
}
}
});
$('#vm').show();
}
$(function() {
getJSON('/api/blogs', {
page: {{ page_index }}
}, function (err, results) {
if (err) {
return fatal(err);
}
$('#loading').hide();
initVM(results);
});
});
</script>
~~~
View的容器是`#vm`,包含一個table,我們用`v-repeat`可以把Model的數組`blogs`直接變成多行的``:
~~~
<div id="vm" class="uk-width-1-1">
<a href="/manage/blogs/create" class="uk-button uk-button-primary"><i class="uk-icon-plus"></i> 新日志</a>
<table class="uk-table uk-table-hover">
<thead>
<tr>
<th class="uk-width-5-10">標題 / 摘要</th>
<th class="uk-width-2-10">作者</th>
<th class="uk-width-2-10">創建時間</th>
<th class="uk-width-1-10">操作</th>
</tr>
</thead>
<tbody>
<tr v-repeat="blog: blogs" >
<td>
<a target="_blank" v-attr="href: '/blog/'+blog.id" v-text="blog.name"></a>
</td>
<td>
<a target="_blank" v-attr="href: '/user/'+blog.user_id" v-text="blog.user_name"></a>
</td>
<td>
<span v-text="blog.created_at.toDateTime()"></span>
</td>
<td>
<a href="#0" v-on="click: edit_blog(blog)"><i class="uk-icon-edit"></i>
<a href="#0" v-on="click: delete_blog(blog)"><i class="uk-icon-trash-o"></i>
</td>
</tr>
</tbody>
</table>
<div v-component="pagination" v-with="page"></div>
</div>
~~~
往Model的`blogs`數組中增加一個Blog元素,table就神奇地增加了一行;把`blogs`數組的某個元素刪除,table就神奇地減少了一行。所有復雜的Model-View的映射邏輯全部由MVVM框架完成,我們只需要在HTML中寫上`v-repeat`指令,就什么都不用管了。
可以把`v-repeat="blog: blogs"`看成循環代碼,所以,可以在一個``內部引用循環變量`blog`。`v-text`和`v-attr`指令分別用于生成文本和DOM節點屬性。
完整的Blog列表頁如下:

### 參考源碼
[day-12](https://github.com/michaelliao/awesome-python3-webapp/tree/day-12)
- 關于
- Python簡介
- 安裝Python
- Python解釋器
- 第一個Python程序
- 使用文本編輯器
- Python代碼運行助手
- 輸入和輸出
- Python基礎
- 數據類型和變量
- 字符串和編碼
- 使用list和tuple
- 條件判斷
- 循環
- 使用dict和set
- 函數
- 調用函數
- 定義函數
- 函數的參數
- 遞歸函數
- 高級特性
- 切片
- 迭代
- 列表生成式
- 生成器
- 迭代器
- 函數式編程
- 高階函數
- 返回函數
- 匿名函數
- 裝飾器
- 偏函數
- 模塊
- 使用模塊
- 安裝第三方模塊
- 面向對象編程
- 類和實例
- 訪問限制
- 繼承和多態
- 獲取對象信息
- 實例屬性和類屬性
- 面向對象高級編程
- 使用slots
- 使用@property
- 多重繼承
- 定制類
- 使用枚舉類
- 使用元類
- 錯誤、調試和測試
- 錯誤處理
- 調試
- 單元測試
- 文檔測試
- IO編程
- 文件讀寫
- StringIO和BytesIO
- 操作文件和目錄
- 序列化
- 進程和線程
- 多進程
- 多線程
- ThreadLocal
- 進程 vs. 線程
- 分布式進程
- 正則表達式
- 常用內建模塊
- datetime
- collections
- base64
- struct
- hashlib
- itertools
- XML
- HTMLParser
- urllib
- 常用第三方模塊
- PIL
- virtualenv
- 圖形界面
- 網絡編程
- TCP/IP簡介
- TCP編程
- UDP編程
- 電子郵件
- SMTP發送郵件
- POP3收取郵件
- 訪問數據庫
- 使用SQLite
- 使用MySQL
- 使用SQLAlchemy
- Web開發
- HTTP協議簡介
- HTML簡介
- WSGI接口
- 使用Web框架
- 使用模板
- 異步IO
- 協程
- asyncio
- aiohttp
- 實戰
- Day 1 - 搭建開發環境
- Day 2 - 編寫Web App骨架
- Day 3 - 編寫ORM
- Day 4 - 編寫Model
- Day 5 - 編寫Web框架
- Day 6 - 編寫配置文件
- Day 7 - 編寫MVC
- Day 8 - 構建前端
- Day 9 - 編寫API
- Day 10 - 用戶注冊和登錄
- Day 11 - 編寫日志創建頁
- Day 12 - 編寫日志列表頁
- Day 13 - 提升開發效率
- Day 14 - 完成Web App
- Day 15 - 部署Web App
- Day 16 - 編寫移動App
- FAQ
- 期末總結