1 /** 2 * @fileOverview 3 * @author <a href="https://www.labkey.org">LabKey</a> (<a href="mailto:info@labkey.com">info@labkey.com</a>) 4 * @license Copyright (c) 2012-2019 LabKey Corporation 5 * <p/> 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * <p/> 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * <p/> 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * <p/> 18 */ 19 20 /** 21 * @class <font color="red">DEPRECATED</font> - Consider using 22 * <a href="http://docs.sencha.com/extjs/3.4.0/#!/api/Ext.grid.EditorGridPanel">Ext.grid.EditorGridPanel</a> instead. 23 * <p> The LABKEY.ext.EditorGridPanel class is very similar to this class, except that it is a proper 24 * extension of the Ext.grid.EditorGridPanel class, and thus exposes all of its properties, methods, 25 * and events, and can participate in complex Ext layouts.</p> 26 * <p> To transition from this class to the new LABKEY.ext.EditorGridPanel class, follow these steps: 27 * <ul> 28 * <li>Create a new LABKEY.ext.EditorGridPanel instead of a LABKEY.GridView</li> 29 * <li>Ensure that you create the class after the page has fully loaded. Use the Ext.onReady() function to 30 * specify a function to execute after the page has fully loaded. See the example in the 31 * LABKEY.ext.EditorGridPanel class documentation.</li> 32 * <li>In the new grid, the data store configuration has been separated from the grid configuration. 33 * Therefore, you should move the schemaName, queryName, viewName, and containerPath config properties to 34 * the config for the LABKEY.ext.Store you create for the value of the 'store' config property. See 35 * the example in LABKEY.ext.EditorGridPanel class documentation.</li> 36 * <li>If you specify a value for the renderTo config property, there is no need to call the 37 * render() method as there was when using the old LABKEY.GridView.</li> 38 * </ul> 39 * @constructor 40 * @param {Object} config Describes the GridView's properties. 41 * @param {Object} config.schemaName Name of a schema defined within the current 42 * container. Example: 'study'. See also: <a class="link" 43 href="https://www.labkey.org/Documentation/wiki-page.view?name=findNames"> 44 How To Find schemaName, queryName & viewName</a>. 45 * @param {Object} config.queryName Name of a query defined within the specified schema 46 * in the current container. Example: 'SpecimenDetail'. See also: <a class="link" 47 href="https://www.labkey.org/Documentation/wiki-page.view?name=findNames"> 48 How To Find schemaName, queryName & viewName</a>. 49 * @param {Object} [config.viewName] Name of a custom view defined over the specified query. 50 * in the current container. Example: 'SpecimenDetail'. See also: <a class="link" 51 href="https://www.labkey.org/Documentation/wiki-page.view?name=findNames"> 52 How To Find schemaName, queryName & viewName</a>. 53 * @param {String} config.renderTo Name of the div in which to place the grid. 54 * @param {Bool} config.editable Whether the grid should be made editable. Note that 55 * not all tables and columns are editable, and not all users have 56 * permission to edit. For this reason, part or all of the grid may 57 * degrade to being non-editable despite the 'editable' parameter. 58 * @param {Object} [config.gridPanelConfig] Sets the display configuration for the new grid. This 59 * configuration is passed through to the underlying Ext.grid.GridPanel implementation, 60 * so all <a href="http://www.extjs.com/deploy/dev/docs/?class=Ext.grid.GridPanel"> 61 * GridPanel config options</a> are valid. <p/>Note that providing this configuration 62 * is optional. Further, if you do provide it, you take responsibility for 63 * providing a valid and complete config object. If you do not set the 64 * GridPanel config, LabKey Server will use a default configuration option. 65 * @param {Object} [config.storeConfig] Config object that is passed to the underlying Store. 66 * This configuration is passed through to the underlying Ext.data.Store implementation, 67 * so all <a href="http://www.extjs.com/deploy/dev/docs/?class=Ext.data.Store"> 68 * Store config options</a> are valid. <p/>Note that providing this configuration 69 * is optional. Further, if you do provide it, you take responsibility for 70 * providing a valid and complete config object. If you do not set the 71 * Store config, LabKey Server will use a default configuration option. 72 * @param {Function(columnModel)} [config.columnModelListener] Callback function that allows 73 * you to adjust the column 74 * model without providing a full GridPanel config. The columnModel 75 * element/object contains information about how one may interact with 76 * the columns within a user interface. This format is generated to match 77 * the requirements of the Ext grid component. See 78 * <a href="http://www.extjs.com/deploy/dev/docs/?class=Ext.grid.ColumnModel"> 79 * Ext.grid.ColumnModel</a> for further information. 80 * @param {Function(Ext.grid.GridPanel)} config.gridCustomizeCallback Function that should be called after the 81 * grid has been constructed and populated with data. You can use this to 82 * further customize the grid's appearance, add toolbar buttons, or call 83 * any method on the <a href="http://www.extjs.com/deploy/dev/docs/?class=Ext.grid.GridPanel"> 84 * Ext GridPanel object</a>. The function passed as this config property 85 * should look like this: 86 * @param {String} [config.containerPath] The container path in which the schemaName and queryName are defined. 87 * If not supplied, the current container path will be used. 88 */ 89 90 LABKEY.GridView = function(config) 91 { 92 Ext.QuickTips.init(); 93 94 Date.patterns = { 95 ISO8601Long:"Y-m-d H:i:s", 96 ISO8601Short:"Y-m-d" 97 }; 98 99 if (!config.schemaName || !config.queryName) 100 { 101 Ext.Msg.alert("Configuration Error", "config.schemaName and config.queryName are required parameters"); 102 return; 103 } 104 105 var _primarySchemaName = config.schemaName; 106 var _primaryQueryName = config.queryName; 107 var _primaryViewName = config.viewName; 108 var _renderTo = config.renderTo; 109 var _gridPanelConfig = config.gridPanelConfig; 110 var _storeConfig = config.storeConfig; 111 var _selectionModel; 112 var _editable = config.editable; 113 var _errorsInGridData = false; 114 115 // private member variables: 116 var _ds; 117 var _myReader; 118 var _columnModelListener = config.columnModelListener; 119 var _gridCustomizeCallback = config.gridCustomizeCallback; 120 var _pageLimit = 20; 121 var _grid; 122 var _containerPath = config.containerPath; 123 124 125 // private methods: 126 function getDefaultRenderer(fieldColumn, displayColumn) 127 { 128 switch (fieldColumn.type) 129 { 130 case "date": 131 return function(data) 132 { 133 if (!data) 134 return; 135 var date = new Date(data); 136 if (date.getHours() == 0 && date.getMinutes() == 0 && date.getSeconds() == 0) 137 return date.format(Date.patterns.ISO8601Short); 138 else 139 return date.format(Date.patterns.ISO8601Long) 140 }; 141 break; 142 case "boolean": 143 case "int": 144 case "float": 145 case "string": 146 default: 147 } 148 } 149 150 function getDefaultEditor(fieldColumn, displayColumn) 151 { 152 if (displayColumn.editable) 153 { 154 var editor; 155 switch (fieldColumn.type) 156 { 157 case "boolean": 158 editor = new Ext.form.Checkbox(); 159 break; 160 case "int": 161 editor = new Ext.form.NumberField({ 162 allowDecimals : false 163 }); 164 break; 165 case "float": 166 editor = new Ext.form.NumberField({ 167 allowDecimals : true 168 }); 169 break; 170 case "date": 171 editor = new Ext.form.DateField({ 172 format : Date.patterns.ISO8601Long, 173 altFormats: Date.patterns.ISO8601Short + 174 'n/j/y g:i:s a|n/j/Y g:i:s a|n/j/y G:i:s|n/j/Y G:i:s|' + 175 'n-j-y g:i:s a|n-j-Y g:i:s a|n-j-y G:i:s|n-j-Y G:i:s|' + 176 'n/j/y g:i a|n/j/Y g:i a|n/j/y G:i|n/j/Y G:i|' + 177 'n-j-y g:i a|n-j-Y g:i a|n-j-y G:i|n-j-Y G:i|' + 178 'j-M-y g:i a|j-M-Y g:i a|j-M-y G:i|j-M-Y G:i|' + 179 'n/j/y|n/j/Y|' + 180 'n-j-y|n-j-Y|' + 181 'j-M-y|j-M-Y|' + 182 'Y-n-d H:i:s|Y-n-d' 183 }); 184 break; 185 case "string": 186 editor = new Ext.form.TextField(); 187 break; 188 default: 189 } 190 if (editor) 191 { 192 editor.allowBlank = !displayColumn.required; 193 registerEditorListeners(editor); 194 } 195 return editor; 196 } 197 } 198 199 function getLookupEditor(dsLookup, lookupDef, allowNull) 200 { 201 var editor = new Ext.form.ComboBox({ //dropdown based on server side data (from db) 202 typeAhead: false, //will be querying database so may not want typeahead consuming resources 203 triggerAction: 'all', 204 editable:false, 205 lazyRender: true,//prevents combo box from rendering until requested, should always be true for editor 206 store: dsLookup,//Industry,//where to get the data for our combobox 207 displayField: lookupDef.displayColumn,//the underlying data field name to bind to this ComboBox 208 //(defaults to undefined if mode = 'remote' or 'text' if transforming a select) 209 valueField: lookupDef.keyColumn, //the underlying value field name to bind to this ComboBox 210 tpl : '<tpl for="."><div class="x-combo-list-item">{[values["' + lookupDef.displayColumn + '"]]}</div></tpl>', 211 allowBlank : allowNull 212 }); 213 registerEditorListeners(editor); 214 return editor; 215 } 216 217 function registerEditorListeners(editor) 218 { 219 editor.addListener("complete", afterCellEdit); 220 editor.addListener("beforeshow", function() 221 { 222 document.ActiveExtGridViewCellId = editor.id; 223 return true; 224 }); 225 } 226 227 // this function creates a closure that allows the references to dsLookup and 228 // lookupDef to stick to the render function: 229 function getLookupRenderer(dsLookup, lookupDef) 230 { 231 var refreshed = false; 232 dsLookup.on("load", function(store, recordArray, options) 233 { 234 if (_grid && !refreshed) 235 _grid.getView().refresh(); 236 refreshed = true; 237 store.un("load", this); 238 }); 239 240 return function(data) 241 { 242 var record = dsLookup.getById(data); 243 if (record) 244 return record.data[lookupDef.displayColumn]; 245 else if (data) 246 return '[' + data + ']'; 247 else 248 return '[None]'; 249 }; 250 } 251 252 function initColumnUI() 253 { 254 var columnModelNameMap = {}; 255 for (var columnId in _myReader.jsonData.columnModel) 256 { 257 var column = _myReader.jsonData.columnModel[columnId]; 258 columnModelNameMap[column.dataIndex] = column; 259 } 260 261 for (var fieldId = 0; fieldId < _myReader.jsonData.metaData.fields.length; fieldId++) 262 { 263 var fieldColumn = _myReader.jsonData.metaData.fields[fieldId]; 264 var displayColumn = columnModelNameMap[fieldColumn.name]; 265 if (fieldColumn.lookup) 266 { 267 var lookupDef = fieldColumn.lookup; 268 var allowNull = !displayColumn.required; 269 var storeConfig = {schemaName: lookupDef.schema, queryName: lookupDef.table, containerPath: _containerPath}; 270 if (allowNull) 271 { 272 storeConfig.allowNull = { keyColumn: lookupDef.keyColumn, displayColumn: lookupDef.displayColumn }; 273 } 274 var dsLookup = LABKEY.ext.Utils.createExtStore(storeConfig); 275 276 displayColumn.renderer = getLookupRenderer(dsLookup, lookupDef); 277 if (_editable) 278 displayColumn.editor = getLookupEditor(dsLookup, lookupDef, allowNull); 279 280 dsLookup.load(); 281 } 282 else 283 { 284 if (_editable) 285 displayColumn.editor = getDefaultEditor(fieldColumn, displayColumn); 286 displayColumn.renderer = getDefaultRenderer(fieldColumn, displayColumn); 287 } 288 } 289 } 290 291 function handleGridData(r, options, success) 292 { 293 if (!success) 294 return; 295 296 if (!_myReader || !_myReader.jsonData || !_myReader.jsonData.columnModel) 297 return; 298 299 if (!_gridPanelConfig) 300 _gridPanelConfig = {}; 301 302 _gridPanelConfig.store = _ds; 303 304 if (_columnModelListener) 305 _gridPanelConfig.columns = _columnModelListener(_myReader.jsonData.columnModel) 306 else 307 _gridPanelConfig.columns = _myReader.jsonData.columnModel; 308 309 // double check to see if there are any editable columns in this col model. If not, 310 // degrade to a non-editable grid. 311 if (_editable) 312 { 313 var anyEditable = false; 314 for (var i = 0; i < _gridPanelConfig.columns.length && !anyEditable; i++) 315 anyEditable = _gridPanelConfig.columns[i].editable; 316 if (!anyEditable) 317 _editable = false; 318 } 319 320 initColumnUI(); 321 322 if (!_gridPanelConfig.view) 323 { 324 _gridPanelConfig.view = new Ext.grid.GridView({ 325 forceFit:true, 326 // custom grouping text template to display the number of items per group 327 groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})' 328 }); 329 } 330 331 if (!_gridPanelConfig.selModel) 332 { 333 if (_editable) 334 { 335 _gridPanelConfig.selModel = new Ext.grid.CheckboxSelectionModel({singleSelect:false}); 336 _gridPanelConfig.columns = [_gridPanelConfig.selModel].concat(_gridPanelConfig.columns); 337 } 338 else 339 _gridPanelConfig.selModel = new Ext.grid.RowSelectionModel({singleSelect:true}); 340 } 341 _selectionModel = _gridPanelConfig.selModel; 342 343 if (_editable) 344 { 345 if (!_gridPanelConfig.clicksToEdit) 346 _gridPanelConfig.clicksToEdit = 2; 347 348 if (!_gridPanelConfig.tbar) 349 { 350 _gridPanelConfig.tbar = [ 351 { 352 text: 'Add Record', 353 tooltip: 'Click to add a row', 354 handler: addRecord, //what happens when user clicks on it 355 id: 'add-record-button' 356 }, '-', //add a separator 357 { 358 text: 'Delete Selected', 359 tooltip: 'Click to delete selected row(s)', 360 handler: handleDelete, //what happens when user clicks on it 361 id: 'delete-records-button' 362 }, '-', //add a separator 363 { 364 text: 'Refresh', 365 tooltip: 'Click to refresh the table', 366 id: 'refresh-button', 367 handler: refreshGrid //what happens when user clicks on it 368 } 369 ]; 370 } 371 _grid = new Ext.grid.EditorGridPanel(_gridPanelConfig); 372 _ds.addListener("beforeload", commitEdits); 373 _grid.addListener('afteredit', afterCellEdit);//give event name, handler (can use 'on' shorthand for addListener) 374 _grid.addListener('beforeedit', beforeCellEdit);//give event name, handler (can use 'on' shorthand for addListener) 375 _editCommitTimer = new Ext.util.DelayedTask(commitEdits, this) 376 } 377 else 378 { 379 if (!_gridPanelConfig.tbar) 380 { 381 _gridPanelConfig.tbar = [ 382 { 383 text: 'Refresh', 384 tooltip: 'Click to Refresh the table', 385 handler: refreshGrid //what happens when user clicks on it 386 } 387 ]; 388 } 389 _grid = new Ext.grid.GridPanel(_gridPanelConfig); 390 } 391 392 //call the grid customize callback if any 393 if(_gridCustomizeCallback) 394 _gridCustomizeCallback(_grid); 395 } 396 397 function typeConvert(fieldColumn, value) 398 { 399 if (!value && !fieldColumn.required) 400 return null; 401 switch (fieldColumn.type) 402 { 403 case "boolean": 404 // strange trick here to boolean-ify our value. From 405 // http://www.jibbering.com/faq/faq_notes/type_convert.html 406 return value instanceof Boolean ? value : !!value; 407 case "int": 408 return value instanceof Number ? value : Math.round(Number(value)); 409 case "float": 410 return value instanceof Number ? value : Number(value); 411 case "date": 412 return value instanceof Date ? value : Date(value); 413 case "string": 414 return value instanceof String ? value : "" + value; 415 default: 416 return value; 417 } 418 } 419 420 function getDefaultValues(fields) 421 { 422 var record = {}; 423 for (var i = 0; i < fields.length; i++) 424 { 425 var field = fields[i]; 426 record[field.name] = null; 427 } 428 return record; 429 } 430 431 function addRecord() 432 { 433 var fields = _myReader.jsonData.metaData.fields; 434 var recordCreator = Ext.data.Record.create(fields); 435 var newRecord = new recordCreator(getDefaultValues(fields)); 436 newRecord.LABKEY$isNew = true; 437 _grid.stopEditing();//stops any acitve editing 438 _ds.insert(0, newRecord); //1st arg is index, 439 //2nd arg is Ext.data.Record[] records 440 //very similar to ds.add, with ds.insert we can specify the insertion point 441 _grid.startEditing(0, 1);//starts editing the specified rowIndex, colIndex 442 //make sure you pick an editable location in the line above 443 //otherwise it won't initiate the editor 444 } 445 446 function isNullRecord(record) 447 { 448 for (var field in record) 449 { 450 var value = record[field]; 451 if (value) 452 return false; 453 } 454 return true; 455 } 456 457 var _currentEditRow; 458 var _editCommitTimer; 459 function beforeCellEdit(parameters) 460 { 461 if (_currentEditRow == parameters.row) 462 _editCommitTimer.cancel(); 463 _currentEditRow = parameters.row; 464 _selectionModel.selectRow(parameters.row); 465 var record = _ds.getAt(parameters.row); 466 record.saveNeeded = true; 467 } 468 469 function afterCellEdit(parameters) 470 { 471 _editCommitTimer.delay(250); 472 _errorsInGridData = false; 473 } 474 475 function commitEdits() 476 { 477 var keyColumn = _myReader.jsonData.metaData.id; 478 var records = _ds.getModifiedRecords(); 479 for (var i = 0; i < records.length; i++) 480 { 481 var record = records[i]; 482 if (!record.data.toBeDeleted && (record.data[keyColumn] || !isNullRecord(record.data))) 483 updateDB(record) 484 } 485 } 486 487 function handleDelete() 488 { 489 if (_selectionModel) 490 { 491 commitEdits(); 492 var records = _selectionModel.getSelections(); 493 if (records && records.length) 494 { 495 if (confirm("Permanently delete the selected records?")) 496 { 497 var data = []; 498 var keyColumn = _myReader.jsonData.metaData.id; 499 var uncommittedRecords = false; 500 for (var i = 0; i < records.length; i++) 501 { 502 var recordData = records[i].data; 503 if (recordData[keyColumn]) 504 data[data.length] = recordData; 505 else 506 { 507 uncommittedRecords = true; 508 recordData.toBeDeleted = true; 509 } 510 } 511 if (data.length > 0) 512 { 513 LABKEY.Query.deleteRows(_primarySchemaName, _primaryQueryName, data, 514 afterSuccessfulDelete, afterFailedEdit); 515 } 516 else if (uncommittedRecords) 517 refreshGrid(); 518 } 519 } 520 } 521 } 522 523 function refreshGrid() 524 { 525 _ds.reload(); 526 } 527 528 function afterSuccessfulDelete(responseObj, options) 529 { 530 refreshGrid(); 531 } 532 533 function afterSuccessfulEdit(responseObj, options) 534 { 535 commitSavedRows(responseObj); 536 //_ds.commitChanges();//commit changes (removes the red triangle which indicates a 'dirty' field) 537 // refreshGrid(); 538 } 539 540 function commitSavedRows(responseObj) 541 { 542 if (!responseObj.rows || responseObj.rows.length == 0) 543 return; 544 545 for (var rowIndex = 0; rowIndex < responseObj.rows.length; rowIndex++) 546 { 547 var keyValue = responseObj.rows[rowIndex][_myReader.jsonData.metaData.id]; 548 var record = _ds.getById(keyValue); 549 550 if(record) 551 { 552 //set all fields that are present in the rows[rowIndex] object 553 var retRow = responseObj.rows[rowIndex]; 554 for(var field in record.data) 555 { 556 if(retRow[field]) 557 record.set(field, retRow[field]); 558 } 559 560 record.commit(); 561 } 562 } 563 } 564 565 function getAfterSuccessfulEdit(record) 566 { 567 return function(responseObj, options) 568 { 569 record.operationPendingSinceLastEdit = false; 570 record.commit(); 571 572 //the key value may have changed in response to the edit 573 //(study dataset case) 574 var retRecord = responseObj.rows[0]; 575 var idCol = _myReader.jsonData.metaData.id; 576 if(retRecord[idCol] != record[idCol]) 577 { 578 //if the key changed, we need to create a new record, 579 //add it to the store, and remove the old one. Ext 580 //has no way to update the key of an existing record. 581 var recordCreator = Ext.data.Record.create(_myReader.jsonData.metaData.fields); 582 var newKeyRecord = new recordCreator(retRecord, retRecord[idCol]); 583 _ds.insert(_ds.indexOf(record), newKeyRecord); 584 _ds.remove(record); 585 } 586 else 587 { 588 //even if the key didn't change, other fields may 589 //have been modified at the server, so copy over the 590 //values that were returned 591 for(var field in retRecord.data) 592 record.set(field, retRecord[field]); 593 } 594 595 } 596 } 597 598 function getAfterSuccessfulInsert(newRecord) 599 { 600 return function(responseObj, options) 601 { 602 newRecord.operationPendingSinceLastEdit = false; 603 newRecord.commit(); 604 605 //create a new record based on the fields returned from the server 606 //and specify the newly-assigned id 607 //and remove the temporary newRecord 608 var row = responseObj.rows[0]; 609 var fields = _myReader.jsonData.metaData.fields; 610 var recordCreator = Ext.data.Record.create(fields); 611 var newNewRecord = new recordCreator(row, row[_myReader.jsonData.metaData.id]); 612 _ds.insert(_ds.indexOf(newRecord), newNewRecord); 613 _ds.remove(newRecord); 614 } 615 } 616 617 function afterFailedEdit(jsonResponse, options) 618 { 619 _errorsInGridData = true; 620 if (jsonResponse && jsonResponse.exception) 621 { 622 Ext.Msg.alert("Update Failed", jsonResponse.exception + "\n(Exception class " + jsonResponse.exceptionClass + ")") 623 } 624 else 625 Ext.Msg.alert("Update Failed", jsonResponse.statusText + " (Response code " + jsonResponse.status + ")"); 626 } 627 628 function updateDB(record) 629 { 630 if (_errorsInGridData) 631 return; 632 633 if (!record.saveNeeded) 634 return; 635 /* 636 * editEvent has the following properties: 637 * grid - This grid 638 * record - The record being edited 639 * field - The field name being edited 640 * value - The value being set 641 * originalValue - The original value for the field, before the edit. 642 * row - The grid row index 643 * column - The grid column index 644 */ 645 var store = _grid.getStore(); 646 var fields = store.fields; 647 var recordData = {}; 648 649 for (var fieldId = 0; fieldId < fields.length; fieldId++) 650 { 651 var field = fields.itemAt(fieldId); 652 var value = record.data[field.name]; 653 if (value != null) 654 value = typeConvert(field, value); 655 recordData[field.name] = value; 656 } 657 658 var validRecord = true; 659 for (var colId = 0; colId < _myReader.jsonData.columnModel.length && validRecord; colId++) 660 { 661 var col = _myReader.jsonData.columnModel[colId]; 662 // we allow a null key column, since that's always going to be the case for insert 663 if (col.dataIndex != store.reader.jsonData.metaData.id) 664 { 665 if (recordData[col.dataIndex] == null && col.required) 666 validRecord = false; 667 } 668 } 669 670 671 if (validRecord) 672 { 673 record.saveNeeded = false; 674 record.operationPendingSinceLastEdit = true; 675 if (record.LABKEY$isNew) //!recordData[store.reader.jsonData.metaData.id]) 676 { 677 LABKEY.Query.insertRows(_primarySchemaName, _primaryQueryName, [recordData], 678 getAfterSuccessfulInsert(record), afterFailedEdit); 679 } 680 else 681 { 682 LABKEY.Query.updateRows(_primarySchemaName, _primaryQueryName, [recordData], 683 getAfterSuccessfulEdit(record), afterFailedEdit); 684 } 685 } 686 } 687 688 689 690 function createDefaultStoreImpl() 691 { 692 if (!_storeConfig) 693 _storeConfig = {}; 694 _storeConfig.schemaName = _primarySchemaName; 695 _storeConfig.queryName = _primaryQueryName; 696 _storeConfig.viewName = _primaryViewName; 697 _storeConfig.containerPath = _containerPath; 698 return LABKEY.ext.Utils.createExtStore(_storeConfig); 699 } 700 701 function displayGridImpl() 702 { 703 if (!_gridPanelConfig) 704 _gridPanelConfig = {}; 705 _gridPanelConfig.renderTo = _renderTo; 706 707 _ds = createDefaultStoreImpl(); 708 _myReader = _ds.reader; 709 710 if (!_gridPanelConfig.bbar) 711 { 712 _gridPanelConfig.bbar = new Ext.PagingToolbar({ 713 pageSize: _pageLimit,//default is 20 714 store: _ds, 715 // paramNames : _extParamMapping, 716 emptyMsg: "No data to display"//display message when no records found 717 }); 718 } 719 720 _ds.load({ callback : handleGridData, 721 params : { 722 start: 0, 723 limit: _pageLimit 724 }}); 725 } 726 727 // public methods: 728 /** @scope LABKEY.GridView.prototype */ 729 return { 730 /** 731 * Renders the grid view to the div specified in the renderTo config property. 732 */ 733 render : function() 734 { 735 return displayGridImpl(); 736 }, 737 738 /** 739 * Returns the Ext.data.Store used to manage the data displayed in the grid. 740 * You can use the returned object to programmatically manipulate the store. 741 * <p/> 742 * See <a href='http://www.extjs.com/deploy/dev/docs/?class=Ext.data.Store'> 743 * http://www.extjs.com/deploy/dev/docs/?class=Ext.data.Store</a> for more 744 * information on the Ext.data.Store class. 745 * 746 * @example Example: 747 * <pre name="code" class="xml"> 748 * //this code will programmatically refresh the data 749 * //displayed in the myGrid object 750 * myGrid.getStore().reload(); 751 * </pre> 752 */ 753 getStore : function() 754 { 755 return _grid.getStore(); 756 } 757 } 758 }; 759 760