Laminas form Collection missing data-template

Hi, I have made a form with nested fieldsets and instead of using formCollection to render the view, I have made a loop for rendering my elements. Everything seems to be ok. But I do not how to generate the data-template in my source code for adding new collection element. I will post below my code.

  1. Here is the main form
<?php

/**
    * @module     Commun
    * @subpackage Form\Admin
    * @author     Samuel NANGUI <nanguisamuel@gmail.com>
    * @copyright  Copyright (c) 2021 Nslabs
    */

namespace Commun\Form\Modules\Application;

use Commun\Form\Modules\Application\Fieldset\ImpactFieldset;


use Commun\Form\CommunForm;

class CadreLogiqueForm extends CommunForm 
{
            
    public function init() {        
        $this->setName('CadreLogiqueForm');
        $this->addFieldset(ImpactFieldset::class,['use_as_base_fieldset' => true]);
        $this->addSubmitButton('next', 'Poursuivre', 'next', 'btn btn-vert w-100');        
        $this->addSubmitButton('previous', 'Précédent', 'previous', 'btn btn-rouge w-100');
        $this->addSubmitButton('home', 'Accueil', 'home', 'btn btn-noir w-100');
    }  
  
}

Impact fieldset :

<?php

/**
    * @module     Commun
    * @subpackage Form\Admin
    * @author     Samuel NANGUI <nanguisamuel@gmail.com>
    * @copyright  Copyright (c) 2020 Nslabs
    */

namespace Commun\Form\Modules\Application\Fieldset;

use Commun\Model\Entity\Projet;
use Laminas\InputFilter\InputFilterProviderInterface;
use Laminas\Hydrator\ReflectionHydrator;

use Commun\Form\CommunFormFieldset;


class ImpactFieldset extends CommunFormFieldset implements InputFilterProviderInterface
{
        
    
    private $mapper;
       
    public function __construct($mappers=[])
    {        
        $this->mapper = $mappers;
        parent::__construct($this->mapper,'ImpactForm');           
        $this->setHydrator(new ReflectionHydrator());
        $this->setObject(new Projet());
        $this->setLabel('impactFieldset');          
        
    }
    
    
    public function init() {
        parent::init();                      
        $this->addTextarea('impactProjet', 'Impact', 'impactProjet', ['class' => 'form-control','rows' => 3]);   
        $this->addCollection('effets','Nouvel effet', EffetFieldset::class,1);
    }
    
        
    /**
     * @return array
     */
    public function getInputFilterSpecification()
    {
        return [
            
        ];
    }
}

EffetFieldset :

<?php

/**
    * @module     Commun
    * @subpackage Form\Admin
    * @author     Samuel NANGUI <nanguisamuel@gmail.com>
    * @copyright  Copyright (c) 2020 Nslabs
    */

namespace Commun\Form\Modules\Application\Fieldset;

use Commun\Model\Entity\EffetProjet;
use Laminas\InputFilter\InputFilterProviderInterface;
use Laminas\Hydrator\ReflectionHydrator;

use Commun\Form\CommunFormFieldset;


class EffetFieldset extends CommunFormFieldset implements InputFilterProviderInterface
{
        
    
    private $mapper;
       
    public function __construct($mappers=[])
    {        
        $this->mapper = $mappers;
        parent::__construct($this->mapper,'EffetForm');           
        $this->setHydrator(new ReflectionHydrator());
        $this->setObject(new EffetProjet());
        $this->setLabel('EffetFieldset');          
        
    }
    
    
    public function init() {
        parent::init();                      
        $this->addText('libelleEffet','Intitulé de l\'effet','libelleEffet',['required' => 'required','class' => 'form-control champ-requis'],'champ-requis');
        $options = $this->mapper['indicateur']->getOptions('idRefIndicateur','libelle','Aucune valeur choisie',null,['libelle']);           
        //$options = [];          
        $this->addSelect('idRefIndicateur','Indicateur de performance',$options,['class' => 'form-control'],'champ-requis');
        $this->addText('valeurReference','Valeur de référence','valeurReference',['class' => 'form-control champ_decimal'],'champ-requis');
        $this->addText('valeurCible','Valeur cible','valeurCible',['class' => 'form-control champ_decimal'],'champ-requis');
        $this->addTextarea('moyenVerification', 'Moyen de vérification', 'moyenVerification', ['class' => 'form-control','rows' => 3]); 
        $this->addTextarea('risqueMesureAttenuation', 'Risques et mesures d\'atténuation', 'risqueMesureAttenuation', ['class' => 'form-control','rows' => 3]);   
        $this->addCollection('composantes','Nouvelle composante', ComposanteFieldset::class,1);
    }
    
        
    /**
     * @return array
     */
    public function getInputFilterSpecification()
    {
        return [
            
        ];
    }
}

