Search code examples
javascriptformssymfony-3.3

symfony3 wrong index on add collection form


I have added some custom fields in FOSUser. I add to my user a entity called Customer with collections of Phonenumbers, and Addresses.

I had the javascript code which allow adding Phonenumber and Address field. The new indexes added are wrong and follow the number of field of my collection.

I've got 2 fields for my phonenumber so the new index start to 2. And I've got 7 fields in my address collection and the new index start to 7.

At the end my new collections are not present in the request. Why my indexes are following the number of fields in my collection form?

Here my formTypes:

class ProfileType extends AbstractType
{

public function buildForm(FormBuilderInterface $builder, array $options)
{

    $builder->add('customer', CustomerType::class,array(
        'label'=>'sitebundle.customer',
        'by_reference'=>false,

    ));
    $builder->remove('plainPassword');

}

public function getParent()
{
    return 'FOS\UserBundle\Form\Type\RegistrationFormType';
}

public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => 'AppBundle\Entity\User'
    ));
}

}

CustomerType:

class CustomerType extends AbstractType
{

 public function buildForm(FormBuilderInterface $builder, array                         $options)
{

    $builder

        ->add('lastName',null,array(
            'required' => true,
            'label'=>'sitebundle.lastname'
        ))
        ->add('firstName',null,array(
            'label'=>'sitebundle.firstname',
            'required' => true
        ))

        ->add('companyName',null,array(
            'required' => false,
            'label'=>'sitebundle.companyname'

        ))

        ->add('phonenumbers', CollectionType::class, array(
            'label'=>'sitebundle.phonenumbers',
            'mapped'=>true,
            'allow_add'=>true,
            'required' => true,
            'allow_delete' => true,
            'delete_empty' => true,
            'by_reference' => false,
            'entry_type'   => PhoneNumberType::class
        ))


        ->add('addresses', CollectionType::class, array(
            'label'=>'sitebundle.addresses',
            'mapped'=>true,
            'allow_add'=>true,
            'required' => true,
            'allow_delete' => true,
            'delete_empty' => true,
            'by_reference' => false,
            'entry_type'   => AddressType::class
        ))
    ;
}

/**
 * @param OptionsResolver $resolver
 */
public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults(array(
       # 'validation_groups' => array('registration'),
        'data_class' => 'LilWorks\StoreBundle\Entity\Customer'
    ));
}


}

PhonenumberType And AddressType:

class PhoneNumberType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('phonenumber',null,array(
                'label'=>'sitebundle.phonenumber',
                'required' => true
            ))
            ->add('description',null,array(
                'label'=>'sitebundle.description',
            ))
        ;
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'LilWorks\StoreBundle\Entity\PhoneNumber'
        ));
    }
    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'lilworks_storebundle_phonenumber';
    }



}


class AddressType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name',null,array(
                'label'=>'sitebundle.address.name',
            ))
            ->add('street',null,array(
                'label'=>'sitebundle.address.street',
            ))
            ->add('complement',null,array(
                'label'=>'sitebundle.address.complement',
            ))
            ->add('zipCode',null,array(
                'label'=>'sitebundle.address.zipcode',
            ))
            ->add('city',null,array(
                'label'=>'sitebundle.address.city',
            ))
            ->add('country', EntityType::class, array(
                'label'=>'sitebundle.address.country',
                'class'    => 'LilWorksStoreBundle:Country' ,
                'choice_label' => function ($obj) { return   $obj->getName() ; },
                'required' => true ,
                'mapped'=> true,
                'expanded' => false ,
                'multiple' => false
            ))
            ;
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'LilWorks\StoreBundle\Entity\Address'
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'lilworks_storebundle_address';
    }


}

Solution

  • You are probably using this doc. I had the same problem as you. My solution was to calculate the index by other way.

    In my javascript function I have a "index" var which controls the amount of prototypes, then I pass that var to all prototypes to recalculate next time on add or del. have a look...

    $addTelefono.on('click', function (e) {
                    e.preventDefault();
                    var prototype = $('#render-telefonos').attr('data-prototype');
                    var index = parseInt($render_tel.attr('index'));
                    var newForm = prototype.replace(/__name__/g, index).replace(/label__/g, '');
    
                    $render_tel.attr('index', index + 1);
    
                    $toRenderTel.append('<div class="row"></div>');
                    $toRenderTel.append($('<h3 class="form-section">Nuevo teléfono <button class="btn red btn-xs delete-tel" type="button">Eliminar</button></h3>'))
                    $toRenderTel.append(newForm);
                    SGIPI.manageFormControl();
                });
    

    Before that you need to assign a index attr to the collection render like this:

    $render_dir.attr('index', $render_dir.find(':input').length);
    

    You could use your own logic like put a specific attr to find() later.

    Hope it helps...