I am developing a form wizard with 5 steps using the Form and AJAX APIs.
My form also has some add/remove logic for some of the input fields.
Both wizard and add/remove were built on top of the relevant AJAX Examples.

Everything works well, my data comes and goes through steps (both on "Next" and "Previous" actions), except for this weird problem.

On step3 I have a set of hidden input fields with empty values. These values get filled using javascript when a user drops a draggable element on a droppable area (using jQuery UI Draggable & Droppable widgets). The values are actually the positioning (top, left) of the draggable element separated with a "_" (for example something like "123_54").

This is what I get using the dd() function (Devel module helper) to output all values of step3 (the step on which the problem occurs):

Array
(
    [step3] => Array
        (
            [instrument] => Array
                (
                    [0] =>
                )

        )
)

where [step3][instrument][0] should be the hidden field's value

If I create a hidden field with a preset value it gets posted as expected.

Anyone has any idea why this might be happening...?

(If it's of any use to anyone, I use jQuery 1.7.1 & jQuery UI 1.8.11 on a Drupal 7.21 installation)

Comments

criznach’s picture

In theory it should work, as many modules do this. Can you trim it down to the minimum piece of code that doesn't work and post it here?

void13’s picture

the php code that builds the form is:

case 3: // 3rd step
            
            $form['step3'] = array(
                '#type' => 'fieldset',
                '#prefix'=> '<div id="instrument_list" class="droppable" style="position: relative;">',
                '#suffix'=> '</div><div id="mainapp">
                                <div id="stage_plot_container">
                                    <div id="stage_plot" class="droppable"></div>
                                </div>
                             </div>'
            );            
 
            for ($i = 0; $i < $form_state['counter']; $i++) {
                
                $form['step3']['instrument'][$i] = array(
                    '#type' => 'hidden',
                    '#value' => empty($form_state['values']['step3']['instrument'][$i]) ? '' : $form_state['values']['step3']['instrument'][$i],
                    '#prefix'=> '<div class="draggable" data-hidden="step3-instrument-'.$i.'" id="'.$form_state['storage']['values']['step1']['right']['bandmember'][$i]['instrument'].'">'.ucfirst($form_state['storage']['values']['step1']['right']['bandmember'][$i]['instrument']).'</div>',
                    '#attributes' => array('id' => 'edit-step3-instrument-'.$i)
                );
            }
            
        break;

which works fine and prints out the correct fields

then the javascript code is:

jQuery('#stage_plot').droppable({
        accept: '.draggable',
        tolerance: 'fit',
        drop: function (event, ui) {
            // get the offset position of the dropped element (from window)
            var droppedPos = ui.draggable.offset()
            // get the offset position of the droppable area (from window)
            var stagePos   = jQuery(this).offset();
            // position of the dropped element relative to the droppable area
            var droppedTop = droppedPos.top - stagePos.top;
            var droppedLeft = droppedPos.left - stagePos.left;
            
            // add a class to the dropped element
            ui.draggable.addClass('dropped');
            // set correct position, relative to the stage plot
            ui.draggable.css('position', 'absolute');
            ui.draggable.css('top', droppedTop);
            ui.draggable.css('left', droppedLeft);
            // set respective hidden input value
            dragId = ui.draggable.attr('id');
            jQuery('input#hidden_'+dragId).val(droppedTop+'_'+droppedLeft);
            // append dropped element to stage-plot to fix markup issue
            ui.draggable.appendTo(jQuery(this));
        }
    });

which also works fine as monitored in firebug.

The result is that when the user drops a div in the droppable area, a hidden input's value is changed.

Printed hidden field before JS:

<input id="edit-step3-instrument-0" type="hidden" name="step3[instrument][0]" value="">

and after "drop" event:

<input id="edit-step3-instrument-0" type="hidden" name="step3[instrument][0]" value="154_214">

But when I submit the form, hidden fields POST nothing at all as shown in the original post.

jaypan’s picture

Your problem lies here (and this is why you should always include your code - this could have been debugged in your first post):

                    '#value' => empty($form_state['values']['step3']['instrument'][$i]) ? '' : $form_state['values']['step3']['instrument'][$i],

Whenever you set #value on an element, it cannot be changed by the user. Or rather, it can be changed, but any changes will be disregarded by the system upon submit, and replaced with the value you provided in #value.

Change #value to #default_value (which can be changed by the user), and it should solve your problem.

Contact me to contract me for D7 -> D10/11 migrations.

void13’s picture

Thanks a lot, this solved the problem and everything works well!

sandip27’s picture

I have a plain text field added in Content Type viz., 'field_nominee_uid'... This field doesn't have any default value and is blank on form load.

Now, I am altering the value of this field via jQuery as -
(function ($) {
Drupal.behaviors.NominationForm = {
attach: function(context) {
$('.my-portal-node-form input#edit-title-0-value').on('focusout',function() {
// code to fetch the employee id of selected user via service which works okay and
// receives correct value in 'data_result.employeeID'
// and now assign the value to field.
$('.my-portal-node-form #edit-field-nominee-emp-id-0-value').val(data_result.employeeID);
});
}
};
})(jQuery);

This works like charm and assigns the value to field.

Problem is, now that I want to make this field hidden via form alter, it dosen't post any value further after submission.

For instance, to make the field hidden I am doing as follows -

function my_portal_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$form['field_nominee_emp_id']['widget'][0]['value']['#type'] = 'hidden';
}

