【IT168技术】在上一讲中,我们假设读者已经对jQuery或者jQuery Mobile有一定的认识,而且讲述了如何开发jQuery Mobile记事本应用。其中jQuery Mobile框架在http://jquerymobile.com 可以获得下载。如果读者对jQuery Mobile基础知识不大了解,可以参考如下的几篇文章:
统一接口工具JQuery Mobile简介
http://tech.it168.com/a2010/1210/1136/000001136835.shtml
使用JQuery Mobile实现手机新闻浏览器
http://tech.it168.com/a2011/0321/1168/000001168231.shtml
JQuery Mobile实现手机新闻浏览器(2)
http://tech.it168.com/a2011/0323/1169/000001169682.shtml
使用jQuery Mobile实现新闻浏览器(3)
http://tech.it168.com/a2011/0324/1170/000001170077.shtml
在本讲中,将会实现如下的几个目标:
1、当用户在新增记事时,如果记事内容不完整,系统应给予用户友好的提示。
2、实现删除按钮的功能。
3、创建自定义的样式,改变系统默认的对话框的样式。
我们能期望达到如下的效果:
验证输入记事的合法性
在上一讲中,当我们要新保存新的记事时,只是针对标题和内容在保存前进行了是否为空的校验,回忆下相关代码如下:
"use strict";
if (this.title && this.title.length > 0) {
return true;
}
return false;
};
并且在onSaveNoteButtonTapped的方法中,我们上一讲并没有对校验失败时,应该如何提示通知用户,所以这里我们使用jQuery mobile框架中的对话框,其中有一个标题和内容文本提示即可,如下图:
如何设计jQuery Mobile对话框
在jQuery Mobile中,使用对话框很简单,只需要添加data-role属性为dialog即可,在index.html中添加如下代码:
<div id="invalid-note-dialog" data-role="dialog" data-title="Invalid Note" data-theme="e">
<div data-role="header" data-theme="e">
<h1>Wait!</h1>
</div>
<div data-role="content">Enter a title for this note.</div>
</div>
接下来,我们还要在控制层中添加对话框的标识,
我们将修改onSaveNoteButtonTapped事件的代码如下:
var onSaveNoteButtonTapped = function () {
// 校验记事
var titleEditor = $(noteTitleEditorSel);
var narrativeEditor = $(noteNarrativeEditorSel);
var tempNote = dataContext.createBlankNote();
tempNote.title = titleEditor.val();
tempNote.narrative = narrativeEditor.val();
if (tempNote.isValid()) {
if (null !== currentNote) {
currentNote.title = tempNote.title;
currentNote.narrative = tempNote.narrative;
} else {
currentNote = tempNote;
}
dataContext.saveNote(currentNote);
returnToNotesListPage();
} else {
$.mobile.changePage(invalidNoteDlgSel, defaultDlgTrsn);
}
};
这里,请注意$.mobile.changePage(invalidNoteDlgSel, defaultDlgTrsn);
这句。在这里,我们使用了 $mobile.changePage方法,这个方法能动态地触发页面改变。这个方法有两个参数,第一个参数是要出现的页面的id(这里就是对话框的id),而第二个参数则是页面切换的效果,这里使用的过渡切换效果如下定义:
var defaultDlgTrsn = { transition: "slideup" };
mobileinit事件的放置位置
在上一讲中,我们在index.html中编写了mobileinit事件的代码,但这个习惯不是很好,我们不希望在html页中出现跟逻辑相关的代码,因此我们重构一下,将mobileinit事件的代码放到Controller.js这个文件中进行处理,代码如下:
//省略了详细的contoller代码,具体见附件
} (jQuery, Notes.dataContext, document));
$(document).bind("mobileinit", function () {
Notes.controller.init();
});
现在,index.html代码中,则只是引用了相关的JS文件了,不会再象上一讲中,在index.html中混杂了js代码。
设计删除记事功能
接下来,我们要设计删除某一个记事的功能,设计好的界面如下图:
当用户按delete按钮后,弹出如下界面:
当用户选No时,则返回查看记事具体内容的界面,如果选择的是YES,则删除一条记事。
首先我们在index.html中,设计删除信息提示的界面,如下:
<div data-role="header">
<h1>Delete Note?</h1>
</div>
<div data-role="content">
<div id="delete-note-content-placeholder"></div>
<a id="cancel-delete-note-button" data-role="button" data-theme="b" data-rel="back">No</a>
<a id="ok-to-delete-note-button" data-role="button" data-theme="f">Yes</a></div>
</div>
注意我们使用了data-role=”dialog”属性,这个之前谈到的一样,让框架输出一个对话框风格的页面。之后在Yes和No的按钮中,注意使用了data-role=”button”的方法,并指出了每个按钮的data-theme的风格样式,其中b和f都是jQuery Mobile内置的CSS样式,当然用户可以自己修改。
跟之前一样,我们要在控制层中控制这个删除界面的行为,同样首先定义删除界面的标识如下:
var confirmDeleteNoteDlgSel = "#confirm-delete-note-dialog";
再将在编辑记事页面中的删除按钮以及删除界面中的确认按钮、在删除确认界面中的一个DIV都定义一个别名标识,如下:
var deleteNoteButtonSel = "#delete-note-button",
deleteNoteContentPlaceholderSel = "#delete-note-content-placeholder",
okToDeleteNoteButtonSel = "#ok-to-delete-note-button";
接下来,我们编写删除按钮的基本事件框架如下:
// 其余省略的代码
d.delegate(deleteNoteButtonSel, "tap", onDeleteNoteButtonTapped);
};
其中onDeleteNoteButtonTapped代码如下:
var onDeleteNoteButtonTapped = function () {
if (currentNote) {
var noteContentPlaceholder = $(deleteNoteContentPlaceholderSel);
noteContentPlaceholder.empty();
$("
" + currentNote.title + "
" + currentNote.narrative + "
").appendTo(noteContentPlaceholder);
$.mobile.changePage(confirmDeleteNoteDlgSel, defaultDlgTrsn);
}
};
在onDeleteNotebuttonTapped()方法中,首先我们读取了当前即将要被删除的记事,然后将其添加到placeholder这个div中,并且再使用changePage方法让当前界面成为焦点界面,这个之前的论述是一样的。
接下来,再分别看下当用户点NO或YES时,如何进行事件的处理。当用户点NO按钮时,
页面的代码为:
其中的data-rel=”back”是内置的功能,能自动回退到上一个页面,而这正是我们所需要的。
而在YES按钮的事件处理中,当用户点YES后,需要删除记事后再带用户回到记事列表,并且刷新此时的记事列表。同样在控制层中的init方法中加入如下代码:
//省略其他代码
d.delegate(okToDeleteNoteButtonSel, "tap", onOKToDeleteNoteButtonTapped);
};
接下来定义onOKToDeleteNoteButtonTapped方法,如下:
var onOKToDeleteNoteButtonTapped = function () {
dataContext.deleteNote(currentNote);
returnToNotesListPage();
};
这个很容易理解,其中调用了dataContext中的deleteNote方法删除一个真正的记事,然后再调用returnToNotesListPage()返回记事列表。在编写deleteNote方法前,先继续编写单元测试用例如下:
// 创建记事
var dateCreated = new Date();
var id = new String(dateCreated.getTime());
var noteModel = new Notes.NoteModel({
id: id,
dateCreated: dateCreated,
title: "",
narrative: ""
});
// 清空记事列表
var notesList = [];
// 添加到localstorage本地存储
notesList.push(noteModel);
$.jStorage.set(notesListStorageKey, notesList);
notesList = $.jStorage.get(notesListStorageKey);
expect(notesList.length).toEqual(1);
// 删除记事
Notes.dataContext.init(notesListStorageKey);
Notes.dataContext.deleteNote(noteModel);
// 删除后应该返回空的列表,断言判断
notesList = $.jStorage.get(notesListStorageKey);
expect(notesList.length).toEqual(0);
// 清除相关资源
$.jStorage.deleteKey(notesListStorageKey);
});
运行测试后当然结果是失败的,因为没有编写deleteNote方法。因此编写代码如下:
var deleteNote = function (noteModel) {
var i;
for (i = 0; i < notesList.length; i += 1) {
if (notesList[i].id === noteModel.id) {
notesList.splice(i, 1);
i = notesList.length;
}
}
saveNotesToLocalStorage();
};
这里就是通过循环遍历数组的方法,根据notedId找出要删除的元素,然后通过数组的splice方法进行删除,删除后记得调用saveNotesToLocalStorage();
方法进行保存。为了方便在模块的接口处进行调用,我们添加到controller.js中:
init: init,
createBlankNote: createBlankNote,
getNotesList: getNotesList,
saveNote: saveNote,
deleteNote: deleteNote
};
再运行测试,可以看到顺利通过。如下图:
更换Jquery Mobile的自定义样式
在完成上面的工作后,可以看到默认的删除界面的按钮样式,如果不经过改变,是如下的样子的:
如果要改变其样式,这就要需要使用jQuery Mobile的自定义样式。回忆一下之前的代码,在按钮中:
其中的data-theme=“f”,要求我们定义一个新的名为f的样式,这个可以通过
jQuery Mobile提供的在线样式编辑器实现,地址为:http://jquerymobile.com/themeroller/
(需要在chrome,firefox下使用)。该设计器如下图:
用户可以根据自己的喜好,对所有的按钮,文字,超链接进行样式的自定义,十分方便,在定义好后,可以使用其download的功能,把样式导出下载,就可以把样式增加到我们的app.css中,如下代码:
border: 1px solid #c1272d /*{f-bup-border}*/;
background: #c1272d /*{f-bup-background-color}*/;
font-weight: bold;
color: #ffffff /*{f-bup-color}*/;
text-shadow: 0 /*{f-bup-shadow-x}*/ 1px /*{f-bup-shadow-y}*/ 1px /*{f-bup-shadow-radius}*/ #444444 /*{f-bup-shadow-color}*/;
background-image: -webkit-gradient(linear, left top, left bottom, from( #D42A31 /*{f-bup-background-start}*/), to( #AD2328 /*{f-bup-background-end}*/)); /* Saf4+, Chrome */
background-image: -webkit-linear-gradient(top, #D42A31 /*{f-bup-background-start}*/, #AD2328 /*{f-bup-background-end}*/); /* Chrome 10+, Saf5.1+ */
background-image: -moz-linear-gradient(top, #D42A31 /*{f-bup-background-start}*/, #AD2328 /*{f-bup-background-end}*/); /* FF3.6 */
background-image: -ms-linear-gradient(top, #D42A31 /*{f-bup-background-start}*/, #AD2328 /*{f-bup-background-end}*/); /* IE10 */
background-image: -o-linear-gradient(top, #D42A31 /*{f-bup-background-start}*/, #AD2328 /*{f-bup-background-end}*/); /* Opera 11.10+ */
background-image: linear-gradient(top, #D42A31 /*{f-bup-background-start}*/, #AD2328 /*{f-bup-background-end}*/);
}
.ui-btn-up-f a.ui-link-inherit {
color: #ffffff /*{f-bup-color}*/;
}
.ui-btn-hover-f {
border: 1px solid #DD2C33 /*{f-bhover-border}*/;
background: #DD2C33 /*{f-bhover-background-color}*/;
font-weight: bold;
color: #ffffff /*{f-bhover-color}*/;
text-shadow: 0 /*{f-bhover-shadow-x}*/ 1px /*{f-bhover-shadow-y}*/ 1px /*{f-bhover-shadow-radius}*/ #444444 /*{f-bhover-shadow-color}*/;
background-image: -webkit-gradient(linear, left top, left bottom, from( #F33038 /*{f-bhover-background-start}*/), to( #C6272D /*{f-bhover-background-end}*/)); /* Saf4+, Chrome */
background-image: -webkit-linear-gradient(top, #F33038 /*{f-bhover-background-start}*/, #C6272D /*{f-bhover-background-end}*/); /* Chrome 10+, Saf5.1+ */
background-image: -moz-linear-gradient(top, #F33038 /*{f-bhover-background-start}*/, #C6272D /*{f-bhover-background-end}*/); /* FF3.6 */
background-image: -ms-linear-gradient(top, #F33038 /*{f-bhover-background-start}*/, #C6272D /*{f-bhover-background-end}*/); /* IE10 */
background-image: -o-linear-gradient(top, #F33038 /*{f-bhover-background-start}*/, #C6272D /*{f-bhover-background-end}*/); /* Opera 11.10+ */
background-image: linear-gradient(top, #F33038 /*{f-bhover-background-start}*/, #C6272D /*{f-bhover-background-end}*/);
}
.ui-btn-hover-f a.ui-link-inherit {
color: #ffffff /*{f-bhover-color}*/;
}
.ui-btn-down-f {
border: 1px solid #DD2C33 /*{f-bdown-border}*/;
background: #DD2C33 /*{f-bdown-background-color}*/;
font-weight: bold;
color: #ffffff /*{f-bdown-color}*/;
text-shadow: 0 /*{f-bdown-shadow-x}*/ 1px /*{f-bdown-shadow-y}*/ 1px /*{f-bdown-shadow-radius}*/ #444444 /*{f-bdown-shadow-color}*/;
background-image: -webkit-gradient(linear, left top, left bottom, from( #C6272D /*{f-bdown-background-start}*/), to( #F33038 /*{f-bdown-background-end}*/)); /* Saf4+, Chrome */
background-image: -webkit-linear-gradient(top, #C6272D /*{f-bdown-background-start}*/, #F33038 /*{f-bdown-background-end}*/); /* Chrome 10+, Saf5.1+ */
background-image: -moz-linear-gradient(top, #C6272D /*{f-bdown-background-start}*/, #F33038 /*{f-bdown-background-end}*/); /* FF3.6 */
background-image: -ms-linear-gradient(top, #C6272D /*{f-bdown-background-start}*/, #F33038 /*{f-bdown-background-end}*/); /* IE10 */
background-image: -o-linear-gradient(top, #C6272D /*{f-bdown-background-start}*/, #F33038 /*{f-bdown-background-end}*/); /* Opera 11.10+ */
background-image: linear-gradient(top, #C6272D /*{f-bdown-background-start}*/, #F33038 /*{f-bdown-background-end}*/);
}
.ui-btn-down-f a.ui-link-inherit {
color: #ffffff /*{f-bdown-color}*/;
}
最后,可以看到效果为下图,实现了样式的变更
小结
在本文中,实现了对记事的删除的相关操作,介绍了如何在jQuery Mobile中实现对话框的功能,
本文的相关代码在
http://miamicoder.com/wp-content/uploads/2012/01/Building-a-jQuery-Mobile-App-Part-4-Src.zip可以下载。至此,本系列教程结束,希望读者举一反三,领会jQuery Mobile的开发技巧。