Implemented the server-side data access. Added automatic POSTing of client-memory data source data. Updated documentation. Minor fixes in the drop-down cell processor.

This commit is contained in:
alekseybobkov 2014-12-11 22:12:43 -08:00
parent bffd4574b6
commit c6eb544101
7 changed files with 116 additions and 17 deletions

View File

@ -43,7 +43,8 @@ class TableClientMemoryDataSource extends TableDataSourceBase
* Return records from the data source.
* @param integer $offset Specifies the offset of the first record to return, zero-based.
* @param integer $count Specifies the number of records to return.
* @return array
* @return array Returns the records.
* If there are no more records, returns an empty array.
*/
public function getRecords($offset, $count)
{

View File

@ -6,10 +6,15 @@
abstract class TableDataSourceBase
{
/**
* @var Specifies a name of record's key column
* @var string Specifies a name of record's key column
*/
protected $keyColumn;
/**
* @var integer Internal record offset
*/
protected $offset = 0;
/**
* Class constructor.
* @param string $keyColumn Specifies a name of the key column.
@ -43,7 +48,31 @@ abstract class TableDataSourceBase
* Return records from the data source.
* @param integer $offset Specifies the offset of the first record to return, zero-based.
* @param integer $count Specifies the number of records to return.
* @return array
* @return array Returns the records.
* If there are no more records, returns an empty array.
*/
abstract public function getRecords($offset, $count);
/**
* Rewinds the the data source to the first record.
* Use this method with the readRecords() method.
*/
public function reset()
{
$this->offset = 0;
}
/**
* Returns a set of records from the data source.
* @param integer $count Specifies the number of records to return.
* @return array Returns the records.
* If there are no more records, returns an empty array.
*/
public function readRecords($count = 10)
{
$result = $this->getRecords($this->offset, $count);
$this->offset += count($result);
return $result;
}
}

View File

@ -1,6 +1,7 @@
<?php namespace Backend\Widgets;
use Input;
use Request;
use Backend\Classes\WidgetBase;
use System\Classes\SystemException;
@ -57,6 +58,16 @@ class Table extends WidgetBase
throw new SystemException(sprintf('The Table widget data source class "%s" is could not be found.', $dataSourceClass));
$this->dataSource = new $dataSourceClass($this->recordsKeyColumn);
if (Request::method() == 'POST' && $this->isClientDataSource()) {
$requestDataField = $this->alias.'TableData';
if (Request::exists($requestDataField)) {
// Load data into the client memory data source on POST
$this->dataSource->purge();
$this->dataSource->initRecords(Request::input($requestDataField));
}
}
}
/**
@ -85,7 +96,7 @@ class Table extends WidgetBase
$this->vars['columns'] = $this->prepareColumnsArray();
$this->vars['recordsKeyColumn'] = $this->recordsKeyColumn;
$isClientDataSource = $this->dataSource instanceof \Backend\Classes\TableClientMemoryDataSource;
$isClientDataSource = $this->isClientDataSource();
$this->vars['clientDataSourceClass'] = $isClientDataSource ? 'client' : 'server';
$this->vars['data'] = $isClientDataSource ?
@ -132,6 +143,11 @@ class Table extends WidgetBase
return $result;
}
protected function isClientDataSource()
{
return $this->dataSource instanceof \Backend\Classes\TableClientMemoryDataSource;
}
/*
* Event handlers
*/

View File

@ -78,6 +78,8 @@ The options below are listed in the JavaScript notation. Corresponding data attr
- `columns` - column definitions in JSON format, see the server-side column definition format below.
- `rowSorting` - enables the drag & drop row sorting. The sorting cannot be used with the pagination (`recordsPerPage` is not `null` or `false`).
- `keyColumn` - specifies the name of the key column. The default value is **id**.
- `postback` - post the client-memory data source data to the server automatically when the parent form gets submitted. The default value is `true`. The option is used only with client-memory data sources. When enabled, the data source data is available in the widget's server-side data source: `$table->getDataSource()->getRecords();` The data postback occurs only of the request handler name matches the `postbackHandlerName` option value.
- `postbackHandlerName` - AJAX data handler name for the automatic data postback. The data will be posted only when the AJAX requests posts data to this handler. The default value is **onSave**.
## Client-side helper classes
@ -233,4 +235,22 @@ $dataSource->purge();
## Reading data from the data source
TODO
The server-side data sources (PHP) automatically maintain the actual data, but that mechanism for the client-memory and server-memory data sources is different.
In case of the client-memory data source, the table widget adds the data records to the POST, when the form is saved (see `postback` and `postbackHandlerName` options). On the server side the data is inserted to the data source by the table widget.
The server-memory data source always automatically maintain its contents in synch with the client using AJAX, and POSTing data is not required.
In PHP reading data from a data source of any type looks like this (it should be in the AJAX handler that saves the data, for the client-memory data source the handler name should match the `postbackHandlerName` option value):
```
public function onSave()
{
// Assuming that the widget was initialized in the
// controller constructor with the "table" alias.
$dataSource = $this->widget->table->getDataSource();
while ($records = $dataSource->readRecords(5)) {
traceLog($records);
}
```

