From 5c56c9579d4049cb47927bf80e4d0cf3ac961825 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Tue, 28 Feb 2017 20:36:32 -0600 Subject: [PATCH 1/2] Add support for maxItems to the Repeater FormWidget Refs: #2710 --- modules/backend/formwidgets/Repeater.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/modules/backend/formwidgets/Repeater.php b/modules/backend/formwidgets/Repeater.php index 7b7c28b75..9a677a959 100644 --- a/modules/backend/formwidgets/Repeater.php +++ b/modules/backend/formwidgets/Repeater.php @@ -48,10 +48,15 @@ class Repeater extends FormWidgetBase */ protected $formWidgets = []; - /** - * @var bool Stops nested repeaters populating from previous sibling. - */ + /** + * @var bool Stops nested repeaters populating from previous sibling. + */ protected static $onAddItemCalled = false; + + /** + * @var int Maximum repeated items (0 == unlimited items) + */ + protected $maxItems = 0; /** * {@inheritDoc} @@ -62,6 +67,7 @@ class Repeater extends FormWidgetBase 'form', 'prompt', 'sortable', + 'maxItems', ]); if (!self::$onAddItemCalled) { @@ -86,6 +92,7 @@ class Repeater extends FormWidgetBase $this->vars['indexName'] = self::INDEX_PREFIX.$this->formField->getName(false).'[]'; $this->vars['prompt'] = $this->prompt; $this->vars['formWidgets'] = $this->formWidgets; + $this->vars['maxItems'] = $this->maxItems; } /** From 3aa251db4e400a719af477aa7e397213503399fe Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Tue, 28 Feb 2017 20:43:08 -0600 Subject: [PATCH 2/2] Client side enforcement of repeater maxitems Adds client side enforcement of the max items property of the repeater form widget --- .../repeater/assets/js/repeater.js | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/modules/backend/formwidgets/repeater/assets/js/repeater.js b/modules/backend/formwidgets/repeater/assets/js/repeater.js index 68fa766fe..e21451fdd 100644 --- a/modules/backend/formwidgets/repeater/assets/js/repeater.js +++ b/modules/backend/formwidgets/repeater/assets/js/repeater.js @@ -33,25 +33,28 @@ Repeater.DEFAULTS = { sortableHandle: '.repeater-item-handle', sortableContainer: 'ul.field-repeater-items', - titleFrom: null + titleFrom: null, + maxItems: null } Repeater.prototype.init = function() { this.bindSorting() - this.$el.on('ajaxDone', '[data-repeater-remove]', this.proxy(this.onRemoveItemSuccess)) - this.$el.on('ajaxDone', '[data-repeater-add]', this.proxy(this.onAddItemSuccess)) + this.$el.on('ajaxDone', '> .field-repeater-items > .field-repeater-item > .repeater-item-remove > [data-repeater-remove]', this.proxy(this.onRemoveItemSuccess)) + this.$el.on('ajaxDone', '> .field-repeater-add-item > [data-repeater-add]', this.proxy(this.onAddItemSuccess)) this.$el.on('click', '> ul > li > .repeater-item-collapse .repeater-item-collapse-one', this.proxy(this.toggleCollapse)) this.$el.one('dispose-control', this.proxy(this.dispose)) + + this.togglePrompt() } Repeater.prototype.dispose = function() { this.$sortable.sortable('destroy') - - this.$el.off('ajaxDone', '[data-repeater-remove]', this.proxy(this.onRemoveItemSuccess)) - this.$el.off('ajaxDone', '[data-repeater-add]', this.proxy(this.onAddItemSuccess)) - this.$el.off('click', '> ul > li > .repeater-item-collapse .repeater-item-collapse-one', this.proxy(this.toggleCollapse)) + + this.$el.off('ajaxDone', '> .field-repeater-items > .field-repeater-item > .repeater-item-remove > [data-repeater-remove]', this.proxy(this.onRemoveItemSuccess)) + this.$el.off('ajaxDone', '> .field-repeater-add-item > [data-repeater-add]', this.proxy(this.onAddItemSuccess)) + this.$el.off('click', '> .field-repeater-items > .field-repeater-item > .repeater-item-collapse .repeater-item-collapse-one', this.proxy(this.toggleCollapse)) this.$el.off('dispose-control', this.proxy(this.dispose)) this.$el.removeData('oc.repeater') @@ -76,13 +79,27 @@ this.$sortable.sortable(sortableOptions) } - + Repeater.prototype.onRemoveItemSuccess = function(ev) { $(ev.target).closest('.field-repeater-item').remove() + this.togglePrompt() } - // This fires twice, not sure why Repeater.prototype.onAddItemSuccess = function(ev) { + this.togglePrompt() + } + + Repeater.prototype.togglePrompt = function () { + if (this.options.maxItems != 0) { + var repeatedItems = this.$el.find('> .field-repeater-items > .field-repeater-item').length, + $addItemBtn = this.$el.find('> .field-repeater-add-item') + + if (repeatedItems >= this.options.maxItems) { + $addItemBtn.hide() + } else if (repeatedItems < this.options.maxItems) { + $addItemBtn.show() + } + } } Repeater.prototype.toggleCollapse = function(ev) {