ComposanteFieldset :


<?php

/**
    * @module     Commun
    * @subpackage Form\Admin
    * @author     Samuel NANGUI <nanguisamuel@gmail.com>
    * @copyright  Copyright (c) 2020 Nslabs
    */

namespace Commun\Form\Modules\Application\Fieldset;

use Commun\Model\Entity\ComposanteEffet;
use Laminas\InputFilter\InputFilterProviderInterface;
use Laminas\Hydrator\ReflectionHydrator;

use Commun\Form\CommunFormFieldset;


class ComposanteFieldset extends CommunFormFieldset implements InputFilterProviderInterface
{
        
    
    private $mapper;
       
    public function __construct($mappers=[])
    {        
        $this->mapper = $mappers;
        parent::__construct($this->mapper,'ComposanteEffetForm');           
        $this->setHydrator(new ReflectionHydrator());
        $this->setObject(new ComposanteEffet());
        $this->setLabel('composanteEffetFieldset');          
        
    }
    
    
    public function init() {
        parent::init();                      
        $this->addText('libelle','Intitulé de la composante','libelle',['class' => 'form-control champ-requis'],'champ-requis');
        $options = $this->mapper['indicateur']->getOptions('idRefIndicateur','libelle','Aucune valeur choisie',null,['libelle']);           
        $this->addSelect('idRefIndicateur','Indicateur de performance',$options,['class' => 'form-control'],'champ-requis');
        $this->addText('valeurReference','Valeur de référence','valeurReference',['class' => 'form-control champ_decimal'],'champ-requis');
        $this->addText('valeurCible','Valeur cible','valeurCible',['class' => 'form-control champ_decimal'],'champ-requis');
        $this->addTextarea('moyenVerification', 'Moyen de vérification', 'moyenVerification', ['class' => 'form-control','rows' => 3]); 
        $this->addTextarea('risqueMesureAttenuation', 'Risques et mesures d\'atténuation', 'risqueMesureAttenuation', ['class' => 'form-control','rows' => 3]);
        $this->addCollection('produits','Nouveau produit', ProduitFieldset::class,1);
    }
    
        
    /**
     * @return array
     */
    public function getInputFilterSpecification()
    {
        return [
            
        ];
    }
}

ProduitFieldset :

<?php

/**
    * @module     Commun
    * @subpackage Form\Admin
    * @author     Samuel NANGUI <nanguisamuel@gmail.com>
    * @copyright  Copyright (c) 2020 Nslabs
    */

namespace Commun\Form\Modules\Application\Fieldset;

use Commun\Model\Entity\ProduitEffet;
use Laminas\InputFilter\InputFilterProviderInterface;
use Laminas\Hydrator\ReflectionHydrator;

use Commun\Form\CommunFormFieldset;


class ProduitFieldset extends CommunFormFieldset implements InputFilterProviderInterface
{
        
    
    private $mapper;
       
