Allow datatables to work within a repeater (#4102)

Fixes #4063. Credit to @bennothommo.
This commit is contained in:
Ben Thomson 2019-02-05 02:50:40 +08:00 committed by Luke Towers
parent 78e68ed632
commit 970d5622c0
4 changed files with 120 additions and 6 deletions

View File

@ -149,8 +149,13 @@ class DataTable extends FormWidgetBase
$config = $this->makeConfig((array) $this->config);
$config->dataSource = 'client';
$config->alias = studly_case(HtmlHelper::nameToId($this->fieldName)) . 'datatable';
$config->fieldName = $this->fieldName;
if (isset($this->getParentForm()->arrayName)) {
$config->alias = $this->getParentForm()->arrayName . '[' . studly_case(HtmlHelper::nameToId($this->fieldName)) . 'datatable' . ']';
$config->fieldName = $this->getParentForm()->arrayName . '[' . $this->fieldName . ']';
} else {
$config->alias = studly_case(HtmlHelper::nameToId($this->fieldName)) . 'datatable';
$config->fieldName = $this->fieldName;
}
$table = new Table($this->controller, $config);

View File

@ -3,6 +3,7 @@
use Lang;
use ApplicationException;
use Backend\Classes\FormWidgetBase;
use October\Rain\Html\Helper as HtmlHelper;
/**
* Repeater Form Widget
@ -198,6 +199,21 @@ class Repeater extends FormWidgetBase
throw new ApplicationException(Lang::get('backend::lang.repeater.max_items_failed', ['name' => $this->fieldName, 'max' => $this->maxItems, 'items' => count($value)]));
}
/*
* Give repeated form field widgets an opportunity to process the data.
*/
foreach ($this->formWidgets as $field => $form) {
foreach ($form->getFormWidgets() as $formField => $widget) {
$parts = HtmlHelper::nameToArray($field . '[' . $formField . ']');
$widgetValue = $widget->getSaveValue($this->dataArrayGet($value, $parts));
if (empty($widgetValue) || !count($widgetValue)) {
continue;
}
$this->dataArraySet($value, $parts, $widgetValue);
}
}
return array_values($value);
}
@ -403,4 +419,83 @@ class Repeater extends FormWidgetBase
{
return array_get($this->groupDefinitions, $groupCode.'.name');
}
/**
* Internal helper for method existence checks.
*
* @param object $object
* @param string $method
* @return boolean
*/
protected function objectMethodExists($object, $method)
{
if (method_exists($object, 'methodExists')) {
return $object->methodExists($method);
}
return method_exists($object, $method);
}
/**
* Variant to array_get() but preserves dots in key names.
*
* @param array $array
* @param array $parts
* @param null $default
* @return array|null
*/
protected function dataArrayGet(array $array, array $parts, $default = null)
{
if ($parts === null) {
return $array;
}
if (count($parts) === 1) {
$key = array_shift($parts);
if (isset($array[$key])) {
return $array[$key];
}
return $default;
}
foreach ($parts as $segment) {
if (!is_array($array) || !array_key_exists($segment, $array)) {
return $default;
}
$array = $array[$segment];
}
return $array;
}
/**
* Variant to array_set() but preserves dots in key names.
*
* @param array $array
* @param array $parts
* @param string $value
* @return array
*/
protected function dataArraySet(array &$array, array $parts, $value)
{
if ($parts === null) {
return $value;
}
while (count($parts) > 1) {
$key = array_shift($parts);
if (!isset($array[$key]) || !is_array($array[$key])) {
$array[$key] = [];
}
$array =& $array[$key];
}
$array[array_shift($parts)] = $value;
return $array;
}
}

View File

@ -116,6 +116,17 @@
}
Repeater.prototype.onRemoveItemSuccess = function(ev) {
// Allow any widgets inside a deleted item to be disposed
$(ev.target).closest('.field-repeater-item').find('[data-disposable]').each(function () {
var $elem = $(this),
control = $elem.data('control'),
widget = $elem.data('oc.' + control)
if (widget && typeof widget['dispose'] === 'function') {
widget.dispose()
}
})
$(ev.target).closest('.field-repeater-item').remove()
this.togglePrompt()
}

View File

@ -4,6 +4,7 @@ use Lang;
use Input;
use Request;
use Backend\Classes\WidgetBase;
use October\Rain\Html\Helper as HtmlHelper;
use SystemException;
/**
@ -79,12 +80,14 @@ class Table extends WidgetBase
if (Request::method() == 'POST' && $this->isClientDataSource()) {
if (strpos($this->fieldName, '[') === false) {
$requestDataField = $this->fieldName.'TableData';
}
else {
$requestDataField = $this->fieldName.'[TableData]';
$requestDataField = $this->fieldName . 'TableData';
} else {
$requestDataField = $this->fieldName . '[TableData]';
}
// Use dot notation for request data field
$requestDataField = implode('.', HtmlHelper::nameToArray($requestDataField));
if (Request::exists($requestDataField)) {
// Load data into the client memory data source on POST
$this->dataSource->purge();