技术开发 频道

jQuery Mobile开发记事本应用四大技巧

  【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、创建自定义的样式,改变系统默认的对话框的样式。

  我们能期望达到如下的效果:

验证输入记事的合法性

验证输入记事的合法性

  验证输入记事的合法性

  在上一讲中,当我们要新保存新的记事时,只是针对标题和内容在保存前进行了是否为空的校验,回忆下相关代码如下:

  Notes.NoteModel.prototype.isValid = function () {

  
"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中添加如下代码:

<!--Invalid Note dialog-->
<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>

  接下来,我们还要在控制层中添加对话框的标识,

var invalidNoteDlgSel = "#invalid-note-dialog";
   我们将修改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这个文件中进行处理,代码如下:

  Notes.controller = (function ($, dataContext, document) {

  
//省略了详细的contoller代码,具体见附件

  } (jQuery, Notes.dataContext, document));

  $(document).bind(
"mobileinit", function () {

  Notes.controller.init();

  });

  现在,index.html代码中,则只是引用了相关的JS文件了,不会再象上一讲中,在index.html中混杂了js代码。

 

  设计删除记事功能

  接下来,我们要设计删除某一个记事的功能,设计好的界面如下图:

设计删除记事功能

  当用户按delete按钮后,弹出如下界面:

设计删除记事功能

  当用户选No时,则返回查看记事具体内容的界面,如果选择的是YES,则删除一条记事。

  首先我们在index.html中,设计删除信息提示的界面,如下:

<div id="confirm-delete-note-dialog" data-role="dialog" data-title="Delete Note">
<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";

  接下来,我们编写删除按钮的基本事件框架如下:

  var init = function () {

  
// 其余省略的代码

  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按钮时,

  页面的代码为:

<a id="cancel-delete-note-button" data-role="button" data-theme="b" data-rel="back">No</a>

  其中的data-rel=”back”是内置的功能,能自动回退到上一个页面,而这正是我们所需要的。

  而在YES按钮的事件处理中,当用户点YES后,需要删除记事后再带用户回到记事列表,并且刷新此时的记事列表。同样在控制层中的init方法中加入如下代码:

    var init = function () {
    
//省略其他代码
    d.delegate(okToDeleteNoteButtonSel,
"tap", onOKToDeleteNoteButtonTapped);
};
接下来定义onOKToDeleteNoteButtonTapped方法,如下:
  var onOKToDeleteNoteButtonTapped
= function () {
    dataContext.deleteNote(currentNote);
    returnToNotesListPage();
};

  这个很容易理解,其中调用了dataContext中的deleteNote方法删除一个真正的记事,然后再调用returnToNotesListPage()返回记事列表。在编写deleteNote方法前,先继续编写单元测试用例如下:

     it("Removes a note from local storage", function () {
        
// 创建记事
        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中:

  var pub = {
    init: init,
    createBlankNote: createBlankNote,
    getNotesList: getNotesList,
    saveNote: saveNote,
    deleteNote: deleteNote
};

  再运行测试,可以看到顺利通过。如下图:

设计删除记事功能

  更换Jquery Mobile的自定义样式

  在完成上面的工作后,可以看到默认的删除界面的按钮样式,如果不经过改变,是如下的样子的:

更换Jquery Mobile的自定义样式

  如果要改变其样式,这就要需要使用jQuery Mobile的自定义样式。回忆一下之前的代码,在按钮中:

  <a id="ok-to-delete-note-button" data-role="button" data-theme="f">Yes</a>

  其中的data-theme=“f”,要求我们定义一个新的名为f的样式,这个可以通过

  jQuery Mobile提供的在线样式编辑器实现,地址为:http://jquerymobile.com/themeroller/

  (需要在chrome,firefox下使用)。该设计器如下图:

更换Jquery Mobile的自定义样式

  用户可以根据自己的喜好,对所有的按钮,文字,超链接进行样式的自定义,十分方便,在定义好后,可以使用其download的功能,把样式导出下载,就可以把样式增加到我们的app.css中,如下代码:

.ui-btn-up-f {
    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的自定义样式

  小结

  在本文中,实现了对记事的删除的相关操作,介绍了如何在jQuery Mobile中实现对话框的功能,

  本文的相关代码在

  http://miamicoder.com/wp-content/uploads/2012/01/Building-a-jQuery-Mobile-App-Part-4-Src.zip可以下载。至此,本系列教程结束,希望读者举一反三,领会jQuery Mobile的开发技巧。

1
相关文章