    public function __construct($mappers=[])
    {        
        $this->mapper = $mappers;
        parent::__construct($this->mapper,'produitForm');           
        $this->setHydrator(new ReflectionHydrator());
        $this->setObject(new ProduitEffet());
        $this->setLabel('produitFieldset');          
        
    }
    
    
    public function init() {
        parent::init();                      
        $this->addText('libelle','Intitulé du produit','libelle',['class' => 'form-control champ-requis'],'champ-requis');
        $options = $this->mapper['indicateur']->getOptions('idRefIndicateur','libelle','Aucune valeur choisie',null,['libelle']);           
        $this->addSelect('idRefIndicateur','Indicateur de performance',$options,['class' => 'form-control'],'champ-requis');
        $this->addText('valeurReference','Valeur de référence','valeurReference',['class' => 'form-control champ_decimal'],'champ-requis');
        $this->addText('valeurCible','Valeur cible','valeurCible',['class' => 'form-control champ_decimal'],'champ-requis');
        $this->addTextarea('moyenVerification', 'Moyen de vérification', 'moyenVerification', ['class' => 'form-control','rows' => 3]); 
        $this->addTextarea('risqueMesureAttenuation', 'Moyen de vérification', 'risqueMesureAttenuation', ['class' => 'form-control','rows' => 3]);  
        $this->addCollection('activites','Nouvelle activité', ActiviteFieldset::class,1);
    }
    
        
    /**
     * @return array
     */
    public function getInputFilterSpecification()
    {
        return [
            
        ];
    }
}

ActiviteFieldset :

<?php

/**
    * @module     Commun
    * @subpackage Form\Admin
    * @author     Samuel NANGUI <nanguisamuel@gmail.com>
    * @copyright  Copyright (c) 2020 Nslabs
    */

namespace Commun\Form\Modules\Application\Fieldset;

use Commun\Model\Entity\ActiviteProduit;
use Laminas\InputFilter\InputFilterProviderInterface;
use Laminas\Hydrator\ReflectionHydrator;

use Commun\Form\CommunFormFieldset;


class ActiviteFieldset extends CommunFormFieldset implements InputFilterProviderInterface
{
        
    
    private $mapper;
       
    public function __construct($mappers=[])
    {        
        $this->mapper = $mappers;
        parent::__construct($this->mapper,'ActiviteForm');           
        $this->setHydrator(new ReflectionHydrator());
        $this->setObject(new ActiviteProduit());
        $this->setLabel('activiteFieldset');          
        
    }
    
    
    public function init() {
        parent::init();                      
        $this->addText('libelle','Intitulé de l\'activité','libelle',['class' => 'form-control champ-requis'],'champ-requis');
        $options = $this->mapper['indicateur']->getOptions('idRefIndicateur','libelle','Aucune valeur choisie',null,['libelle']);           
        $this->addSelect('idRefIndicateur','Indicateur de performance',$options,['class' => 'form-control'],'champ-requis');
        $this->addText('valeurReference','Valeur de référence','valeurReference',['class' => 'form-control champ_decimal'],'champ-requis');
        $this->addText('valeurCible','Valeur cible','valeurCible',['class' => 'form-control champ_decimal'],'champ-requis');
        $this->addTextarea('moyenVerification', 'Moyen de vérification', 'moyenVerification', ['class' => 'form-control','rows' => 3]); 
        $this->addTextarea('risqueMesureAttenuation', 'Moyen de vérification', 'risqueMesureAttenuation', ['class' => 'form-control','rows' => 3]);         
    }
    
        
    /**
     * @return array
     */
    public function getInputFilterSpecification()
    {
        return [
            
        ];
    }
}

My main view here :

<?php
/**
 *
 * @module    Application
 * @subpackage View
 * @author     Samuel NANGUI <nanguisamuel@gmail.com>
 * @copyright  Copyright (c) 2021 Nslabs
 */
$this->headTitle($this->translate('Cadre logique'));
$form->setAttribute('action', $this->url('front-user-gestion-des-projets', [
    'action' => 'cadre-logique',
    'id'     => $id,
]));
$form->prepare();
$impactForm = $form->get('ImpactForm');
?>

