On day 3 of trying to get this dang dynamic form to work. Scoured all the internet with only a few instances of someone trying this with a custom form. I am trying to create a dynamic form by building it multiple inside a foreach loop and rendering it to a template. Everything works great until I submit a value. The values to the correct edited form do not pass to the validate or submit handler.

Further more, I've not been able to figure out how to attach an ajax callback to the correctly edited form as well. However I left that snippet out for clarity on the problem.

  • Add the theme
  • Render the theme to a field on the node being viewed
  • Preprocess the theme. Build the forms pass them to template
  • Make the form, validate, submit functions
  • Hook_forms to add a callback to the dynamic form

Here is the code

ec_task_credits.module

<?php
//[+] ADD THEME
function ec_task_credits_theme($existing, $type, $theme, $path) {
    return [
        'vocab_list' => [
            'template' => 'vocab-list',
            'variables' => array(),
            'path' => drupal_get_path('module', 'ec_task_credits') . '/theme',
        ],
    ];
}

//[+] RENDER TPL INTO FIELD ON NODE_VIEW
function ec_task_credits_node_view($node, $view_mode, $langcode) {

    if ($node->type == 'ol_todo') {

            //render the template
            $output = theme('vocab_list', array(
                    'task_nid' => $node->nid,
                    'task_list_nid' => $node->field_todo_list_reference[LANGUAGE_NONE][0]['nid'],
                )
            );

            //return the template to a field on node_view
            $node->content['vocab_list_container'] = array(
                '#markup' => $output,
            );
    }
}

//[+] PREPROCESS THE TEMPLATE FILE VARS - BUILD FORMS
function ec_task_credits_preprocess_vocab_list(&$vars) {
    //build all the forms and send them to the template file

    $vocab_insulation = taxonomy_vocabulary_machine_name_load('credit_list_insulation');
    $terms_insulation = entity_load('taxonomy_term', FALSE, array('vid' => $vocab_insulation->vid));
    $vocab_terms = $terms_insulation;   //simple array of terms

    if (!empty($vocab_terms)) {
        $i=0;
        foreach ($vocab_terms as $tid => $term){
            $vars['test_form'][] = drupal_get_form('ec_task_credits_credit_form', $term->tid, $term->name);
            $i++;
        }
    }
}

//[+] CUSTOM FORM CALLED MANY TIMES
function ec_task_credits_credit_form($form, &$form_state, $term_id, $term_name) {
    if (!empty($term_id)) {
        $form_id = '';
        $form['#id'] = 'ec_task_credit_form_'.$term_id; //dynamic ID - works
        $form['#base'] = 'ec_task_credits_credit_form'; //same #base

        $form['term_group'] = array(
            'term_field' => array(
                '#markup' => $term_name,
                '#tid' => $term_id,
                '#id' => 'term_field_'.$term_id,
            ),
            'browse_credit' => array(
                '#type' => 'managed_file',
                '#name' => 'browse_credit_'.$term_id,
                '#title' => t('Block image'),
                '#size' => 40,
                '#id' => 'browse_credit_'.$term_id,
                '#description' => t("Image should be less than 400 pixels wide and in JPG format."),
                '#upload_location' => 'public://'
            ),
            'comment_credit' => array(
                '#type' => 'textarea',
                '#name' => 'comment_credit_'.$term_id,
                '#id' => 'comment_credit_'.$term_id,
                '#title' => t('Comment'),
                '#value' => t('Comment'),
            ),
            'submit' => array(
                '#type' => 'submit',
                '#name' => 'comment_credit_'.$term_id,
                '#id' => 'comment_credit_'.$term_id,
                '#title' => t('Save'),
                '#value' => t('Save'),
            ),
        );

    }
    return $form;
}

//[+] VALIDATE FORM - RUNS ON SUBMIT BUT DOES NOT CONTAIN SUBMITTED $VALUES
function ec_task_credits_credit_form_validate($form, &$form_state) {
    dpm('validate handler is running');
    dpm($form_state);
    dpm($form);
    form_set_error('', t('Force this error to avoid submit for now'));
}

//[+] SUBMIT FORM - NOT DEALING WITH THIS YET
function ec_task_credits_credit_form_submit($form, &$form_state) {

    if (isset($form_state['values']['browse_credit'])) {
        $file = file_load($form_state['values']['browse_credit']);
        $file->status = FILE_STATUS_PERMANENT;
        file_save($file);
    }
}

//[+] HOOK_FORMS()
/*hook_forms does not work - it never prints a form_id of ec_task_credits_credit_form.
* It never sees it and thus cannot work with it
*/
function ec_task_credits_forms($form_id, $args) {

//  $forms['ec_task_credits_credit_form']['callback'] = 'ec_task_credits_credit_form_callback';
//  return $forms;

//$form_id only prints other forms on the page. never my custom form

    $args = func_get_args();
    $form_id = $args[0][0]; //trying to get the damn form_id - doesnt work

    $dynamic_forms = array(
        'ec_task_credits_credit_form',
    );

    $forms = array();
    foreach ($dynamic_forms as $dynform) {
        if (strpos($form_id, $dynform) === 0) {
            $forms[$form_id] = array(
                'callback' => $dynform,
                'callback arguments' => array('testarg'),
            );
        }
    }
    return $forms;
}