View File

@ -136,5 +136,9 @@
}).indexOf(parseInt(key))
}
Client.prototype.getAllData = function() {
return this.data
}
$.oc.table.datasource.client = Client
}(window.jQuery);

View File

@ -57,6 +57,9 @@
this.clickHandler = this.onClick.bind(this)
this.keydownHandler = this.onKeydown.bind(this)
if (this.options.postback && this.options.clientDataSourceClass == 'client')
this.formSubmitHandler = this.onFormSubmit.bind(this)
// Navigation helper
this.navigation = null
@ -122,6 +125,9 @@
Table.prototype.registerHandlers = function() {
this.el.addEventListener('click', this.clickHandler)
this.el.addEventListener('keydown', this.keydownHandler)
if (this.options.postback && this.options.clientDataSourceClass == 'client')
this.$el.closest('form').bind('oc.beforeRequest', this.formSubmitHandler)
}
Table.prototype.unregisterHandlers = function() {
@ -130,6 +136,11 @@
this.el.removeEventListener('keydown', this.keydownHandler);
this.keydownHandler = null
if (this.formSubmitHandler) {
this.$el.closest('form').unbind('oc.beforeRequest', this.formSubmitHandler)
this.formSubmitHandler = null
}
}
Table.prototype.initCellProcessors = function() {
@ -331,6 +342,10 @@
this.commitEditedRow()
this.activeCellProcessor = null
if (this.activeCell)
this.activeCell.setAttribute('class', '')
this.activeCell = null
}
@ -466,6 +481,17 @@
)
}
Table.prototype.notifyRowProcessorsOnChange = function(cellElement) {
var columnName = cellElement.getAttribute('data-column'),
row = cellElement.parentNode
for (var i = 0, len = row.children.length; i < len; i++) {
var column = this.options.columns[i].key
this.cellProcessors[column].onRowValueChanged(columnName, row.children[i])
}
}
// EVENT HANDLERS
// ============================
@ -522,14 +548,10 @@
return
}
Table.prototype.notifyRowProcessorsOnChange = function(cellElement) {
var columnName = cellElement.getAttribute('data-column'),
row = cellElement.parentNode
for (var i = 0, len = row.children.length; i < len; i++) {
var column = this.options.columns[i].key
this.cellProcessors[column].onRowValueChanged(columnName, row.children[i])
Table.prototype.onFormSubmit = function(ev, data) {
if (data.handler == this.options.postbackHandlerName) {
this.unfocusTable()
data.options.data[this.options.alias + 'TableData'] = this.dataSource.getAllData()
}
}
@ -683,7 +705,9 @@
clientDataSourceClass: 'client',
keyColumn: 'id',
recordsPerPage: false,
data: null
data: null,
postback: true,
postbackHandlerName: 'onSave'
}
// TABLE PLUGIN DEFINITION

View File

@ -75,9 +75,8 @@
this.fetchOptions(cellContentContainer.parentNode, function renderCellFetchOptions(options) {
if (options[value] !== undefined)
value = options[value]
viewContainer.textContent = options[value]
viewContainer.textContent = value
cellContentContainer.setAttribute('tabindex', 0)
})
}
@ -113,7 +112,6 @@
this.hideDropdown()
this.itemListElement = null
this.activeCell = null
}
@ -178,6 +176,13 @@
var activeItemElement = this.itemListElement.querySelector('ul li.selected')
if (!activeItemElement) {
activeItemElement = this.itemListElement.querySelector('ul li:first-child')
if (activeItemElement)
activeItemElement.setAttribute('class', 'selected')
}
if (activeItemElement) {
window.setTimeout(function(){
activeItemElement.focus()