<?=$this->partial('partial/commun/debContent',['user'=>$this->user])?>       
<div class="container-fluid inner-starter">                                
    <?=$this->partial('partial/commun/starting-view',[
        'flash_redirect' => true,
        'titre' => 'Cadre logique du projet',
        'description' => 'Saisissez ici les informations du cadre logique puis cliquez sur le bouton POURSUIVRE situé au bas de la page ',
    ])?> 
    
    <?=$this->form()->openTag($form)?> 
    <div class="row clearfix">
        <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 mb-30 mt-20 bloc2">            
            <div class="p-30 form-saisie box-shadow bloc-vert-border form-input background-form-input">                                                                                                   
                <?=$this->partial('partial/cadre-logique/view-rendering-cadre-logique-elements',['form' => $impactForm,'colLabel'=>4,'colInput'=>8,'setLabel'=>true])?>                                                           
            </div>
        </div>          
    </div>  
    <div class="mt-20 mb-20">
        <?=$this->partial('partial/commun/view-rendering-buttons',['form' => $form,'buttons' => ['previous','','','next'],'col' => 3])?> 
    </div>  
    <?=$this->form()->closeTag();?>
</div>

The partial for rendering elements (partial/cadre-logique/view-rendering-cadre-logique-elements)

<?php 
//var_dump($form->get('effets'));
//die();
foreach ($form as $element){  
    $classCheckBox='';
    if(!($element instanceof Laminas\Form\Element\Collection) && ($element->getAttributes()['type']=='multi_checkbox')){
        $classCheckBox = 'icheck-default';
    }    
    
?>
<!--We render elements wich are not collections-->
<?php if(!($element instanceof Laminas\Form\Element\Collection)): ?> 
    <div class="row clearfix">
        <?php if($setLabel): ?>
        <div class="mb-0 col-lg-<?=$colLabel?> col-md-<?=$colLabel?> col-sm-4 form-control-label">                                    
            <?= $this->formLabel($element) ?>
        </div>
        <?php endif; ?>
        <div class="mb-0 col-lg-<?=$colInput?> col-md-<?=$colInput?> col-sm-8">
            <div class="form-group <?=$classCheckBox?>">
                <?= $this->formElement($element) ?>            
            </div>
        </div>
        <div class="col-lg-12">
            <div class="form-group error-message mt-0">
                <?= $this->formElementErrors()->render($element) ?>
            </div>
        </div>
    </div>
<?php else : ?>
<?php 
  $effetFieldsets = $element->getFieldsets();  
  
?>
    <div class="div-scrollable-effet">
        <?php 
            $i=1; 
            
            foreach ($effetFieldsets as $effetFieldset){               
                echo('<div class="title-element-cadre-logique">Effet '.$i.' </div>');
                echo('<div class="div-effet">');                
                    foreach ($effetFieldset->getElements() as $elementsEffetFieldset){ 
                        ?>
                        <?=$this->partial('partial/cadre-logique/view-rendering-element',['v_elements' => $elementsEffetFieldset,
                                                                                          'v_setLabel' => $setLabel,
                                                                                          'v_colInput' => $colInput,
                                                                                          'v_classCheckBox' => $classCheckBox,
                                                                                          'v_colLabel' => $colLabel,
                                                                                           ])?> 
                        <?php                                                                  
                    }
                    $composanteFieldsets = $effetFieldset->get('composantes');
                    $j=1;                    
                    foreach ($composanteFieldsets as $composanteFieldset){
                        echo('<div class="title-element-cadre-logique">Composante '.$j.' </div>');
                        echo('<div class="div-composante">');
                        foreach ($composanteFieldset->getElements() as $elementsComposanteFieldset){
                            ?>
                        <?=$this->partial('partial/cadre-logique/view-rendering-element',['v_elements' => $elementsComposanteFieldset,
                                                                                          'v_setLabel' => $setLabel,
                                                                                          'v_colInput' => $colInput,
                                                                                          'v_classCheckBox' => $classCheckBox,
                                                                                          'v_colLabel' => $colLabel,
                                                                                           ])?> 
                        <?php                                                        
                        }
                       
                        $produitFieldsets = $composanteFieldset->get('produits');
                        $k=1;                    
                        foreach ($produitFieldsets as $produitFieldset){
                            echo('<div class="title-element-cadre-logique">Produit '.$k.' </div>');
                            echo('<div class="div-produit">');
                            foreach ($produitFieldset->getElements() as $elementsProduitFieldset){
                                ?>
                                <?=$this->partial('partial/cadre-logique/view-rendering-element',['v_elements' => $elementsProduitFieldset,
                                                                                                  'v_setLabel' => $setLabel,
                                                                                                  'v_colInput' => $colInput,
                                                                                                  'v_classCheckBox' => $classCheckBox,
                                                                                                  'v_colLabel' => $colLabel,
                                                                                                   ])?> 
                                <?php                                
                            }
                           
                            $activiteFieldsets = $produitFieldset->get('activites');
                            var_dump($activiteFieldsets);
                            $l=1;                    
                            foreach ($activiteFieldsets as $activiteFieldset){                                  
                                echo('<div class="title-element-cadre-logique">Activite '.$l.' </div>');
                                echo('<div class="div-activite">');
                                foreach ($activiteFieldset->getElements() as $elementsActiviteFieldset){                                    
                                    ?>
                                <?=$this->partial('partial/cadre-logique/view-rendering-element',['v_elements' => $elementsActiviteFieldset,
                                                                                                  'v_setLabel' => $setLabel,
                                                                                                  'v_colInput' => $colInput,
                                                                                                  'v_classCheckBox' => $classCheckBox,
                                                                                                  'v_colLabel' => $colLabel,
                                                                                                   ])?> 
                                <?php                                    
                                }                                
                                echo('</div>');                                
                                $l++;
                            }
                            echo('<div class="text-right"><button class="btn btn-vert btn-add-activite">Ajouter une activité</div>');
                            echo('</div>');
                            $k++;
                        }
                        echo('<div class="mt-20 text-right"><button class="btn btn-vert btn-add-produit">Ajouter un produit</div>');
                        echo('</div>');
                        $j++;
                    }
                    echo('<div class="mt-20 text-right"><button class="btn btn-vert btn-add-composante">Ajouter une composante</div>');
                echo('</div>'); 
                $i++;
            }
            echo('<div class="mt-20 text-right"><button class="btn btn-vert btn-add-effet">Ajouter un effet</div>');
        ?>
    </div>
<?php endif; ?>
<?php    
  }
