zend-frameworkzend-formzend-framework3

Different value_options in Form Collection


I hava a Collection, in which a field User (a multiselect) depends on a previous Select, the Department. Therefore each User select contain a different "value_options".

How can I set different "value_options" when retrieving the form for each row of the Collection?


Solution

  • You have different options:

    I personally discourage the second option. Is there just to say that it is a possible solution, but NO.

    The first option, the API, is interesting, but requires actually more work, especially if it is the only endpoint in your application.

    The third option is the one I always use, since it requires the less code and it quite simple to implement.

    In your form, you have your two elements:

    $this->add([
        'name' => 'department_id',
        'type' => 'Select',
        'attributes' => [
            'id' => 'department_id'
        ],
        'options' => [
            'value_options' => [
                1 => 'Marketing',
                2 => 'IT',
                3 => 'Logistic'
            ]
        ]
    ]);
    
    $this->add([
        'name' => 'user_id',
        'type' => 'Select',
        'attributes' => [
            'id' => 'user_id',
            'multiple' => true
        ],
        'options' => [
            'value_options' => [
                [
                    'value' => 1,
                    'label' => 'John Doe - Marketing',
                    'attributes' => ['data-department-id' => 1]
                ],
                [
                    'value' => 2,
                    'label' => 'Jane Doe - Marketing',
                    'attributes' => ['data-department-id' => 1]
                ],
                [
                    'value' => 3,
                    'label' => 'Jack Doe - IT',
                    'attributes' => ['data-department-id' => 2]
                ],
                [
                    'value' => 4,
                    'label' => 'Dana Doe - IT',
                    'attributes' => ['data-department-id' => 2]
                ],
                [
                    'value' => 5,
                    'label' => 'Frank Doe - Logistic',
                    'attributes' => ['data-department-id' => 3]
                ],
                [
                    'value' => 6,
                    'label' => 'Lara Doe - Logistic',
                    'attributes' => ['data-department-id' => 3]
                ]
            ]
        ]
    ]);
    

    As you can see, all the users are put in the value_options. Keep in mind that this is just an example, you should use custom elements to populate this kind of selects ;)

    Then, in your view, you render the elements:

    <?= $this->formElement($this->form->get('department_id')); ?>
    <?= $this->formElement($this->form->get('user_id')); ?>
    

    And you finally add the JS code to handle the filter. Here I use jQuery, but it's not necessary to use it:

    $(function () {
        // Filter users when you load the page
        filterUsers(false);
        // Filter users when you change value on multiselect
        $('#department_id').on('change', function () {
            filterUsers(true);
        });
    });
    function filterUsers(resetValue) {
        var departmentId = $('#department_id').val();
        // Remove previous value only when filter is changed
        if (resetValue) {
            $('#user_id').val(null);
        }
        // Disable all options
        $('#user_id option').attr('disabled', 'disabled').attr('hidden', true);
        // Enable only those that respect the criteria
        $('#user_id option[data-department-id=' + departmentId + ']').attr('disabled', false).attr('hidden', false);
    }
    

    Final tip: don't forget to create and add to the form a Validator to check the couple department_id - user_id is correct, just to avoid (on my example) to accept Lara Doe (logistic) with IT department ;)