本文转载自http://www.cnblogs.com/yage/archive/2009/10/30/1591785.html
在上一个系列当中,我们学习了如何对grid中的内容进行编辑,但是编辑的结果我们并没有保存,这在实际的应用中是没有什么意义的。在有些情况下,除了编辑之外,还要通过grid进行数据的增加和删除,这两个操作也涉及到对于数据的保存。在这个系列里边,我们将学习如何保存数据以及通过grid对数据进行增加和删除。
我们在前边的学习过程中已经知道,grid其实只是显示数据,它通过配置参数store来获得数据,这个参数需要的是Store类或者其子类的一个对象,里边封装了我们需要的数据。我们现在应该已经比较熟悉Store类了,这次我们需要使用它的一个属性modified,里边保存了被修改过的记录的集合。我们通过把上个系列中的例子改变一下来看看如何保存数据:
- ///<reference path="vswd-ext_2.0.2.js" />
- /**//*
- *作者:大笨
- *日期:2009-10-20
- *版本:1.0
- *博客地址:http://yage.cnblogs.com
- *QQ:14202190
- */
- Ext.BLANK_IMAGE_URL = '../extjs/resources/images/default/s.gif';
- Ext.onReady(function() {
- Ext.QuickTips.init();
- //格式化日期
- function formatDate(value) {
- return value ? value.dateFormat('Y年m月d日') : '';
- }
- // 别名
- var fm = Ext.form;
- //构造一个只能包含checkbox的列
- var checkColumn = new Ext.grid.CheckColumn({
- header: 'Indoor?',
- dataIndex: 'indoor',
- width: 55
- });
- // 构造ColumnModel
- var cm = new Ext.grid.ColumnModel({
- columns: [{
- id: 'common',
- header: 'Common Name',
- dataIndex: 'common',
- width: 220,
- // 使用上边定义好的别名
- editor: new fm.TextField({
- allowBlank: false
- })
- }, {
- header: 'Light',
- dataIndex: 'light',
- width: 130,
- editor: new fm.ComboBox({
- typeAhead: true,
- triggerAction: 'all',
- transform: 'light',
- lazyRender: true,
- listClass: 'x-combo-list-small'
- })
- }, {
- header: 'Price',
- dataIndex: 'price',
- width: 70,
- align: 'right',
- renderer: 'usMoney',
- editor: new fm.NumberField({
- allowBlank: false,
- allowNegative: false,
- maxValue: 100000
- })
- }, {
- header: 'Available',
- dataIndex: 'availDate',
- width: 95,
- renderer: formatDate,
- editor: new fm.DateField({
- format: 'Y年m月d日',
- minValue: '01/01/06',
- disabledDays: [0, 6],
- disabledDaysText: 'Plants are not available on the weekends'
- })
- },
- checkColumn
- ],
- defaults: {
- sortable: true
- }
- });
- // 构造一个Store对象
- var store = new Ext.data.Store({
- url: 'plants.xml',
- reader: new Ext.data.XmlReader(
- {
- record: 'plant'
- },
- [
- { name: 'common', type: 'string' },
- { name: 'botanical', type: 'string' },
- { name: 'light' },
- { name: 'price', type: 'float' },
- { name: 'availDate', mapping: 'availability', type: 'date', dateFormat: 'm/d/Y' },
- { name: 'indoor', type: 'bool' }
- ]
- ),
- sortInfo: { field: 'common', direction: 'ASC' }
- });
- // 构造可编辑的grid
- var grid = new Ext.grid.EditorGridPanel({
- store: store,
- cm: cm,
- renderTo: 'grid',
- width: 600,
- height: 300,
- autoExpandColumn: 'common',
- title: 'Edit Plants?',
- frame: true,
- plugins: checkColumn,
- clicksToEdit: 1,
- listeners: {
- "afterEdit": {
- fn: afterEdit,
- scope: this
- }
- },
- tbar: [{
- text: "保存",
- handler: function() {
- var modified = store.modified;
- updateData(modified);
- }
- }]
- });
- // 触发数据的加载
- store.load();
- //发送数据到服务器端进行更新
- function updateData(modified) {
- var json = [];
- Ext.each(modified, function(item) {
- json.push(item.data);
- });
- if (json.length > 0) {
- Ext.Ajax.request({
- url: "EditGrid.aspx",
- params: { data: Ext.util.JSON.encode(json) },
- method: "POST",
- success: function(response) {
- Ext.Msg.alert("信息", "数据更新成功!", function() { store.reload(); });
- },
- failure: function(response) {
- Ext.Msg.alert("警告", "数据更新失败,请稍后再试!");
- }
- });
- }
- else {
- Ext.Msg.alert("警告", "没有任何需要更新的数据!");
- }
- }
- //编辑后触发的事件,可在此进行数据有效性的验证
- function afterEdit(e) {
- if (e.field == "common") {
- if (e.value == "大笨") {
- Ext.Msg.alert("错误", "大笨是人物不是植物", function() { grid.startEditing(e.row, e.column) });
- }
- }
- }
- });
我们首先给grid增加了一个afterEdit事件,顾名思义,该事件在编辑之后被触发,我们可以在此时对数据的有效性进行验证,在本例中,我们只是简单的让common列的值不能等于一个特定的字符串,实际的项目中可能需要对每一列用正则表达式来进行验证。在触发afterEdit事件的时候会传递一个事件对象,该对象有如下几个属性:
grid:当前grid。
record:当前行。
field:当前列名。
value:被设置的值。
originalValue:编辑前的值。
row:行索引。
column:列索引。
我们还在grid上加了一个工具栏,上边放了一个保存按钮,用以将修改后的数据传递到服务器进行保存。由于记录的集合是一个复杂的json对象,我们需要的只是相关数据的几个,因此第137-140行对该集合进行了处理。当然,我们还需要在服务器端接收数据然后进行处理,在系列一中我们已经学习到相关的方法,这里不再赘述。我们看一看运行之后的效果图:
通过firebug,我们可以看到向服务器投递的数据:
接下来看看如何添加数据,我们在工具栏里边添加一个增加按钮,相关代码如下:
- tbar: [{
- text: "保存",
- handler: function() {
- var modified = store.modified;
- updateData(modified);
- }
- },
- '-',
- {
- text: "增加",
- handler: function() {
- var Plant = store.recordType;
- var p = new Plant({
- common: 'New Plant 1',
- light: 'Mostly Shade',
- price: 0,
- availDate: (new Date()).clearTime(),
- indoor: false
- });
- grid.stopEditing();
- store.insert(0, p);
- grid.startEditing(0, 0);
- }
- }
- ]
在向grid中插入数据之前,必须要先获得插入数据的格式,Store类的recordType属性返回Record的构造函数,表明了数据的格式,然后通过构造函数传递数据,这样就准备好了要插入的数据了。接下来将grid的编辑状态关闭,插入数据,然后把要插入数据的第一列设置为编辑状态。运行后点击增加按钮后的效果如下:
可以看到,在grid的最上边增加了一行,对应行索引0,第一列出于编辑状态,对应列索引1,增加行中的默认初始数据就是我们在构造函数中传递进来的数据。注意,新增加的数据在修改之前是不会保存在store的modified属性里边的。
我们再来看看如何进行删除操作。在进行删除之前,我们必须要选择需要删除的行,grid有单元格选择模型和行选择模型两种,分别以类CellSelectionModel和RowSelectionModel来表示,默认是单元格选择模型,也就是在点击单元格的时候选中的是当前单元格,可以通过配置属性selModle(可简写为sm)来指定选择模式:sm:new Ext.grid.RowSelectionModel({singleSelection:true}), 这样就把选择模型改为了行选择模型,参数singleSelect指明是否是单选的,为真的时候只能选择一行,默认是false,可以按下ctrl键或者shift键进行多行的选择。选择好之后,我们就来看看如何删除吧:
可以看到,在grid的最上边增加了一行,对应行索引0,第一列出于编辑状态,对应列索引1,增加行中的默认初始数据就是我们在构造函数中传递进来的数据。注意,新增加的数据在修改之前是不会保存在store的modified属性里边的。
我们再来看看如何进行删除操作。在进行删除之前,我们必须要选择需要删除的行,grid有单元格选择模型和行选择模型两种,分别以类CellSelectionModel和RowSelectionModel来表示,默认是单元格选择模型,也就是在点击单元格的时候选中的是当前单元格,可以通过配置属性selModle(可简写为sm)来指定选择模式:sm:new Ext.grid.RowSelectionModel({singleSelection:true}), 这样就把选择模型改为了行选择模型,参数singleSelect指明是否是单选的,为真的时候只能选择一行,默认是false,可以按下ctrl键或者shift键进行多行的选择。选择好之后,我们就来看看如何删除吧:
- tbar: [{
- text: "保存",
- handler: function() {
- var modified = store.modified;
- updateData(modified);
- }
- },
- '-',
- {
- text: "增加",
- handler: function() {
- var Plant = store.recordType;
- var p = new Plant({
- common: 'New Plant 1',
- light: 'Mostly Shade',
- price: 0,
- availDate: (new Date()).clearTime(),
- indoor: false
- });
- grid.stopEditing();
- store.insert(0, p);
- grid.startEditing(0, 0);
- }
- },
- " ",
- {
- text: "删除",
- handler: function() {
- var selModel = grid.getSelectionModel();
- if (selModel.hasSelection()) {
- Ext.Msg.confirm("警告", "确定要删除吗?", function(button) {
- if (button == "yes") {
- var selections = selModel.getSelections();
- Ext.each(selections, function(item) {
- store.remove(item);
- });
- }
- });
- }
- else {
- Ext.Msg.alert("错误", "没有任何行被选中,无法进行删除操作!");
- }
- }
- }
- ]
我们先通过grid的getSelectionModel方法获得当前的选择模型,由于当前我们使用了行选择模型,所以返回的是RowSelectionModel的对象,然后通过该对象的hasSelection方法判断有没有行呗选中,没有的话就弹出一个对话框,如果有选中的行要被删除,弹出一个提示框让用户确定删除操作,如果确实要删除,使用RowSelectionModel对象的getSelections方法返回所有被选择行的集合,遍历此集合,从store中去掉集合中的行。看看效果图:
点击“是”,所有选中的行被删除,通过firebug可以看到被删除的行在store的removed属性中保存。
但是有些人可能不习惯这里所谓的行选择模型,他们可能喜欢看到在每一行的前边有一个checkbox,勾选了之后表示选择该行,extjs中的CheckboxSelectionModel类可以方便的让我们实现这一点,该类继承自RowSelectionModel,说明这只是一种特殊的行选择模型。我们具体来看看怎么进行操作。首先需要定义一个CheckboxSelectionModel的对象:var sm=new Ext.grid.CheckboxSelctionModle({checkOnly:true}),这里chckOnly:true表明只能通过checkbox来选择行,为false的话则还可以通过我们上边提到的方法来选择行。接下来需要把原来的行选择模型替换成我们新的sm,然后运行一下看看吧。我们没有看到预期中的效果,这是为什么呢?仔细想一想,确实应该是看不到什么效果,我们知道列都是在ColumnModel里边配置的,我们的CheckboxSelectionModel需要添加一列,自然也要在其中配置。那我们就加上去吧,完整的代码如下:
- ///<reference path="vswd-ext_2.0.2.js" />
- /*
- *作者:大笨
- *日期:2009-10-20
- *版本:1.0
- *博客地址:http://yage.cnblogs.com
- *QQ:14202190
- */
- Ext.BLANK_IMAGE_URL = '../extjs/resources/images/default/s.gif';
- Ext.onReady(function() {
- Ext.QuickTips.init();
- //格式化日期
- function formatDate(value) {
- return value ? value.dateFormat('Y年m月d日') : '';
- }
- // 别名
- var fm = Ext.form;
- //checkbox选择模型
- var sm = new Ext.grid.CheckboxSelectionModel({ checkOnly: true });
- //构造一个只能包含checkbox的列
- var checkColumn = new Ext.grid.CheckColumn({
- header: 'Indoor?',
- dataIndex: 'indoor',
- width: 55
- });
- // 构造ColumnModel
- var cm = new Ext.grid.ColumnModel({
- columns: [
- sm,
- {
- id: 'common',
- header: 'Common Name',
- dataIndex: 'common',
- width: 220,
- // 使用上边定义好的别名
- editor: new fm.TextField({
- allowBlank: false
- })
- }, {
- header: 'Light',
- dataIndex: 'light',
- width: 130,
- editor: new fm.ComboBox({
- typeAhead: true,
- triggerAction: 'all',
- transform: 'light',
- lazyRender: true,
- listClass: 'x-combo-list-small'
- })
- }, {
- header: 'Price',
- dataIndex: 'price',
- width: 70,
- align: 'right',
- renderer: 'usMoney',
- editor: new fm.NumberField({
- allowBlank: false,
- allowNegative: false,
- maxValue: 100000
- })
- }, {
- header: 'Available',
- dataIndex: 'availDate',
- width: 95,
- renderer: formatDate,
- editor: new fm.DateField({
- format: 'Y年m月d日',
- minValue: '01/01/06',
- disabledDays: [0, 6],
- disabledDaysText: 'Plants are not available on the weekends'
- })
- },
- checkColumn,
- ],
- defaults: {
- sortable: true
- }
- });
- // 构造一个Store对象
- var store = new Ext.data.Store({
- url: 'plants.xml',
- reader: new Ext.data.XmlReader(
- {
- record: 'plant'
- },
- [
- { name: 'common', type: 'string' },
- { name: 'botanical', type: 'string' },
- { name: 'light' },
- { name: 'price', type: 'float' },
- { name: 'availDate', mapping: 'availability', type: 'date', dateFormat: 'm/d/Y' },
- { name: 'indoor', type: 'bool' }
- ]
- ),
- sortInfo: { field: 'common', direction: 'ASC' }
- });
- // 构造可编辑的grid
- var grid = new Ext.grid.EditorGridPanel({
- //sm: new Ext.grid.RowSelectionModel({ singleSelect: false }),
- sm: sm,
- store: store,
- cm: cm,
- renderTo: 'grid',
- width: 600,
- height: 300,
- autoExpandColumn: 'common',
- title: 'Edit Plants?',
- frame: true,
- plugins: checkColumn,
- clicksToEdit: 2,
- listeners: {
- "afterEdit": {
- fn: afterEdit,
- scope: this
- }
- },
- tbar: [{
- text: "保存",
- handler: function() {
- var modified = store.modified;
- updateData(modified);
- }
- },
- '-',
- {
- text: "增加",
- handler: function() {
- var Plant = store.recordType;
- var p = new Plant({
- common: 'New Plant 1',
- light: 'Mostly Shade',
- price: 0,
- availDate: (new Date()).clearTime(),
- indoor: false
- });
- grid.stopEditing();
- store.insert(0, p);
- grid.startEditing(0, 0);
- }
- },
- " ",
- {
- text: "删除",
- handler: function() {
- var selModel = grid.getSelectionModel();
- if (selModel.hasSelection()) {
- Ext.Msg.confirm("警告", "确定要删除吗?", function(button) {
- if (button == "yes") {
- var selections = selModel.getSelections();
- Ext.each(selections, function(item) {
- store.remove(item);
- store.removed.push(item);
- });
- }
- });
- }
- else {
- Ext.Msg.alert("错误", "没有任何行被选中,无法进行删除操作!");
- }
- }
- }
- ]
- });
- // 触发数据的加载
- store.load();
- //发送数据到服务器端进行更新
- function updateData(modified) {
- var json = [];
- Ext.each(modified, function(item) {
- json.push(item.data);
- });
- if (json.length > 0) {
- Ext.Ajax.request({
- url: "EditGrid.aspx",
- params: { data: Ext.util.JSON.encode(json) },
- method: "POST",
- success: function(response) {
- Ext.Msg.alert("信息", "数据更新成功!", function() { store.reload(); });
- },
- failure: function(response) {
- Ext.Msg.alert("警告", "数据更新失败,请稍后再试!");
- }
- });
- }
- else {
- Ext.Msg.alert("警告", "没有任何需要更新的数据!");
- }
- }
- //编辑后触发的事件,可在此进行数据有效性的验证
- function afterEdit(e) {
- if (e.field == "common") {
- if (e.value == "大笨") {
- Ext.Msg.alert("错误", "大笨是人物不是植物", function() { grid.startEditing(e.row, e.column) });
- }
- }
- }
- });
再运行一下看看,OK,我们想要的效果出来了:
最后我们来总结一下,当修改grid中的内容时,Store类的modified会自动将被修改的行保存下来;当删除的时候,Store类给我们提供了一个 removed属性,但不知道为什么不自动保存删除的行,不过自己添加也很简单;增加是最麻烦的,我们可能需要一些小技巧来保存添加的行,无论是修改的还是未修改的,可以都放到Store类的modified属性之中,也可以放在自己建的一个集合中,但是需要从modified里边去被添加之后修改的行。无论是编辑,增加或者删除操作,都是在本地进行,需要自己写代码将改动发送到服务器端进行处理。