?>

And Finally (partial/cadre-logique/view-rendering-element)


<?php 
    if(!($v_elements instanceof Laminas\Form\Element\Collection)){
        ?>                        
        <div class="row clearfix">
            <?php if($v_setLabel): ?>
            <div class="mb-0 col-lg-<?=$v_colLabel?> col-md-<?=$v_colLabel?> col-sm-4 form-control-label">                                    
                <?= $this->formLabel($v_elements) ?>
            </div>
            <?php endif; ?>
            <div class="mb-0 col-lg-<?=$v_colInput?> col-md-<?=$v_colInput?> col-sm-8">
                <div class="form-group <?=$v_classCheckBox?>">
                    <?= $this->formElement($v_elements) ?>            
                </div>
            </div>
            <div class="col-lg-12">
                <div class="form-group error-message mt-0">
                    <?= $this->formElementErrors()->render($v_elements) ?>
                </div>
            </div>
        </div>
        <?php
    }
?>

How can I render the template inside my view rendering ?
Thans for any help

$this->formCollection()->renderTemplate($collectionElement);

Your form and fieldset contain a typical mistake: injection of a mapper, repository or database connection for setting the options for an element. In your example here it is always the same element.

Create a custom element as a class and set the options there, then use the element in your forms and fieldset. Then you only have to pass the mapper once.
This is much simpler to test and you avoid repeats.

Thanks @froschdesign. Thank you so much.

How can I apply Filter on EffetFieldset ?
When I submit the form, EffetFieldset data is empty. I need to precise that my filter is in my entity like this.

<?php
                
/**
    * @module     Commun
    * @subpackage Model\Entity
    * @author     Allround automation by Nslabs Entity Creator
    * @created on 06-07-2021 19:07:31
    * @copyright  Copyright (c) 2021 Nslabs by Samuel NANGUI <nanguisamuel@gmail.com>
    */                
                
namespace Commun\Model\Entity;  
    
use DomainException;
use Laminas\Filter\StringTrim;
use Laminas\Filter\StripTags;
use Laminas\InputFilter\InputFilter;
use Laminas\InputFilter\InputFilterAwareInterface;
use Laminas\InputFilter\InputFilterInterface; 
use Commun\Model\EntityFilterValidator;

