8000 [Form] Support event listeners in buttons · Issue #8735 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Form] Support event listeners in buttons #8735

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
webmozart opened this issue Aug 13, 2013 · 9 comments
Closed

[Form] Support event listeners in buttons #8735

webmozart opened this issue Aug 13, 2013 · 9 comments

Comments

@webmozart
Copy link
Contributor

The event POST_SUBMIT (and potentially PRE_SUBMIT, PRE_SET_DATA and POST_SET_DATA) should be supported on buttons. Using this event, dynamic functionality can be built into forms that depends on whether a button was clicked or not:

$builder->get('refresh')->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
    $refresh = $event->getForm();
    $form = $refresh->getParent();

    if ($refresh->isClicked()) {
        // do something with $form
    }
});

May be related to #5807.

@wiejakp
Copy link
wiejakp commented Oct 31, 2018

Long forgotten post I see. I do agree this should be added. Otherwise there's no way to remove buttons dynamically, ex: removing certain buttons based on user role.

@fabpot
Copy link
Member
fabpot commented Aug 6, 2019

@wiejakp Would you try to work on implementing this feature?

@wiejakp
Copy link
wiejakp commented Aug 7, 2019

@fabpot, might attempt this if time allows.

@Romaxx
Copy link
Romaxx commented Aug 11, 2019

@wiejakp Current hack is to place SubmitType BEFORE an hidden element (or an element used in your form), addListener to the element and get parent getClickedButton() method.

$builder->add('mybutton',SubmitType::class);
$builder->add('ghostelement',HiddenType::class);


$builder->get('ghostelement')->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
    $form = $event->getForm();
    $parentform = $form->getParent();

    if ($parentform->has('mybutton') && $parentform->getClickedButton() === $parentform->get('mybutton')) {
        // mybutton clicked
    }
});

@wiejakp
Copy link
wiejakp commented Aug 14, 2019

@Romaxx , I'm not sure that's the case we're talking about. Well, OP was, but I'm having different issue with Event Listener.

Buttons not being included in Form Event listeners means that buttons themselves aren't able to be removed from form model. Please note that this example clearly demonstrates behaviour that button is excluded from:


declare(strict_types=1);

namespace App\Form\Extension;

use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Security;

/**
 * Automatically remove form elements based on users' role.
 */
class RoleFormTypeExtension extends AbstractTypeExtension
{
    /**
     * @var Security
     */
    private $security;

    /**
     * @param Security $security
     */
    public function __construct(Security $security)
    {
        $this->security = $security;
    }

    /**
     * @param FormBuilderInterface $builder
     * @param array                $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $detach = false;
        $role   = $options['role'];

        // Check $role.
        if ($role && false === $this->security->isGranted($role)) {
            $detach = true;
        }

        // Check $hide.
        if (true === $options['hide']) {
            $detach = true;
        }

        // detach form or element when at least one rule agree
        if ($detach) {
            $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
                $form = $event->getForm();

                if (!$form->isRoot()) {
                    $parent = $form->getParent();
                    $parent->remove($form->getName());
                }
            });
        }
    }

    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'role' => null,
            'hide' => false,
        ]);
        $resolver->setAllowedTypes('role', ['string', 'null']);
        $resolver->setAllowedTypes('hide', 'boolean');
    }

    /**
     * @return string[]
     */
    public static function getExtendedTypes(): array
    {
        return [
            FormType::class,
        ];
    }
}

@Romaxx
Copy link
Romaxx commented Aug 14, 2019

to @wiejakp, @webmozart dont talk about button removal, but listening button 'dynamic functionality can be built into forms that depends on whether a button was clicked or not', so my response is in that way.

@wiejakp
Copy link
wiejakp commented Aug 14, 2019

to @wiejakp, @webmozart dont talk about button removal, but listening button 'dynamic functionality can be built into forms that depends on whether a button was clicked or not', so my response is in that way.

Ok, gotcha. I edited my post because it clicked in after few minutes.

@wiejakp
Copy link
wiejakp commented Aug 22, 2019

@fabpot , I'm wondering if you might have a word of advice on problem from this puzzle. I'm having an issue with button type's parent removing button form type from it's children collection, and I hope you have an idea why, or when should I call my event. Seems like buttons createView() and setData() are out of the question (button dont't support data, and view changes seem to be too late(?)).

PR in question: https://github.com/wiejakp/form/pull/2

Extension for testing:

declare(strict_types=1); namespace App\Form\Extension; use Symfony\Component\Form\AbstractTypeExtension; use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Security\Core\Security; /** * Class RoleFormTypeExtension * * @package App\Form\Extension */ class RoleFormTypeExtension extends AbstractTypeExtension { /** * @var Security */ private $security; /** * @param Security $security */ public function __construct(Security $security) { $this->security = $security; } /** * @param FormBuilderInterface $builder * @param array $options */ public function buildForm(FormBuilderInterface $builder, array $options): void { if ($options['hide'] ?? null) { // Using FormEvents::PRE_SET_DATA to remove common fields from paren's form view. $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { $form = $event->getForm(); if (!$form->isRoot()) { $form->getParent()->remove($form->getName()); } }); /** * TODO: issues: * - pre/post build view doesn't seem to be working * - pre/post finish view doesn't seem to be working */ $builder->addEventListener(FormEvents::PRE_FINISH_VIEW, function (FormEvent $event) { $form = $event->getForm(); if (!$form->isRoot()) { $form->getParent()->remove($form->getName()); } }); } } /** * @param OptionsResolver $resolver */ public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(['hide' => false]); $resolver->setAllowedTypes('hide', 'boolean'); } /** * @return string[] */ public static function getExtendedTypes(): array { return [FormType::class]; } }

Form type for testing:


declare(strict_types=1);

namespace App\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ButtonType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * Class DefaultType
 *
 * @package App\Form\Type
 */
class DefaultType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array                $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            // works just fine
             ->add('text', TextType::class, [
                'label' => 'TextType',
                'hide'  => true,
            ])
            // parent can't remove
            ->add('button', ButtonType::class, [
                'label' => 'ButtonType',
                'hide'  => true,
            ]);
    }

    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => null,
        ]);
    }
}

@xabbuh
Copy link
Member
xabbuh commented Nov 23, 2019

I fail to see a real use case for this feature. So I am going to close here. We can reconsider this decision in the future if more information pops up that indicates otherwise.

@xabbuh xabbuh closed this as completed Nov 23, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants
0