Setting Access to False makes the field disappear altogether from form, so that won't work.

function my_portal_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$form['field_nominee_emp_id']['#access'] = FALSE;
}

I am sure, I am missing something but not able to figure out what.  Any suggestions please ?

Also, I am not sure if '.my-portal-node-form #edit-field-nominee-emp-id-0-value' or for that matter '$form['field_nominee_emp_id']['widget'][0]['value']['#type']' is the correct way to address the field in jQuery, so if possible, please comment about that as well.
 

BTW, Drupal Version : 9.4.8PHP Version : 8.1.11

Thanks

jaypan’s picture

Drupal's security will discard any submitted values from form elements that were not added by Drupal.

Contact me to contract me for D7 -> D10/11 migrations.

sandip27’s picture

Thanks @Jaypan for the reply.  If that is the case, how come if I keep the field as Textfield then it accepts and tracks the value in Form Submission correctly but fails for hidden field only ?  Is there any workaround to my problem ?  All I want to do is post the dynamic value further...  Below is the code I use to pull the records off the DB in jQuery...

// Fetch Employee Data
$.ajax({
url: "/v1/get_data",
type: 'post',
data: 'param1=' + encodeURIComponent(param1_value) + '&param2=' + encodeURIComponent(param2_value) ,
dataType: 'json',
error: function(XMLHttpRequest, textStatus, errorThrown) {
console.log("XMLHttpRequest: " + JSON.stringify(XMLHttpRequest));
console.log("textStatus: " + JSON.stringify(textStatus));
console.log("errorThrown: " + JSON.stringify(errorThrown));
},
success: function (data_result) {
// Assign Data back to fields.
if (typeof data_result.error == "undefined" || data_result.error == '' || data_result.error == null) {
$('.my-portal-node-form #edit-field-nominee-emp-id-0-value').val(data_result.employeeID);
} else {
// Setting default value to "-9999" to avoid failure just in case.
$('.my-portal-node-form #edit-field-nominee-emp-id-0-value').val(-999);
}
}
});

Here /v1/get_data is a regular Drupal path url and that in turn gives call to service to fetch the data.  Is there any other way to achieve the needed feature ?

Do you think, regular AJAX on title textfield would be able to achieve this ? If so,  would be great if you could give some example to achieve that please ....

Thanks

P.S. I can keep those fields as text fields and achieve the result by apply CSS to hide those fields to naked eyes, but I am strongly against that idea as I feel, I am not achieving Drupal's power to its fullest in that case.