class EffetProjet extends EntityFilterValidator implements InputFilterAwareInterface
{            
    
    private $idEffetProjet;
    private $libelleEffet;
    private $idRefIndicateur;
    private $valeurReference;
    private $valeurCible;
    private $moyenVerification;
    private $idProjet;
    private $risqueMesureAttenuation;
    
    /**
     * @var array
     */
    private $composantes;

    
    // Add this property:
    private $inputFilter;    
    
    public function getIdEffetProjet()
    {
        return $this->idEffetProjet;
    }

    public function getLibelleEffet()
    {
        return $this->libelleEffet;
    }

    public function getIdRefIndicateur()
    {
        return $this->idRefIndicateur;
    }

    public function getValeurReference()
    {
        return $this->valeurReference;
    }

    public function getValeurCible()
    {
        return $this->valeurCible;
    }

    public function getMoyenVerification()
    {
        return $this->moyenVerification;
    }

    public function getIdProjet()
    {
        return $this->idProjet;
    }

    public function getRisqueMesureAttenuation()
    {
        return $this->risqueMesureAttenuation;
    }
    
    public function getComposantes()
    {
        return $this->composantes;
    }
        
   
    public function __construct($idEffetProjet=NULL, $libelleEffet=NULL, $idRefIndicateur=NULL, $valeurReference=NULL, $valeurCible=NULL, $moyenVerification=NULL, $idProjet=NULL, $risqueMesureAttenuation=NULL, $composantes=NULL)
    {        
        $this->idEffetProjet = $idEffetProjet;
        $this->libelleEffet = $libelleEffet;
        $this->idRefIndicateur = $idRefIndicateur;
        $this->valeurReference = $valeurReference;
        $this->valeurCible = $valeurCible;
        $this->moyenVerification = $moyenVerification;
        $this->idProjet = $idProjet;
        $this->risqueMesureAttenuation = $risqueMesureAttenuation;
        $this->composantes = $composantes;
                
    }  
        
    public function getArrayCopy()
    {
        return [
            'idEffetProjet' => $this->getIdEffetProjet(),
            'libelleEffet' => $this->getLibelleEffet(),
            'idRefIndicateur' => $this->getIdRefIndicateur(),
            'valeurReference' => $this->getValeurReference(),
            'valeurCible' => $this->getValeurCible(),
            'moyenVerification' => $this->getMoyenVerification(),
            'idProjet' => $this->getIdProjet(),
            'risqueMesureAttenuation' => $this->getRisqueMesureAttenuation(),
            'composantes' => $this->getComposantes(),

        ];
    }
            
    public function setInputFilter(InputFilterInterface $inputFilter){
        throw new DomainException('Cette classe ne supporte pas l\'ajout de filtre');
    }
            
    public function getInputFilter(){
        if ($this->inputFilter) {
            return $this->inputFilter;
        }
            
        $inputFilter = new InputFilter();
            
        $champObligatoire = 'Ce champ est obligatoire';
        $valeurErronee = 'La valeur saisie est erronée';
        $tooShort='Vous n\'avez respecté la taille minimale';
        $tooLong='Vous avez excédé la taille maximale autorisée (20 caracteres maximum)';
        
            
        //Add Validator here  
        $inputFilter->add($this->setDefaultFilterWithoutValidator('libelleEffet',$champObligatoire,false));
        $inputFilter->add($this->setNumericValidatorWithLength('valeurReference', 1, 20, $tooShort, $tooLong, $valeurErronee,false));
        $inputFilter->add($this->setNumericValidatorWithLength('valeurCible', 1, 20, $tooShort, $tooLong, $valeurErronee,false));       
        $inputFilter->add($this->setDefaultFilterWithoutValidator('moyenVerification',$champObligatoire,false));
        $inputFilter->add($this->setDefaultFilterWithoutValidator('risqueMesureAttenuation',$champObligatoire,false));
        $inputFilter->add($this->setDefaultFilterWithoutValidator('idRefIndicateur',$champObligatoire,false));
        
        
                   
        //Add Validator here
            
        $this->inputFilter = $inputFilter;
        return $this->inputFilter;
            
    }

}