/** * @class Ext.grid.plugin.RowEditing * @extends Ext.grid.plugin.Editing * * The Ext.grid.plugin.RowEditing plugin injects editing at a row level for a Grid. When editing begins, * a small floating dialog will be shown for the appropriate row. Each editable column will show a field * for editing. There is a button to save or cancel all changes for the edit. * * The field that will be used for the editor is defined at the * {@link Ext.grid.column.Column#field field}. The editor can be a field instance or a field configuration. * If an editor is not specified for a particular column then that column won't be editable and the value of * the column will be displayed. * * The editor may be shared for each column in the grid, or a different one may be specified for each column. * An appropriate field type should be chosen to match the data structure that it will be editing. For example, * to edit a date, it would be useful to specify {@link Ext.form.field.Date} as the editor. * * {@img Ext.grid.plugin.RowEditing/Ext.grid.plugin.RowEditing.png Ext.grid.plugin.RowEditing plugin} * * ## Example Usage * * Ext.create('Ext.data.Store', { * storeId:'simpsonsStore', * fields:['name', 'email', 'phone'], * data:{'items':[ * {"name":"Lisa", "email":"lisa@simpsons.com", "phone":"555-111-1224"}, * {"name":"Bart", "email":"bart@simpsons.com", "phone":"555--222-1234"}, * {"name":"Homer", "email":"home@simpsons.com", "phone":"555-222-1244"}, * {"name":"Marge", "email":"marge@simpsons.com", "phone":"555-222-1254"} * ]}, * proxy: { * type: 'memory', * reader: { * type: 'json', * root: 'items' * } * } * }); * * Ext.create('Ext.grid.Panel', { * title: 'Simpsons', * store: Ext.data.StoreManager.lookup('simpsonsStore'), * columns: [ * {header: 'Name', dataIndex: 'name', field: 'textfield'}, * {header: 'Email', dataIndex: 'email', flex:1, * editor: { * xtype:'textfield', * allowBlank:false * } * }, * {header: 'Phone', dataIndex: 'phone'} * ], * selType: 'rowmodel', * plugins: [ * Ext.create('Ext.grid.plugin.RowEditing', { * clicksToEdit: 1 * }) * ], * height: 200, * width: 400, * renderTo: Ext.getBody() * }); */ Ext.define('Ext.grid.plugin.RowEditing', { extend: 'Ext.grid.plugin.Editing', alias: 'plugin.rowediting', requires: [ 'Ext.grid.RowEditor' ], editStyle: 'row', /** * @cfg {Boolean} autoCancel * `true` to automatically cancel any pending changes when the row editor begins editing a new row. * `false` to force the user to explicitly cancel the pending changes. Defaults to `true`. * @markdown */ autoCancel: true, /** * @cfg {Number} clicksToMoveEditor * The number of clicks to move the row editor to a new row while it is visible and actively editing another row. * This will default to the same value as {@link Ext.grid.plugin.Editing#clicksToEdit clicksToEdit}. * @markdown */ /** * @cfg {Boolean} errorSummary * `true` to show a {@link Ext.tip.ToolTip tooltip} that summarizes all validation errors present * in the row editor. Set to `false` to prevent the tooltip from showing. Defaults to `true`. * @markdown */ errorSummary: true, /** * @event beforeedit * Fires before row editing is triggered. The edit event object has the following properties <br /> * <ul style="padding:5px;padding-left:16px;"> * <li>grid - The grid this editor is on</li> * <li>view - The grid view</li> * <li>store - The grid store</li> * <li>record - The record being edited</li> * <li>row - The grid table row</li> * <li>column - The grid {@link Ext.grid.column.Column Column} defining the column that initiated the edit</li> * <li>rowIdx - The row index that is being edited</li> * <li>colIdx - The column index that initiated the edit</li> * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li> * </ul> * @param {Ext.grid.plugin.Editing} editor * @param {Object} e An edit event (see above for description) */ /** * @event edit * Fires after a row is edited. The edit event object has the following properties <br /> * <ul style="padding:5px;padding-left:16px;"> * <li>grid - The grid this editor is on</li> * <li>view - The grid view</li> * <li>store - The grid store</li> * <li>record - The record being edited</li> * <li>row - The grid table row</li> * <li>column - The grid {@link Ext.grid.column.Column Column} defining the column that initiated the edit</li> * <li>rowIdx - The row index that is being edited</li> * <li>colIdx - The column index that initiated the edit</li> * </ul> * * <pre><code> grid.on('edit', onEdit, this); function onEdit(e) { // execute an XHR to send/commit data to the server, in callback do (if successful): e.record.commit(); }; * </code></pre> * @param {Ext.grid.plugin.Editing} editor * @param {Object} e An edit event (see above for description) */ /** * @event validateedit * Fires after a cell is edited, but before the value is set in the record. Return false * to cancel the change. The edit event object has the following properties <br /> * <ul style="padding:5px;padding-left:16px;"> * <li>grid - The grid this editor is on</li> * <li>view - The grid view</li> * <li>store - The grid store</li> * <li>record - The record being edited</li> * <li>row - The grid table row</li> * <li>column - The grid {@link Ext.grid.column.Column Column} defining the column that initiated the edit</li> * <li>rowIdx - The row index that is being edited</li> * <li>colIdx - The column index that initiated the edit</li> * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li> * </ul> * Usage example showing how to remove the red triangle (dirty record indicator) from some * records (not all). By observing the grid's validateedit event, it can be cancelled if * the edit occurs on a targeted row (for example) and then setting the field's new value * in the Record directly: * <pre><code> grid.on('validateedit', function(e) { var myTargetRow = 6; if (e.rowIdx == myTargetRow) { e.cancel = true; e.record.data[e.field] = e.value; } }); * </code></pre> * @param {Ext.grid.plugin.Editing} editor * @param {Object} e An edit event (see above for description) */ constructor: function() { var me = this; me.callParent(arguments); if (!me.clicksToMoveEditor) { me.clicksToMoveEditor = me.clicksToEdit; } me.autoCancel = !!me.autoCancel; }, /** * @private * AbstractComponent calls destroy on all its plugins at destroy time. */ destroy: function() { var me = this; Ext.destroy(me.editor); me.callParent(arguments); }, /** * Start editing the specified record, using the specified Column definition to define which field is being edited. * @param {Model} record The Store data record which backs the row to be edited. * @param {Model} columnHeader The Column object defining the column to be edited. * @override */ startEdit: function(record, columnHeader) { var me = this, editor = me.getEditor(); if (me.callParent(arguments) === false) { return false; } // Fire off our editor if (editor.beforeEdit() !== false) { editor.startEdit(me.context.record, me.context.column); } }, // private cancelEdit: function() { var me = this; if (me.editing) { me.getEditor().cancelEdit(); me.callParent(arguments); } }, // private completeEdit: function() { var me = this; if (me.editing && me.validateEdit()) { me.editing = false; me.fireEvent('edit', me.context); } }, // private validateEdit: function() { var me = this; return me.callParent(arguments) && me.getEditor().completeEdit(); }, // private getEditor: function() { var me = this; if (!me.editor) { me.editor = me.initEditor(); } return me.editor; }, // private initEditor: function() { var me = this, grid = me.grid, view = me.view, headerCt = grid.headerCt; return Ext.create('Ext.grid.RowEditor', { autoCancel: me.autoCancel, errorSummary: me.errorSummary, fields: headerCt.getGridColumns(), hidden: true, // keep a reference.. editingPlugin: me, renderTo: view.el }); }, // private initEditTriggers: function() { var me = this, grid = me.grid, view = me.view, headerCt = grid.headerCt, moveEditorEvent = me.clicksToMoveEditor === 1 ? 'click' : 'dblclick'; me.callParent(arguments); if (me.clicksToMoveEditor !== me.clicksToEdit) { me.mon(view, 'cell' + moveEditorEvent, me.moveEditorByClick, me); } view.on('render', function() { // Column events me.mon(headerCt, { add: me.onColumnAdd, remove: me.onColumnRemove, columnresize: me.onColumnResize, columnhide: me.onColumnHide, columnshow: me.onColumnShow, columnmove: me.onColumnMove, scope: me }); }, me, { single: true }); }, startEditByClick: function() { var me = this; if (!me.editing || me.clicksToMoveEditor === me.clicksToEdit) { me.callParent(arguments); } }, moveEditorByClick: function() { var me = this; if (me.editing) { me.superclass.startEditByClick.apply(me, arguments); } }, // private onColumnAdd: function(ct, column) { if (column.isHeader) { var me = this, editor; me.initFieldAccessors(column); editor = me.getEditor(); if (editor && editor.onColumnAdd) { editor.onColumnAdd(column); } } }, // private onColumnRemove: function(ct, column) { if (column.isHeader) { var me = this, editor = me.getEditor(); if (editor && editor.onColumnRemove) { editor.onColumnRemove(column); } me.removeFieldAccessors(column); } }, // private onColumnResize: function(ct, column, width) { if (column.isHeader) { var me = this, editor = me.getEditor(); if (editor && editor.onColumnResize) { editor.onColumnResize(column, width); } } }, // private onColumnHide: function(ct, column) { // no isHeader check here since its already a columnhide event. var me = this, editor = me.getEditor(); if (editor && editor.onColumnHide) { editor.onColumnHide(column); } }, // private onColumnShow: function(ct, column) { // no isHeader check here since its already a columnshow event. var me = this, editor = me.getEditor(); if (editor && editor.onColumnShow) { editor.onColumnShow(column); } }, // private onColumnMove: function(ct, column, fromIdx, toIdx) { // no isHeader check here since its already a columnmove event. var me = this, editor = me.getEditor(); if (editor && editor.onColumnMove) { editor.onColumnMove(column, fromIdx, toIdx); } }, // private setColumnField: function(column, field) { var me = this; me.callParent(arguments); me.getEditor().setField(column.field, column); } });