* question answered: Help needed with hook_node_submit in custom module to save data *


In Drupal 7, I have a custom content type. In this, there is a multi select term reference to select categories for that content.

I now need to be able to select one from the previously selected category and mark it somehow as the 'main' category.

Lets say I have a multiple select term reference field with the following options:

Apples
Bananas
Pears
Oranges
Grapes
Pineapples

The user selects Apples, Pears and Grapes. Now I either need to:

  1. Programmatically create another field for each of these selected—maybe with an ajax callback—and have radio buttons so I can only select one of the selected terms which one is my main category.
  2. Create a radio field next to the ticked items—maybe also with ajax—where I can select the main one out of the selected.

Does anyone have any ideas regarding this?

To be clearer, I have a lot of these lists on one content type. Repeating each list as a single value list is not an option.

I guess my best bet is to use the hook_form_alter() with some sort of AJAX callback to create either a single radio button next to the tick box the user has just ticked, or programmically create a new radio field list for each item checked in the specified list.

Update: Ok, I have decided that the best way to do it would be to create a custom module that uses ajax to create a radio button for each checked checkbox, allowing to select the element that should be used as the main element.

So I have used hook_form_alter() to add an #after_build function as we need to wait for the form to be rendered before we can access the tax term values.

Here is my module so far. I use a lot of comments so it should be clear what I am trying to do:

MYMODULE.module

/**
 * Implementation of HOOK_form_alter()
 * Do the ajax form alteration
 */
function MYMODULE_form_alter(&$form, &$form_state, $form_id) {

  // 1.CONTENT FORM
  // I created a custom content type 'content' and added a term
  // reference to it 
  if($form_id == 'content_node_form') {

    // tax term ref is the main part, so let us
    // remove title and body fields
    unset($form['body']);
    unset($form['title']);

    // do our stuff after the form has been rendered ...
    $form['#after_build'][] = 'MYMODULE_after_build';

  }
}

/**
 * after_build function for content_node_form
 */
function MYMODULE_after_build(&$form, &$form_state) {

    dsm($form);  

    // In the after_build call we can now actually use the 
    // element_children function to grab the values of the fields that
    // don't start with a hash tag #
    // in this test case 1,2,3,4 and 5

    // wrap each of the elements rendered ...
    foreach(element_children($form['field_taxonomy']['und']) as $key) {

      $form['field_taxonomy']['und'][$key] += array(

        // this is added before the element and then replaced by our callback ..
        // we use the $key value in the id so that we know which div to replace 
        // depending on which checkbox is checked ...
        '#prefix' => '<div class="taxonomy_term_wrapper">
                        <div id="callback_replace_'.$key.'">Replace Me ' . $key . '</div>',

        // this is added after the element so we basically wrap around it ..
        '#suffix' => '</div>',

        // add some ajax stuff here ...
        '#ajax' => array(
          // name of the callback function to call upon change
          'callback' => 'MYMODULE_callback',
          // the id of the element that will be replaced
          'wrapper' => 'callback_replace_'.$key,
          // replace the wrapper
          'method' => 'replace',
          // what kind of effect do we want ...
          'effect' => 'fade',
          // show progress on callback
          'progress' => array('type' => 'throbber'),
        ),
      ); 



      if (!empty($form_state['values']['field_taxonomy']['und'][$key])) {
        // the form to show upon change ...
        $form['field_taxonomy']['und']['main_cat'] = array(
          // we want a radio button
          '#type' => 'radio',
          '#title' => t('Test Title'),
          '#description' => t('Test Description ...'),
          '#default_value' => empty($form_state['values']['field_taxonomy']['und'][$key]) ?
                              $form_state['values']['field_taxonomy']['und'][$key] :
                              $form_state['values']['field_taxonomy']['und'][$key],
        );
      }

    }

  return $form;
} 

function MYMODULE_callback($form, $form_state) {
 return $form['field_taxonomy']['und']['main_cat'];
}

This is what it currently looks like prior to checking a box:

This is what it currently looks like prior to checking a box

The HTML of the rendered form is the following one:

screenshot

  • It's not really an answer as such, but try taking a look at the Examples module. It has some neat ajax form examples that might help you along your way :) drupal.org/project/examples – Chapabu Nov 30 '11 at 9:41
  • Hi Chapabu, thanks for your answer. I have used the ajax examples, but my problem lies with the fact that I need to use after_build to add my code and now I am stumped as to wht it does nothing ... I have added a lot more code above - including my progress so far with the module. Maybe you can see the errors – tecjam Nov 30 '11 at 9:49
  • hmm..the only thing I can (quickly) see different in your after_build is the format. In the Drupal docs it says it should look like this - $form['#after_build'] => array('MYMODULE_after_build'); – Chapabu Nov 30 '11 at 9:59
  • I believe $form['#after_build'] => array('MYMODULE_after_build'); is the same as $form['#after_build'][] = 'MYMODULE_after_build'; - Note the [] – tecjam Nov 30 '11 at 10:18
  • Also, the after_build function seems to work fine as it does wrap my taxonomy terms in my custom div and adds my replace divs. Just the callback doesn't work .. – tecjam Nov 30 '11 at 10:19

create the items as check boxes. after they are selected, show a drop down list with these items or another radio button to select the primary category. you can hide the second drop down using the following code until the first check boxes are selected.

'#states' => array(
'visible' => array(
':input[name="your checkbox"]' => array('checked' => TRUE),
 ),
)

add this to the element you want to hide.this will be visible only when the checkbox is checked.

How about two lists, one titled primary that takes one selection, the other multiple. You will still need to address the second list or selection to eliminate duplications.

  • Hi and thanks for your answer. Yes, of course that would work. However, I have a hell of a lot of lists on the content create page and having them all twice is not an option. I simplyfied the question above for the sake of an example. – tecjam Nov 17 '11 at 14:42

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.