Skip to content
Snippets Groups Projects
Commit c279cc2b authored by Bertrand Gauthier's avatar Bertrand Gauthier
Browse files

Nouvel élément de formulaire Collection et aide de vue associée...

Nouvel élément de formulaire Collection et aide de vue associée FormElementCollection (adaptés d'Octopus).
parent 4fa64d0f
Branches
Tags 5.1.7
No related merge requests found
Pipeline #17934 passed
CHANGELOG
=========
5.1.7
-----
- Nouvel élément de formulaire Collection et aide de vue associée FormElementCollection (adaptés d'Octopus).
5.1.6
-----
- Nouveau collecteur dans la barre LaminasDevTools : messages produits par \UnicaenApp\Service\MessageCollector.
......
......@@ -13,6 +13,9 @@ use UnicaenApp\DeveloperTools\MessageCollectorServiceFactory;
use UnicaenApp\Form\Element\SearchAndSelect2;
use UnicaenApp\Form\View\Helper\FormControlGroup;
use UnicaenApp\Form\View\Helper\FormControlGroupFactory;
use UnicaenApp\Form\View\Helper\FormControlText;
use UnicaenApp\Form\View\Helper\FormElementCollection;
use UnicaenApp\Form\View\Helper\FormElementRow;
use UnicaenApp\Form\View\Helper\FormSearchAndSelect2;
use UnicaenApp\HostLocalization\HostLocalization;
use UnicaenApp\HostLocalization\HostLocalizationFactory;
......@@ -432,6 +435,7 @@ return [
'formDate' => 'UnicaenApp\Form\View\Helper\FormDate',
'formDateTime' => Form\View\Helper\FormDateTime::class,
'formDateInfSup' => 'UnicaenApp\Form\View\Helper\FormDateInfSup',
'formElementCollection' => FormElementCollection::class,
'formRowDateInfSup' => 'UnicaenApp\Form\View\Helper\FormRowDateInfSup',
'formSearchAndSelect' => 'UnicaenApp\Form\View\Helper\FormSearchAndSelect',
'formSearchAndSelect2' => FormSearchAndSelect2::class,
......
<?php
namespace UnicaenApp\Form\Element;
use Laminas\Form\Element\Collection as ZendCollection;
use Laminas\Form\Element\Text;
use Laminas\Form\ElementInterface;
use Laminas\Form\FieldsetInterface;
/**
* Collection d'éléments :
* - possibilité d'ajouter un autocomplete sur un (et un seul) élément
* - possibilité de limiter le nombre d'éléments
*
* @author David Surville <david.surville at unicaen.fr>
*/
class Collection extends ZendCollection
{
const AUTOCOMPLETE_MIN_LENGTH = 2;
const AUTOCOMPLETE_DELAY = 750;
/**
* Liste des autocomplete
* Format : ['nom_element' => ['source' => , 'min-length' => , 'delay' => ]]
*
* @var array
*/
protected $autocomplete = [];
/**
* Nombre d'éléments minimum de la collection
* 0 = pas de limitation
*
* @var int
*/
protected $minElements = 0;
/**
* Nombre d'éléments maximum de la collection
* 0 = pas de limitation
*
* @var int
*/
protected $maxElements = 0;
/**
* @param null|int|string $name Optional name for the element
* @param array $options Optional options for the element
*/
public function __construct($name = null, $options = array())
{
parent::__construct($name, $options);
$this->setAttribute('id', uniqid('collection'));
}
/**
* @return array
*/
public function getAutocomplete()
{
return $this->autocomplete;
}
/**
* Ajoute un autocomplete sur un élément
*
* @param string $elementName
* @param string $source
* @param int $minLength
* @param int $delay
*/
public function addAutocomplete($elementName, $source, $minLength = null, $delay = null)
{
$targetElement = $this->getTargetElement();
if ($targetElement instanceof FieldsetInterface) {
$elements = array_keys($targetElement->getElements());
if (!in_array($elementName, $elements)) {
throw new \RuntimeException(sprintf("L'élément '%s' associé à l'autocomplete n'existe pas.", $elementName));
}
$element = $targetElement->get($elementName);
} elseif ($targetElement instanceof ElementInterface) {
if ($targetElement->getName() !== $elementName) {
throw new \RuntimeException(sprintf("L'élément '%s' associé à l'autocomplete n'existe pas.", $elementName));
}
$element = $targetElement;
}
if (!$element instanceof Text) {
throw new \RuntimeException("L'autocomplete doit être associé à un élément de type 'text'.");
}
$element->setAttribute('class', sprintf('%s-autocomplete', $this->getAttribute('id')));
$this->autocomplete[$elementName] = [
'source' => $source,
'min-length' => $minLength ?: self::AUTOCOMPLETE_MIN_LENGTH,
'delay' => $delay ?: self::AUTOCOMPLETE_DELAY
];
}
/**
*
* @return int
*/
function getMinElements()
{
return $this->minElements;
}
/**
*
* @param int $minElements
*
* @return self
*/
function setMinElements($minElements)
{
$this->minElements = $minElements;
return $this;
}
/**
*
* @return int
*/
function getMaxElements()
{
return $this->maxElements;
}
/**
*
* @param int $maxElements
*
* @return self
*/
function setMaxElements($maxElements)
{
$this->maxElements = $maxElements;
return $this;
}
}
<?php
namespace UnicaenApp\Form\View\Helper;
use UnicaenApp\Form\Element\Collection;
use Laminas\Form\View\Helper\FormCollection;
use Laminas\Form\ElementInterface;
use Laminas\Form\Element\Collection as CollectionElement;
use Laminas\Form\FieldsetInterface;
use Laminas\Form\LabelAwareInterface;
use Laminas\Form\Element\Button;
/**
* Aide de vue dessinant une collection d'éléments.
*
* Ajout par rapport à celle de base :
* - boutons d'ajout et de suppression d'élément à la collection
* - respect du nombre mini et maxi d'éléments dans la collection
* - styles inline :-/
*
* @see \UnicaenApp\Form\Element\Collection
* @author Unicaen
*/
class FormElementCollection extends FormCollection
{
/**
* This is the default wrapper that the collection is wrapped into
*
* @var string
*/
protected $wrapper = '<fieldset%4$s>%2$s%1$s%3$s%5$s</fieldset>%6$s%7$s';
/**
* The name of the default view helper that is used to render sub elements.
*
* @var string
*/
protected $defaultElementHelper = 'formControlGroup';
/**
* Nombre d'éléments à afficher
*
* @var int
*/
protected $countElements;
/**
* Render a collection by iterating through all fieldsets and elements
*
* @param ElementInterface $element
* @return string
*/
public function render(ElementInterface $element)
{
$renderer = $this->getView();
if (!method_exists($renderer, 'plugin')) {
// Bail early if renderer is not pluggable
return '';
}
$index=0;
$markup = '';
$linkMarkup = '';
$templateMarkup = '';
$elementHelper = $this->getElementHelper();
$fieldsetHelper = $this->getFieldsetHelper();
if ($element instanceof CollectionElement && $element->shouldCreateTemplate()) {
$this->countElements = $element->getCount();
$linkMarkup = $this->getAddLink();
$javascript = $this->getJavascript($element);
$css = $this->getCss($element);
$element->getTemplateElement()->setAttribute('data-index', '__index__');
$templateMarkup = $this->renderTemplate($element);
}
elseif ($element instanceof FieldsetInterface) {
$linkMarkup = $this->getDelLink();
}
foreach ($element->getIterator() as $elementOrFieldset) {
if ($elementOrFieldset instanceof FieldsetInterface) {
$elementOrFieldset->setAttribute('data-index', $index);
$markup .= $fieldsetHelper($elementOrFieldset, $this->shouldWrap());
$index++;
} elseif ($elementOrFieldset instanceof ElementInterface) {
$markup .= $elementHelper($elementOrFieldset);
}
}
// Every collection is wrapped by a fieldset if needed
if ($this->shouldWrap) {
$attributes = $element->getAttributes();
unset($attributes['name']);
$attributesString = $attributes ? ' ' . $this->createAttributesString($attributes) : '';
$label = $element->getLabel();
$legend = '';
if (!empty($label)) {
if (null !== ($translator = $this->getTranslator())) {
$label = $translator->translate(
$label,
$this->getTranslatorTextDomain()
);
}
if (!$element instanceof LabelAwareInterface || !$element->getLabelOption('disable_html_escape')) {
$escapeHtmlHelper = $this->getEscapeHtmlHelper();
$label = $escapeHtmlHelper($label);
}
$legend = sprintf(
$this->labelWrapper,
$label
);
}
$markup = sprintf(
$this->wrapper,
$markup,
$legend,
$templateMarkup,
$attributesString,
$linkMarkup,
$css,
$javascript
);
} else {
$markup .= $templateMarkup . $linkMarkup;
}
return $markup;
}
protected function getDelLink()
{
return '<div class="collection-delete-link">' .
'<a class="btn btn-link" title="Supprimer cet élément de la collection">' .
'<i class="fas fa-times" aria-hidden="true"></i>' .
'</a>' .
'</div>';
}
/**
* Get add link
*
* @return string
*/
protected function getAddLink()
{
return '<div class="collection-add-link">' .
'<a class="btn btn-link" title="Ajouter un élément à la collection">' .
'<i class="fas fa-plus" aria-hidden="true"></i> Ajouter' .
'</a>' .
'</div>';
}
/**
* @param Collection $container
* @return string
*/
protected function getCss($container)
{
$fieldsetId = $container->getAttribute('id');
$css = <<<EOT
<style>
#{$fieldsetId} {
margin-bottom: 15px;
}
#{$fieldsetId} div.input-group {
position: relative;
float: left;
margin: 0 10px 0 0;
}
#{$fieldsetId} div.collection-delete-link {
position: relative;
float: right;
}
#{$fieldsetId} div.collection-delete-link > .btn {
padding: 6px 0;
}
#{$fieldsetId} div.collection-add-link {
margin-bottom: 15px;
}
</style>
EOT;
return $css;
}
/**
* @param Collection $container
* @return string
*/
protected function getJavascript($container)
{
$maxEnabled = ($container->getMaxElements()) ? 'true' : 'false';
$minEnabled = ($container->getMinElements()) ? 'true' : 'false';
$javascript = <<<EOT
$('#{$container->getAttribute('id')}').on('click', '.collection-add-link > a', function() {
var container = $('#{$container->getAttribute('id')}');
var currentCount = $(container).children('fieldset').length;
var lastFieldset = $(container).children('fieldset').last();
var index = currentCount === 0 ? currentCount : parseInt(lastFieldset.attr('data-index')) + 1;
var template = $(container).children('span').data('template');
template = template.replace(/__index__/g, index);
if(currentCount === 0) {
$(container).prepend(template);
}
else {
lastFieldset.after(template);
}
if({$maxEnabled}) {
if(currentCount+1 >= {$container->getMaxElements()}) {
$('.collection-add-link').remove();
}
}
});
$('#{$container->getAttribute('id')}').on('click', '.collection-delete-link' , function() {
var container = $('#{$container->getAttribute('id')}');
var item = $(this).closest('fieldset');
var curPos = item.index();
var curInd = item.attr('data-index');
// suppression du fieldset lié à l'élément
item.remove();
var currentCount = $(container).children('fieldset').length;
if(currentCount > 0) {
var index = (curInd < {$this->countElements}) ? {$this->countElements} : curInd;
$(container).children('fieldset').each(function(i, el) {
if(i >= curPos) {
$(el)
.attr('data-index', '' + index)
.find('select,input').attr('name', function () {
return this.name.replace(/\[\d+\](\[.*\])$/, '['+index+']$1');
});
index++;
}
});
}
if({$maxEnabled}) {
if(currentCount+1 == {$container->getMaxElements()}) {
$(container).append('{$this->getAddLink()}');
}
}
});
$().ready(function updateButtons()
{
var container = $('#{$container->getAttribute('id')}');
var buttons = $(container).find('.collection-delete-link');
var currentCount = $(container).children('fieldset').length;
if({$maxEnabled}) {
if(currentCount >= {$container->getMaxElements()}) {
$('.collection-add-link').remove();
}
}
if($minEnabled) {
buttons.each(function(i, el) {
if(i<={$container->getMinElements()}-1) {
$(el).remove();
}
});
}
});
EOT;
foreach($container->getAutocomplete() as $name => $options) {
$javascript .= <<<EOT
$('#{$container->getAttribute('id')}').on('keyup', '.{$container->getAttribute('id')}-autocomplete', function() {
$(this).autocompleteOctopus({
source: '{$options['source']}',
minLength: {$options['min-length']},
delay: {$options['delay']}
});
});
EOT;
}
return '<script type="text/javascript">' . $javascript . '</script>';
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment