本文是“使用phoneGap和Sencha Touch 2开发Android应用程序”系列教程的第3章, 在这一章,咱们会建立编辑笔记的视图,在这个视图上,用户可以建立,编辑以及删除笔记。 数据库
在这一章结束后,本应用程序将可以建立新的笔记,对已有的笔记进行编辑。下面咱们开始建立编辑笔记视图。 编程
咱们在NoteEditor.js文件中编写View的代码 , NoteEditor.js文件创建在view目录下: 浏览器
在NoteEditor.js文件中, 咱们将定义一个空的NoteEditor类,以下所示: 缓存
Ext.define("NotesApp.view.NoteEditor", { extend: "Ext.form.Panel", requires: "Ext.form.FieldSet", alias: "widget.noteeditor", config:{ scrollable:'vertical' }, initialize: function () { this.callParent(arguments); } });
该Note Editor 继承自Ext.form.Panel类。由于咱们须要在视图中使用到一个FieldSet类的实例,因此须要声明requires配置参数来告诉加载器下载该类的源码。 一样的,咱们使用scrollable配置参数来使面板的内容可以上下滚动。这样当form表单的高度超过移动设备的屏幕高度时,才不会出现布局混乱。 app
在继续完善NoteEditor的代码以前,咱们先来回顾一下该视图的构成: 框架
很容易发现,该视图由3部分组成,从上至下依次为一个包含了"返回"和“保存”按钮的顶部Toolbar,一个包含了“标题”textfield和“内容”textareafield的Fieldset,以及一个包含了“删除”按钮的底部Toolbar。 dom
与编写Notes ListContainer类同样, 咱们也使用 initialize()方法来定义Note Editor所包含的的组件: 编辑器
Ext.define("NotesApp.view.NoteEditor", { extend: "Ext.form.Panel", requires: "Ext.form.FieldSet", alias: "widget.noteeditor", config:{ scrollable:'vertical' }, initialize: function () { this.callParent(arguments); var backButton = { xtype: "button", ui: "back", text: "返回" }; var saveButton = { xtype: "button", ui: "action", text: "保存" }; var topToolbar = { xtype: "toolbar", docked: "top", title: "编辑笔记", items: [ backButton, { xtype: "spacer" }, saveButton ] }; var deleteButton = { xtype: "button", iconCls: "trash", iconMask: true, scope: this }; var bottomToolbar = { xtype: "toolbar", docked: "bottom", items: [ deleteButton ] }; var noteTitleEditor = { xtype: 'textfield', name: 'title', label: '标题', required: true }; var noteNarrativeEditor = { xtype: 'textareafield', name: 'narrative', label: '内容' }; this.add([ topToolbar, { xtype: "fieldset", items: [noteTitleEditor, noteNarrativeEditor] }, bottomToolbar ]); } });
initialize()方法首先会调用 callParent()方法,而后依次定义包含“返回”和“保存”按钮的顶部工具栏,和包含“删除”按钮的底部工具栏。 ide
noteTitleEditor标题编辑器 和 noteNarrativeEditor内容编辑器被用来编辑笔记的标题和内容,它们分别是Ext.form.Text 和 Ext.form.TextArea组件的实例。 工具
当全部的视图组件都定义以后,接下来的工做就是将它们添加到View视图上去。在这里咱们使用了Ext.form.FieldSet组件来改善Form表单的外观,标题和内容编辑器都将放置在FieldSet中显示。
this.add([ topToolbar, { xtype: "fieldset", items: [noteTitleEditor, noteNarrativeEditor] }, bottomToolbar ]);
在以前的章节中,咱们为“新建”按钮建立过一个handler处理方法,当newNoteCommand 事件发生时,该方法会被调用。咱们还须要将 onNewNoteCommand() 监听器添加到Notes Controller控制器中。这样咱们就能使用该handler方法来激活Note Editor视图。
为了在控制器中激活Note Editor视图,咱们首先须要获得这个视图的引用,因此咱们定义了noteeditor 这个引用来达到目的。
Ext.define("NotesApp.controller.Notes", { extend: "Ext.app.Controller", config: { refs: { // We're going to lookup our views by xtype. notesListContainer: "noteslistcontainer", noteEditor: "noteeditor" }, // Remainder of the controller’s code omitted for brevity. });
以前已经说过,定义以后,框架会在控制器中为这个引用会自动建立getNoteEditor() 方法,使用这个方法,就能获得视图的引用而且在应用程序中激活视图。
接下来,咱们对onNewNoteCommand() 方法进行改写:
onNewNoteCommand : function(){// 当 新增按钮点击时调用 // alert("onNewNoteCommand"); // 笔记建立时间及Id var now = new Date(); var noteId = (now.getTime()).toString() + (this.getRandomInt(0,100)).toString(); var newNote = Ext.create("NotesApp.model.Note",{ id : noteId, dateCreated : now, title : "", narrative : "" }); this.activateNoteEditor(newNote); }
这里建立了一个新的笔记,而且将它做为参数传给了activateNoteEditor() 方法。
在这里咱们使用了getRamdomInt() 辅助方法来生成笔记的惟一id:
getRandomInt: function (min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }
activateNoteEditor() 方法的做用是将新建的笔记加载到Note Editor视图中,而且激活视图:
activateNoteEditor: function (record) { var noteEditor = this.getNoteEditor(); noteEditor.setRecord(record); // load() is deprecated. Ext.Viewport.animateActiveItem(noteEditor, this.slideLeftTransition); }
这里咱们使用了Ext.form.Panel的setRecord()方法,来将一个Model类的实例加载到Form表单中,这个Model实例的各个属性将按照名称来匹配并填充到Form中去。(注:这里很容易理解,相似J2EE的编程方式,点击新增按钮跳转到新建笔记的页面中时,笔记的建立时间和id已经按照必定的规则生成完毕,新增页面只须要展现标题和内容这两个须要用户输入的表单元素)
该方法用到了slideLeftTransition变量,咱们采用下面的代码来定义它:
slideLeftTransition: { type: 'slide', direction: 'left' }
使用这个transition变量会使得Note Editor视图以从右到左滑动的方式来显示出来。
至此为止,当点击新建按钮时,控制器所要作的工做(激活并展现Note Editor视图)就完成了,紧接着咱们将在app.js中配置这个视图,以便应用程序能识别它。
在app.js文件中,在views配置项中添加新建视图:
views: ["NotesList", "NotesListContainer", "NoteEditor"]
接着在应用程序的launch 方法中实例化该视图:
Ext.application({ name: "NotesApp", models: ["Note"], stores: ["Notes"], controllers: ["Notes"], views: ["NotesList", "NotesListContainer", "NoteEditor"], launch: function () { var notesListContainer = { xtype: "noteslistcontainer" }; var noteEditor = { xtype: "noteeditor" }; Ext.Viewport.add([notesListContainer,noteEditor]); } });
这时能够启动模拟器来看看运行效果。
单击新建按钮时将显示Note Editor视图:
如何在Sencha Touch 列表中编辑一条记录
当单击笔记列表中某条记录右侧的disclose按钮图标时,一样须要激活到编辑视图中:
如今咱们来从新编写控制器中的 onEditNoteCommand()方法实现这个功能:
onEditNoteCommand : function(list,record){ // 当 编辑按钮点击时调用 // alert("onEditNoteCommand"); this.activateNoteEditor(record); }
事实上实现这个功能很是简单,由于disclose时间将被选中的Note对象实例经过record参数传递给了handler,而后咱们再次调用activateNoteEditor()来进入编辑笔记视图。(注,看看咱们以前编写的NoteListContainer视图里面的代码):
var notesList = { xtype:"noteslist", store:Ext.getStore("Notes"), listeners:{ disclose:{ fn:this.onNotesListDisclose, scope:this } } }从这里咱们能够看到,在noteList列表定义的时候,咱们就为他添加了一个监听器,用来监听disclose动做,并把处理的权力交给了onNotesListDisclose 方法,
onNotesListDisclose:function(list,record,target,index,evt,options){ // alert("editNoteCommand"); this.fireEvent("editNoteCommand",this,record); }这个onNotesListDisclose 方法能够接收一系列跟列表相关的参数,在咱们这里,只须要传递list对象和被选中的记录就好。
链接好手机,或者启动模拟器,就能看到选中记录的编辑视图了:
完成了笔记的新增和编辑,接下来咱们实现保存的代码,在这以前,咱们先理一下保存的思路。
首先,咱们须要对以前章节采用的硬编码形式存储的笔记对象的代码进行改造。
Ext.define("NotesApp.store.Notes",{ extend:"Ext.data.Store", config:{ model:"NotesApp.model.Note", data:[ {title:"Note 1", narrative:"这是Note 1 的笔记内容"}, {title:"Note 2", narrative:"这是Note 2 的笔记内容"}, {title:"Note 3", narrative:"这是Note 3 的笔记内容"}, {title:"Note 4", narrative:"这是Note 4 的笔记内容"}, {title:"Note 5", narrative:"这是Note 5 的笔记内容"}, {title:"Note 6", narrative:"这是Note 6 的笔记内容"}, ], sorters:[{property:'dateCreated',direction:'DESC'}] } });
咱们使用LocalStorage Proxy的方式来将应用程序运行时添加的笔记缓存到设备上:
Ext.define("NotesApp.store.Notes",{ extend:"Ext.data.Store", requires:"Ext.data.proxy.LocalStorage", config:{ model:"NotesApp.model.Note", proxy : { type : "localstorage", id : "notes-app-store" }, sorters:[{property:'dateCreated',direction:'DESC'}] } });
LocalStorageProxy采用了 HTML5 的 localStorage API 来将Model对象数据 保存在客户端浏览器中。这个代理类能够存储一些相同的数据记录。这个代理类须要一个id配置参数,用来标识不一样的本地数据对象。
这样咱们就可以从本地数据库中保存和读取笔记了,如今咱们回到视图和控制器来完善保存笔记的功能。
在NoteEditor 视图类中,修改initialize() 方法中的saveButton组件定义:
var saveButton = { xtype: "button", ui: "action", text: "保存", handler:this.onSaveButtonTap, scope:this }
经过handler配置参数定义了一个处理方法,当用户点击按钮时,该方法就会运行。另外还配置了scope参数值this来保证在本NoteEditor视图中运行该方法,即声明该方法的做用域只限于该视图。
接下来定义onSaveButtonTap() 方法:
onSaveButtonTap:function(){ //alert("save note"); this.fireEvent("saveNoteCommand",this); }
和以前编写的事件处理方法相似,咱们经过给视图定义saveNoteCommand事件来捕获按钮点击的动做。
事实上,若是NoteEditor视图仅仅只是将这个saveNoteCommand事件传播(fireEvent)出去的话,控制器并不可以监听到这一事件。咱们须要明确的告诉控制器事件是从哪里来的,因此咱们须要给控制器的refs配置参数加上noteEditor视图。
refs: { // We're going to lookup our views by xtype. notesListContainer: "noteslistcontainer", noteEditor: "noteeditor" }
而后,使用control配置参数来给saveNoteCommand这一事件映射一个对应的处理方法。
control: { notesListContainer: { // The commands fired by the notes list container. newNoteCommand: "onNewNoteCommand", editNoteCommand: "onEditNoteCommand" }, noteEditor: { // The commands fired by the note editor. saveNoteCommand: "onSaveNoteCommand" } }
最后,编写onSaveNoteCommand() 方法来处理保存动做:
onSaveNoteCommand: function () { // console.log("onSaveNoteCommand"); var noteEditor = this.getNoteEditor(); // 获得编辑视图 var currentNote = noteEditor.getRecord();// 当前笔记 var newValues = noteEditor.getValues();// 当前表单中所输入的笔记属性 // 用表单数据来更新笔记 currentNote.set("title", newValues.title); currentNote.set("narrative", newValues.narrative); var errors = currentNote.validate(); // 验证 if (!errors.isValid()) {// 验证不经过,提示错误信息 Ext.Msg.alert('警告!', errors.getByField("title")[0].getMessage(), Ext.emptyFn); currentNote.reject(); return; } var notesStore = Ext.getStore("Notes"); // 经过id查找修改的笔记记录,并添加到本地数据库中 if (null == notesStore.findRecord('id', currentNote.data.id)) { notesStore.add(currentNote); } notesStore.sync();// 数据库记录同步 // 对同步后的记录从新排序 notesStore.sort([{ property: 'dateCreated', direction: 'DESC'}]); this.activateNotesList(); }
在onSaveNoteCommand() 方法中,首先须要获得被编辑笔记的引用和编辑表单的值:
var noteEditor = this.getNoteEditor(); var currentNote = noteEditor.getRecord(); var newValues = noteEditor.getValues();
而后,将新的属性值设置到笔记中:
currentNote.set("title", newValues.title); currentNote.set("narrative", newValues.narrative);
接下来是很是重要的一步,咱们须要对表单数据进行验证。首先,对Model对象调用validate() 方法,而后对返回的errors对象调用isValid() 方法进行验证:
var errors = currentNote.validate(); // 验证 if (!errors.isValid()) {// 验证不经过,提示错误信息 Ext.Msg.alert('警告!', errors.getByField("title")[0].getMessage(), Ext.emptyFn); currentNote.reject(); return; }
Ext.data.Model类的validate() 方法会遍历运行全部定义在Model类中的验证器 (validations), 而后返回一个Ext.data.Errors类的实例对象,Errors实例对象包含了一组Ext.data.Error实例对象,在这组对象中,包含了每一个验证的Model属性对应的一个Error实例 。
Ext.define("NotesApp.model.Note",{ extend:"Ext.data.Model", config:{ idProperty:"id", fields:[ {name:"id",type:"int"}, {name:"dateCreated",type:"date",dateFormat:"c"}, {name:"title",type:"string"}, {name:"narrative",type:"string"} ], validations:[ {field:"id",type:"presence"}, {field:"dateCreated",type:"presence"}, {field:"title",type:"presence",message:"请输入标题"} ] } });
咱们的应用程序只给笔记的标题绑定了一个验证器。若是验证不经过,将在屏幕上弹出一个提示窗口,提示信息的内容就是在Model里验证器配置的message,而后,会调用model的 reject()方法,这个方法使得被修改的表单元素恢复成原始值。
在 onSaveNoteCommand()方法中, 若是表单元素验证经过,就须要把数据保存到设备的本地数据库中:
var notesStore = Ext.getStore("Notes"); // 经过id查找修改的笔记记录,并添加到本地数据库中 if (null == notesStore.findRecord('id', currentNote.data.id)) { notesStore.add(currentNote); }
由于咱们对新增和编辑使用了同一个视图,因此在保存以前,咱们须要经过findRecrod()方法来查找本地数据库中是否存在这条记录,若是不存在,就把该笔记添加到本地数据库中。
Store对象的sync() 用来同步数据,在本地数据库中执行新增,编辑或者删除操做。
本地数据更新以后,从新对数据进行排序处理:
notesStore.sort([{ property: 'dateCreated', direction: 'DESC'}]);
在onSaveNoteCommand ()方法的最后,调用了activateNotesList() 方法,和activateNoteEditor()方法相似,这个方法使得应用程序跳转到主视图,即列表视图:
activateNotesList: function () { Ext.Viewport.animateActiveItem(this.getNotesListContainer(), this.slideRightTransition); }
返回主视图时,这回咱们使用定义的一个右滑:
slideRightTransition: { type: 'slide', direction: 'right' }再次运行应用程序,保存笔记:
在这一章,咱们完成了本应用程序的一些新功能,好比建立新的笔记,编辑已有的笔记等。
咱们学习了如何使用Sencha Touch的表单组件来编辑应用程序中的数据,以及在应用程序运行时,如何使用本地存储代理的实例来把数据缓存到本地设备。还学习了如何验证Sencha Touch的数据模型,如何将数据同步存储到本地数据库中,以及在数据验证失败时恢复数据改变。
另外,咱们还学习了在拥有多个视图的应用程序中,如何建立不一样视图切换时的滑动效果。
到此为止,咱们的应用程序还缺乏删除笔记的功能。咱们将在下一章添加该功能,同时,咱们将在下一章对笔记列表进行修改,让列表可以按照日期来分组显示。
未完待续!
源代码已发布到迅雷快传:http://kuai.xunlei.com/d/KLBGTSXIGTJA
原文出自: http://miamicoder.com/2012/how-to-create-a-sencha-touch-2-app-part-3/
本教程快速连接