Editing.html 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>The source code</title>
  6. <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  7. <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  8. <style type="text/css">
  9. .highlight { display: block; background-color: #ddd; }
  10. </style>
  11. <script type="text/javascript">
  12. function highlight() {
  13. document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
  14. }
  15. </script>
  16. </head>
  17. <body onload="prettyPrint(); highlight();">
  18. <pre class="prettyprint lang-js"><span id='Ext-grid-plugin-Editing'>/**
  19. </span> * This class provides an abstract grid editing plugin on selected {@link Ext.grid.column.Column columns}.
  20. * The editable columns are specified by providing an {@link Ext.grid.column.Column#editor editor}
  21. * in the {@link Ext.grid.column.Column column configuration}.
  22. *
  23. * **Note:** This class should not be used directly. See {@link Ext.grid.plugin.CellEditing} and
  24. * {@link Ext.grid.plugin.RowEditing}.
  25. */
  26. Ext.define('Ext.grid.plugin.Editing', {
  27. alias: 'editing.editing',
  28. extend: 'Ext.AbstractPlugin',
  29. requires: [
  30. 'Ext.grid.column.Column',
  31. 'Ext.util.KeyNav'
  32. ],
  33. mixins: {
  34. observable: 'Ext.util.Observable'
  35. },
  36. <span id='Ext-grid-plugin-Editing-cfg-clicksToEdit'> /**
  37. </span> * @cfg {Number} clicksToEdit
  38. * The number of clicks on a grid required to display the editor.
  39. * The only accepted values are **1** and **2**.
  40. */
  41. clicksToEdit: 2,
  42. <span id='Ext-grid-plugin-Editing-cfg-triggerEvent'> /**
  43. </span> * @cfg {String} triggerEvent
  44. * The event which triggers editing. Supercedes the {@link #clicksToEdit} configuration. Maybe one of:
  45. *
  46. * * cellclick
  47. * * celldblclick
  48. * * cellfocus
  49. * * rowfocus
  50. */
  51. triggerEvent: undefined,
  52. // private
  53. defaultFieldXType: 'textfield',
  54. // cell, row, form
  55. editStyle: '',
  56. constructor: function(config) {
  57. var me = this;
  58. me.addEvents(
  59. <span id='Ext-grid-plugin-Editing-event-beforeedit'> /**
  60. </span> * @event beforeedit
  61. * Fires before editing is triggered. Return false from event handler to stop the editing.
  62. *
  63. * @param {Ext.grid.plugin.Editing} editor
  64. * @param {Object} e An edit event with the following properties:
  65. *
  66. * - grid - The grid
  67. * - record - The record being edited
  68. * - field - The field name being edited
  69. * - value - The value for the field being edited.
  70. * - row - The grid table row
  71. * - column - The grid {@link Ext.grid.column.Column Column} defining the column that is being edited.
  72. * - rowIdx - The row index that is being edited
  73. * - colIdx - The column index that is being edited
  74. * - cancel - Set this to true to cancel the edit or return false from your handler.
  75. * - originalValue - Alias for value (only when using {@link Ext.grid.plugin.CellEditing CellEditing}).
  76. */
  77. 'beforeedit',
  78. <span id='Ext-grid-plugin-Editing-event-edit'> /**
  79. </span> * @event edit
  80. * Fires after a editing. Usage example:
  81. *
  82. * grid.on('edit', function(editor, e) {
  83. * // commit the changes right after editing finished
  84. * e.record.commit();
  85. * });
  86. *
  87. * @param {Ext.grid.plugin.Editing} editor
  88. * @param {Object} e An edit event with the following properties:
  89. *
  90. * - grid - The grid
  91. * - record - The record that was edited
  92. * - field - The field name that was edited
  93. * - value - The value being set
  94. * - row - The grid table row
  95. * - column - The grid {@link Ext.grid.column.Column Column} defining the column that was edited.
  96. * - rowIdx - The row index that was edited
  97. * - colIdx - The column index that was edited
  98. * - originalValue - The original value for the field, before the edit (only when using {@link Ext.grid.plugin.CellEditing CellEditing})
  99. * - originalValues - The original values for the field, before the edit (only when using {@link Ext.grid.plugin.RowEditing RowEditing})
  100. * - newValues - The new values being set (only when using {@link Ext.grid.plugin.RowEditing RowEditing})
  101. * - view - The grid view (only when using {@link Ext.grid.plugin.RowEditing RowEditing})
  102. * - store - The grid store (only when using {@link Ext.grid.plugin.RowEditing RowEditing})
  103. */
  104. 'edit',
  105. <span id='Ext-grid-plugin-Editing-event-validateedit'> /**
  106. </span> * @event validateedit
  107. * Fires after editing, but before the value is set in the record. Return false from event handler to
  108. * cancel the change.
  109. *
  110. * Usage example showing how to remove the red triangle (dirty record indicator) from some records (not all). By
  111. * observing the grid's validateedit event, it can be cancelled if the edit occurs on a targeted row (for example)
  112. * and then setting the field's new value in the Record directly:
  113. *
  114. * grid.on('validateedit', function(editor, e) {
  115. * var myTargetRow = 6;
  116. *
  117. * if (e.rowIdx == myTargetRow) {
  118. * e.cancel = true;
  119. * e.record.data[e.field] = e.value;
  120. * }
  121. * });
  122. *
  123. * @param {Ext.grid.plugin.Editing} editor
  124. * @param {Object} e An edit event with the following properties:
  125. *
  126. * - grid - The grid
  127. * - record - The record being edited
  128. * - field - The field name being edited
  129. * - value - The value being set
  130. * - row - The grid table row
  131. * - column - The grid {@link Ext.grid.column.Column Column} defining the column that is being edited.
  132. * - rowIdx - The row index that is being edited
  133. * - colIdx - The column index that is being edited
  134. * - cancel - Set this to true to cancel the edit or return false from your handler.
  135. * - originalValue - The original value for the field, before the edit (only when using {@link Ext.grid.plugin.CellEditing CellEditing})
  136. * - originalValues - The original values for the field, before the edit (only when using {@link Ext.grid.plugin.RowEditing RowEditing})
  137. * - newValues - The new values being set (only when using {@link Ext.grid.plugin.RowEditing RowEditing})
  138. * - view - The grid view (only when using {@link Ext.grid.plugin.RowEditing RowEditing})
  139. * - store - The grid store (only when using {@link Ext.grid.plugin.RowEditing RowEditing})
  140. */
  141. 'validateedit',
  142. <span id='Ext-grid-plugin-Editing-event-canceledit'> /**
  143. </span> * @event canceledit
  144. * Fires when the user started editing but then cancelled the edit.
  145. * @param {Ext.grid.plugin.Editing} editor
  146. * @param {Object} e An edit event with the following properties:
  147. *
  148. * - grid - The grid
  149. * - record - The record that was edited
  150. * - field - The field name that was edited
  151. * - value - The value being set
  152. * - row - The grid table row
  153. * - column - The grid {@link Ext.grid.column.Column Column} defining the column that was edited.
  154. * - rowIdx - The row index that was edited
  155. * - colIdx - The column index that was edited
  156. * - view - The grid view
  157. * - store - The grid store
  158. */
  159. 'canceledit'
  160. );
  161. me.callParent(arguments);
  162. me.mixins.observable.constructor.call(me);
  163. // TODO: Deprecated, remove in 5.0
  164. me.on(&quot;edit&quot;, function(editor, e) {
  165. me.fireEvent(&quot;afteredit&quot;, editor, e);
  166. });
  167. },
  168. // private
  169. init: function(grid) {
  170. var me = this;
  171. me.grid = grid;
  172. me.view = grid.view;
  173. me.initEvents();
  174. me.mon(grid, 'reconfigure', me.onReconfigure, me);
  175. me.onReconfigure();
  176. grid.relayEvents(me, [
  177. <span id='Ext-panel-Table-event-beforeedit'> /**
  178. </span> * @event beforeedit
  179. * Forwarded event from Ext.grid.plugin.Editing.
  180. * @member Ext.panel.Table
  181. * @inheritdoc Ext.grid.plugin.Editing#beforeedit
  182. */
  183. 'beforeedit',
  184. <span id='Ext-panel-Table-event-edit'> /**
  185. </span> * @event edit
  186. * Forwarded event from Ext.grid.plugin.Editing.
  187. * @member Ext.panel.Table
  188. * @inheritdoc Ext.grid.plugin.Editing#edit
  189. */
  190. 'edit',
  191. <span id='Ext-panel-Table-event-validateedit'> /**
  192. </span> * @event validateedit
  193. * Forwarded event from Ext.grid.plugin.Editing.
  194. * @member Ext.panel.Table
  195. * @inheritdoc Ext.grid.plugin.Editing#validateedit
  196. */
  197. 'validateedit',
  198. <span id='Ext-panel-Table-event-canceledit'> /**
  199. </span> * @event canceledit
  200. * Forwarded event from Ext.grid.plugin.Editing.
  201. * @member Ext.panel.Table
  202. * @inheritdoc Ext.grid.plugin.Editing#canceledit
  203. */
  204. 'canceledit'
  205. ]);
  206. // Marks the grid as editable, so that the SelectionModel
  207. // can make appropriate decisions during navigation
  208. grid.isEditable = true;
  209. grid.editingPlugin = grid.view.editingPlugin = me;
  210. },
  211. <span id='Ext-grid-plugin-Editing-method-onReconfigure'> /**
  212. </span> * Fires after the grid is reconfigured
  213. * @private
  214. */
  215. onReconfigure: function() {
  216. this.initFieldAccessors(this.view.getGridColumns());
  217. },
  218. <span id='Ext-grid-plugin-Editing-method-destroy'> /**
  219. </span> * @private
  220. * AbstractComponent calls destroy on all its plugins at destroy time.
  221. */
  222. destroy: function() {
  223. var me = this,
  224. grid = me.grid;
  225. Ext.destroy(me.keyNav);
  226. me.removeFieldAccessors(grid.getView().getGridColumns());
  227. // Clear all listeners from all our events, clear all managed listeners we added to other Observables
  228. me.clearListeners();
  229. delete me.grid.editingPlugin;
  230. delete me.grid.view.editingPlugin;
  231. delete me.grid;
  232. delete me.view;
  233. delete me.editor;
  234. delete me.keyNav;
  235. },
  236. // private
  237. getEditStyle: function() {
  238. return this.editStyle;
  239. },
  240. // private
  241. initFieldAccessors: function(columns) {
  242. columns = [].concat(columns);
  243. var me = this,
  244. c,
  245. cLen = columns.length,
  246. column;
  247. for (c = 0; c &lt; cLen; c++) {
  248. column = columns[c];
  249. Ext.applyIf(column, {
  250. getEditor: function(record, defaultField) {
  251. return me.getColumnField(this, defaultField);
  252. },
  253. setEditor: function(field) {
  254. me.setColumnField(this, field);
  255. }
  256. });
  257. }
  258. },
  259. // private
  260. removeFieldAccessors: function(columns) {
  261. columns = [].concat(columns);
  262. var c,
  263. cLen = columns.length,
  264. column;
  265. for (c = 0; c &lt; cLen; c++) {
  266. column = columns[c];
  267. delete column.getEditor;
  268. delete column.setEditor;
  269. }
  270. },
  271. // private
  272. // remaps to the public API of Ext.grid.column.Column.getEditor
  273. getColumnField: function(columnHeader, defaultField) {
  274. var field = columnHeader.field;
  275. if (!field &amp;&amp; columnHeader.editor) {
  276. field = columnHeader.editor;
  277. delete columnHeader.editor;
  278. }
  279. if (!field &amp;&amp; defaultField) {
  280. field = defaultField;
  281. }
  282. if (field) {
  283. if (Ext.isString(field)) {
  284. field = { xtype: field };
  285. }
  286. if (!field.isFormField) {
  287. field = Ext.ComponentManager.create(field, this.defaultFieldXType);
  288. }
  289. columnHeader.field = field;
  290. Ext.apply(field, {
  291. name: columnHeader.dataIndex
  292. });
  293. return field;
  294. }
  295. },
  296. // private
  297. // remaps to the public API of Ext.grid.column.Column.setEditor
  298. setColumnField: function(column, field) {
  299. if (Ext.isObject(field) &amp;&amp; !field.isFormField) {
  300. field = Ext.ComponentManager.create(field, this.defaultFieldXType);
  301. }
  302. column.field = field;
  303. },
  304. // private
  305. initEvents: function() {
  306. var me = this;
  307. me.initEditTriggers();
  308. me.initCancelTriggers();
  309. },
  310. // @abstract
  311. initCancelTriggers: Ext.emptyFn,
  312. // private
  313. initEditTriggers: function() {
  314. var me = this,
  315. view = me.view;
  316. // Listen for the edit trigger event.
  317. if (me.triggerEvent == 'cellfocus') {
  318. me.mon(view, 'cellfocus', me.onCellFocus, me);
  319. } else if (me.triggerEvent == 'rowfocus') {
  320. me.mon(view, 'rowfocus', me.onRowFocus, me);
  321. } else {
  322. // Prevent the View from processing when the SelectionModel focuses.
  323. // This is because the SelectionModel processes the mousedown event, and
  324. // focusing causes a scroll which means that the subsequent mouseup might
  325. // take place at a different document XY position, and will therefore
  326. // not trigger a click.
  327. // This Editor must call the View's focusCell method directly when we recieve a request to edit
  328. if (view.selModel.isCellModel) {
  329. view.onCellFocus = Ext.Function.bind(me.beforeViewCellFocus, me);
  330. }
  331. // Listen for whichever click event we are configured to use
  332. me.mon(view, me.triggerEvent || ('cell' + (me.clicksToEdit === 1 ? 'click' : 'dblclick')), me.onCellClick, me);
  333. }
  334. // add/remove header event listeners need to be added immediately because
  335. // columns can be added/removed before render
  336. me.initAddRemoveHeaderEvents()
  337. // wait until render to initialize keynav events since they are attached to an element
  338. view.on('render', me.initKeyNavHeaderEvents, me, {single: true});
  339. },
  340. // Override of View's method so that we can pre-empt the View's processing if the view is being triggered by a mousedown
  341. beforeViewCellFocus: function(position) {
  342. // Pass call on to view if the navigation is from the keyboard, or we are not going to edit this cell.
  343. if (this.view.selModel.keyNavigation || !this.editing || !this.isCellEditable || !this.isCellEditable(position.row, position.columnHeader)) {
  344. this.view.focusCell.apply(this.view, arguments);
  345. }
  346. },
  347. // private. Used if we are triggered by the rowfocus event
  348. onRowFocus: function(record, row, rowIdx) {
  349. this.startEdit(row, 0);
  350. },
  351. // private. Used if we are triggered by the cellfocus event
  352. onCellFocus: function(record, cell, position) {
  353. this.startEdit(position.row, position.column);
  354. },
  355. // private. Used if we are triggered by a cellclick event
  356. onCellClick: function(view, cell, colIdx, record, row, rowIdx, e) {
  357. // cancel editing if the element that was clicked was a tree expander
  358. if(!view.expanderSelector || !e.getTarget(view.expanderSelector)) {
  359. this.startEdit(record, view.getHeaderAtIndex(colIdx));
  360. }
  361. },
  362. initAddRemoveHeaderEvents: function(){
  363. var me = this;
  364. me.mon(me.grid.headerCt, {
  365. scope: me,
  366. add: me.onColumnAdd,
  367. remove: me.onColumnRemove
  368. });
  369. },
  370. initKeyNavHeaderEvents: function() {
  371. var me = this;
  372. me.keyNav = Ext.create('Ext.util.KeyNav', me.view.el, {
  373. enter: me.onEnterKey,
  374. esc: me.onEscKey,
  375. scope: me
  376. });
  377. },
  378. // private
  379. onColumnAdd: function(ct, column) {
  380. if (column.isHeader) {
  381. this.initFieldAccessors(column);
  382. }
  383. },
  384. // private
  385. onColumnRemove: function(ct, column) {
  386. if (column.isHeader) {
  387. this.removeFieldAccessors(column);
  388. }
  389. },
  390. // private
  391. onEnterKey: function(e) {
  392. var me = this,
  393. grid = me.grid,
  394. selModel = grid.getSelectionModel(),
  395. record,
  396. pos,
  397. columnHeader = grid.headerCt.getHeaderAtIndex(0);
  398. // Calculate editing start position from SelectionModel
  399. // CellSelectionModel
  400. if (selModel.getCurrentPosition) {
  401. pos = selModel.getCurrentPosition();
  402. if (pos) {
  403. record = grid.store.getAt(pos.row);
  404. columnHeader = grid.headerCt.getHeaderAtIndex(pos.column);
  405. }
  406. }
  407. // RowSelectionModel
  408. else {
  409. record = selModel.getLastSelected();
  410. }
  411. // If there was a selection to provide a starting context...
  412. if (record &amp;&amp; columnHeader) {
  413. me.startEdit(record, columnHeader);
  414. }
  415. },
  416. // private
  417. onEscKey: function(e) {
  418. this.cancelEdit();
  419. },
  420. <span id='Ext-grid-plugin-Editing-method-beforeEdit'> /**
  421. </span> * @private
  422. * @template
  423. * Template method called before editing begins.
  424. * @param {Object} context The current editing context
  425. * @return {Boolean} Return false to cancel the editing process
  426. */
  427. beforeEdit: Ext.emptyFn,
  428. <span id='Ext-grid-plugin-Editing-method-startEdit'> /**
  429. </span> * Starts editing the specified record, using the specified Column definition to define which field is being edited.
  430. * @param {Ext.data.Model/Number} record The Store data record which backs the row to be edited, or index of the record in Store.
  431. * @param {Ext.grid.column.Column/Number} columnHeader The Column object defining the column to be edited, or index of the column.
  432. */
  433. startEdit: function(record, columnHeader) {
  434. var me = this,
  435. context = me.getEditingContext(record, columnHeader);
  436. if (context == null || me.beforeEdit(context) === false || me.fireEvent('beforeedit', me, context) === false || context.cancel || !me.grid.view.isVisible(true)) {
  437. return false;
  438. }
  439. me.context = context;
  440. <span id='Ext-grid-plugin-Editing-property-editing'> /**
  441. </span> * @property {Boolean} editing
  442. * Set to `true` while the editing plugin is active and an Editor is visible.
  443. */
  444. me.editing = true;
  445. },
  446. // TODO: Have this use a new class Ext.grid.CellContext for use here, and in CellSelectionModel
  447. <span id='Ext-grid-plugin-Editing-method-getEditingContext'> /**
  448. </span> * @private
  449. * Collects all information necessary for any subclasses to perform their editing functions.
  450. * @param record
  451. * @param columnHeader
  452. * @returns {Object/undefined} The editing context based upon the passed record and column
  453. */
  454. getEditingContext: function(record, columnHeader) {
  455. var me = this,
  456. grid = me.grid,
  457. view = grid.getView(),
  458. node = view.getNode(record),
  459. rowIdx, colIdx;
  460. // An intervening listener may have deleted the Record
  461. if (!node) {
  462. return;
  463. }
  464. // Coerce the column index to the closest visible column
  465. columnHeader = grid.headerCt.getVisibleHeaderClosestToIndex(Ext.isNumber(columnHeader) ? columnHeader : columnHeader.getIndex());
  466. // No corresponding column. Possible if all columns have been moved to the other side of a lockable grid pair
  467. if (!columnHeader) {
  468. return;
  469. }
  470. colIdx = columnHeader.getIndex();
  471. if (Ext.isNumber(record)) {
  472. // look up record if numeric row index was passed
  473. rowIdx = record;
  474. record = view.getRecord(node);
  475. } else {
  476. rowIdx = view.indexOf(node);
  477. }
  478. return {
  479. grid : grid,
  480. record : record,
  481. field : columnHeader.dataIndex,
  482. value : record.get(columnHeader.dataIndex),
  483. row : view.getNode(rowIdx),
  484. column : columnHeader,
  485. rowIdx : rowIdx,
  486. colIdx : colIdx
  487. };
  488. },
  489. <span id='Ext-grid-plugin-Editing-method-cancelEdit'> /**
  490. </span> * Cancels any active edit that is in progress.
  491. */
  492. cancelEdit: function() {
  493. var me = this;
  494. me.editing = false;
  495. me.fireEvent('canceledit', me, me.context);
  496. },
  497. <span id='Ext-grid-plugin-Editing-method-completeEdit'> /**
  498. </span> * Completes the edit if there is an active edit in progress.
  499. */
  500. completeEdit: function() {
  501. var me = this;
  502. if (me.editing &amp;&amp; me.validateEdit()) {
  503. me.fireEvent('edit', me, me.context);
  504. }
  505. delete me.context;
  506. me.editing = false;
  507. },
  508. // @abstract
  509. validateEdit: function() {
  510. var me = this,
  511. context = me.context;
  512. return me.fireEvent('validateedit', me, context) !== false &amp;&amp; !context.cancel;
  513. }
  514. });
  515. </pre>
  516. </body>
  517. </html>