Merge pull request #2723 from octobercms/feature-RepeaterMaxItems

Add support for maxItems to the Repeater FormWidget. Thanks to @panakour for the initial work in #2710. Fixes #1710, #2649
This commit is contained in:
Luke Towers 2017-02-28 20:46:04 -06:00 committed by GitHub
commit fedf7b2b7d
2 changed files with 36 additions and 12 deletions

View File

@ -48,11 +48,16 @@ class Repeater extends FormWidgetBase
*/ */
protected $formWidgets = []; protected $formWidgets = [];
/** /**
* @var bool Stops nested repeaters populating from previous sibling. * @var bool Stops nested repeaters populating from previous sibling.
*/ */
protected static $onAddItemCalled = false; protected static $onAddItemCalled = false;
/**
* @var int Maximum repeated items (0 == unlimited items)
*/
protected $maxItems = 0;
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@ -62,6 +67,7 @@ class Repeater extends FormWidgetBase
'form', 'form',
'prompt', 'prompt',
'sortable', 'sortable',
'maxItems',
]); ]);
if (!self::$onAddItemCalled) { if (!self::$onAddItemCalled) {
@ -86,6 +92,7 @@ class Repeater extends FormWidgetBase
$this->vars['indexName'] = self::INDEX_PREFIX.$this->formField->getName(false).'[]'; $this->vars['indexName'] = self::INDEX_PREFIX.$this->formField->getName(false).'[]';
$this->vars['prompt'] = $this->prompt; $this->vars['prompt'] = $this->prompt;
$this->vars['formWidgets'] = $this->formWidgets; $this->vars['formWidgets'] = $this->formWidgets;
$this->vars['maxItems'] = $this->maxItems;
} }
/** /**

View File

@ -33,25 +33,28 @@
Repeater.DEFAULTS = { Repeater.DEFAULTS = {
sortableHandle: '.repeater-item-handle', sortableHandle: '.repeater-item-handle',
sortableContainer: 'ul.field-repeater-items', sortableContainer: 'ul.field-repeater-items',
titleFrom: null titleFrom: null,
maxItems: null
} }
Repeater.prototype.init = function() { Repeater.prototype.init = function() {
this.bindSorting() this.bindSorting()
this.$el.on('ajaxDone', '[data-repeater-remove]', this.proxy(this.onRemoveItemSuccess)) this.$el.on('ajaxDone', '> .field-repeater-items > .field-repeater-item > .repeater-item-remove > [data-repeater-remove]', this.proxy(this.onRemoveItemSuccess))
this.$el.on('ajaxDone', '[data-repeater-add]', this.proxy(this.onAddItemSuccess)) 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.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.$el.one('dispose-control', this.proxy(this.dispose))
this.togglePrompt()
} }
Repeater.prototype.dispose = function() { Repeater.prototype.dispose = function() {
this.$sortable.sortable('destroy') this.$sortable.sortable('destroy')
this.$el.off('ajaxDone', '[data-repeater-remove]', this.proxy(this.onRemoveItemSuccess)) this.$el.off('ajaxDone', '> .field-repeater-items > .field-repeater-item > .repeater-item-remove > [data-repeater-remove]', this.proxy(this.onRemoveItemSuccess))
this.$el.off('ajaxDone', '[data-repeater-add]', this.proxy(this.onAddItemSuccess)) this.$el.off('ajaxDone', '> .field-repeater-add-item > [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('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.off('dispose-control', this.proxy(this.dispose))
this.$el.removeData('oc.repeater') this.$el.removeData('oc.repeater')
@ -79,10 +82,24 @@
Repeater.prototype.onRemoveItemSuccess = function(ev) { Repeater.prototype.onRemoveItemSuccess = function(ev) {
$(ev.target).closest('.field-repeater-item').remove() $(ev.target).closest('.field-repeater-item').remove()
this.togglePrompt()
} }
// This fires twice, not sure why
Repeater.prototype.onAddItemSuccess = function(ev) { 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) { Repeater.prototype.toggleCollapse = function(ev) {