theme/vocab-list.tpl.php

<h1>Vocab List</h1>
<div class="credit-list-container">
    <?php print render($test_form);?>
</div>

IMG: The Output Which all appears to be fine and dandy

https://i.stack.imgur.com/nZDaL.png

IMG: Then when you submit a value it reloads the page and the value is not in $form_state

https://i.stack.imgur.com/E0CIX.png

Does anyone have any idea what I'm doing wrong or how I could set this up differently? How should I be make a dynamic form like this?

  • Dang I'm seriously stumped. I don't even know what else to try. It seems the actual id numbers on the inputs in the forms are not different than eachother. Even though I am setting the ids dynamically in the form construction. – Matt Campbell Jun 2 '17 at 15:20

Alright I figured it out. In case anyone else runs into this problem. This is how you can setup a dynamic form, but don't set a variable #name in the array of your form element. I'm not sure why but this cause the values not to carry over to the submit handler.

In conclusion, in order to make a dynamic form:

  • create a custom form
  • call that form many times, along with a variable argument, such as a ticker or other unique number
  • set the $form['#id'] to 'form_name_'.$your_ticker
  • set the $form['#base'] as just 'your_form_name';

This will allow many similar forms to be made all using the same your_form_name_submit handler.

I did not need to use hook_forms or rather I could not figure out how to, but this still works.

Here is the fixed code:

<?php
//[+] ADD THEME
function ec_task_credits_theme($existing, $type, $theme, $path) {
    return [
        'vocab_list' => [
            'template' => 'vocab-list',
            'variables' => array(),
            'path' => drupal_get_path('module', 'ec_task_credits') . '/theme',
        ],
    ];
}

//[+] RENDER TPL INTO FIELD ON NODE_VIEW
function ec_task_credits_node_view($node, $view_mode, $langcode) {

    if ($node->type == 'ol_todo') {

        //render the template
        $output = theme('vocab_list', array(
                'task_nid' => $node->nid,
                'task_list_nid' => $node->field_todo_list_reference[LANGUAGE_NONE][0]['nid'],
            )
        );

        //return the template to a field on node_view
        $node->content['vocab_list_container'] = array(
            '#markup' => $output,
        );
    }
}

//[+] PREPROCESS THE TEMPLATE FILE VARS - BUILD FORMS
function ec_task_credits_preprocess_vocab_list(&$vars) {
    //build all the forms and send them to the template file

    $vocab_insulation = taxonomy_vocabulary_machine_name_load('credit_list_insulation');
    $terms_insulation = entity_load('taxonomy_term', FALSE, array('vid' => $vocab_insulation->vid));
    $vocab_terms = $terms_insulation;   //simple array of terms

    if (!empty($vocab_terms)) {
        $i=0;
        foreach ($vocab_terms as $tid => $term){
            $vars['test_form'][] = drupal_get_form('ec_task_credits_credit_form', $term->tid, $term->name);
            $i++;
        }
    }
}

//[+] CUSTOM FORM CALLED MANY TIMES
function ec_task_credits_credit_form($form, &$form_state, $term_id, $term_name) {
    if (!empty($term_id)) {
        $form_id = '';
        $form['#id'] = 'ec_task_credit_form_'.$term_id; //dynamic ID
        $form['#base'] = 'ec_task_credits_credit_form'; //same #base

        $form['term_group'] = array(
            'term_field' => array(
                '#markup' => $term_name,
                '#tid' => $term_id,
                '#id' => 'term_field_'.$term_id,
            ),
            'browse_credit' => array(
                '#type' => 'managed_file',
                '#title' => t('Block image'),
                '#size' => 40,
                '#id' => 'browse_credit_'.$term_id,
                '#description' => t("Image should be less than 400 pixels wide and in JPG format."),
                '#upload_location' => 'public://'
            ),
            'comment_credit' => array(
                '#type' => 'textarea',
                '#id' => 'comment_credit_'.$term_id,
                '#title' => t('Comment'),
            ),
            'submit' => array(
                '#type' => 'submit',
                '#id' => 'submit_credit_'.$term_id,
                '#title' => t('Save'),
                '#value' => t('Save'),
            ),
        );

    }
    return $form;
}

//[+] VALIDATE FORM
function ec_task_credits_credit_form_validate($form, &$form_state) {
    dpm($form_state['values']);
}

//[+] SUBMIT FORM
function ec_task_credits_credit_form_submit($form, &$form_state) {
//do stuff with submitted values
    if (isset($form_state['values']['browse_credit'])) {
        $file = file_load($form_state['values']['browse_credit']);
        $file->status = FILE_STATUS_PERMANENT;
        file_save($file);
    }
}

This also shows how to work with submitting a file or image on a custom form. Using managed_file

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.