diff --git a/modules/backend/classes/FormTabs.php b/modules/backend/classes/FormTabs.php index 709a0306e..74ccae2f2 100644 --- a/modules/backend/classes/FormTabs.php +++ b/modules/backend/classes/FormTabs.php @@ -27,6 +27,11 @@ class FormTabs implements IteratorAggregate, ArrayAccess */ public $fields = []; + /** + * @var array Names of tabs to lazy load. + */ + public $lazy = []; + /** * @var string Default tab label to use when none is specified. */ @@ -106,6 +111,10 @@ class FormTabs implements IteratorAggregate, ArrayAccess if (array_key_exists('paneCssClass', $config)) { $this->paneCssClass = $config['paneCssClass']; } + + if (array_key_exists('lazy', $config)) { + $this->lazy = $config['lazy']; + } } /** diff --git a/modules/backend/widgets/Form.php b/modules/backend/widgets/Form.php index ce42ad24b..efda2e017 100644 --- a/modules/backend/widgets/Form.php +++ b/modules/backend/widgets/Form.php @@ -456,6 +456,35 @@ class Form extends WidgetBase return $result; } + /** + * Renders all fields of a tab in the target tab-pane. + * + * @return array + */ + public function onLazyLoadTab() + { + $target = post('target'); + $tabName = post('name'); + + $fields = array_get(optional($this->getTab('primary'))->fields, $tabName); + + return [ + $target => $this->makePartial('form_fields', ['fields' => $fields]), + ]; + } + + /** + * Helper method to convert a field name to a valid ID attribute. + * + * @param $input + * + * @return string + */ + public function nameToId($input) + { + return HtmlHelper::nameToId($input); + } + /** * Creates a flat array of form fields from the configuration. * Also slots fields in to their respective tabs. @@ -935,7 +964,7 @@ class Form extends WidgetBase } $widgetConfig = $this->makeConfig($field->config); - $widgetConfig->alias = $this->alias . studly_case(HtmlHelper::nameToId($field->fieldName)); + $widgetConfig->alias = $this->alias . studly_case($this->nameToId($field->fieldName)); $widgetConfig->sessionKey = $this->getSessionKey(); $widgetConfig->previewMode = $this->previewMode; $widgetConfig->model = $this->model; diff --git a/modules/backend/widgets/form/assets/js/october.form.js b/modules/backend/widgets/form/assets/js/october.form.js index 2a95c5aae..7b82f5759 100644 --- a/modules/backend/widgets/form/assets/js/october.form.js +++ b/modules/backend/widgets/form/assets/js/october.form.js @@ -34,6 +34,7 @@ this.bindDependants() this.bindCheckboxlist() this.toggleEmptyTabs() + this.bindLazyTabs() this.bindCollapsibleSections() this.$el.on('oc.triggerOn.afterUpdate', this.proxy(this.toggleEmptyTabs)) @@ -161,6 +162,34 @@ }) } + /* + * Render tab form fields once a lazy tab is selected. + */ + FormWidget.prototype.bindLazyTabs = function() { + this.$el.on('click', '.tab-lazy [data-toggle="tab"]', function() { + var $el = $(this) + $.request('form::onLazyLoadTab', { + data: { + target: $el.data('target'), + name: $el.data('tab-name'), + }, + success: function(data) { + this.success(data) + $el.parent().removeClass('tab-lazy') + // Trigger all input presets to populate new fields. + setTimeout(function() { + $('[data-input-preset]').each(function() { + var preset = $(this).data('oc.inputPreset') + if (preset && preset.$src) { + preset.$src.trigger('input') + } + }) + }, 0) + } + }) + }) + } + /* * Hides tabs that have no content, it is possible this can be * called multiple times in a single cycle due to input.trigger. @@ -184,7 +213,7 @@ /* * Check each tab pane for form field groups */ - $('.tab-pane', tabControl).each(function() { + $('.tab-pane:not(.lazy)', tabControl).each(function() { $('[data-target="#' + $(this).attr('id') + '"]', tabControl) .closest('li') .toggle(!!$('> .form-group:not(:empty):not(.hide)', $(this)).length) diff --git a/modules/backend/widgets/form/partials/_form_tabs.htm b/modules/backend/widgets/form/partials/_form_tabs.htm index 1cbddcca1..4ff1e0204 100644 --- a/modules/backend/widgets/form/partials/_form_tabs.htm +++ b/modules/backend/widgets/form/partials/_form_tabs.htm @@ -14,7 +14,10 @@