web-dev-qa-db-ja.com

単一のカスタムフィールドウィジェットの複数の要素で#ajaxを使用するにはどうすればよいですか?

3列のカスタムフィールドタイプ製品、色、数量を作成しました。

理想的には、ユーザーにはproductを選択するためのドロップダウンが1つだけ表示されます。製品を選択すると、2番目のドロップダウンが読み込まれ、その製品で利用可能なcolorsのみが表示されます。色を選択すると、ユーザーが数量を入力できるテキストフィールドが表示されます。ユーザーが別の製品を選択した場合、2番目のドロップダウンは、その製品で使用可能な色で再読み込みされます。

通常のFAPIフォームでは、要素の#ajaxプロパティを使用してコールバックでreturn drupal_rebuild_form('my_form_name', $form_state);を実行し、元のフォームコンストラクターで実際に$form_state['values']配列を確認して、変更が必要なもの(つまり、「製品」が選択されている場合、その値を使用して「色」ドロップダウンを事前に入力できます)。ウィジェットでは、これと同じロジックは適用されないようです。これは、主に複数の値を持つフィールド(私の場合は、unlimited)を使用できるためです。 return drupal_rebuild_form(...)のようなことをしても、私のhook_field_widget_form()は実行されません。

私が機能している唯一のことは、#element_validate要素の#ajaxプロパティを使用して、$form_state['rebuild'] = TRUE;を呼び出し、次に$formのようなものを介して$form['my_custom_namespace'][$delta_or_something]['product'] = $value_chosen_by_user;にいくつかのダミーコンテンツをスローする検証関数を実行することです。次に、元のフォームコンストラクタで確認できます。 if (isset($form_state['my_custom_namespace'][$delta_or_something]['product'])) { ... }

これは奇抜で、多くの無関係なコードであり、単純なハックのように感じられます。私はアドレスフィールドモジュールから#element_validateトリックを引き出しました。それは本当ですか?例として使用できるこのようなフィールドを提供する他のD7モジュールはありますか?

私の現在のコードは以下です。これは複数値フィールドでは機能しません–複数の製品と色が選択されたときに検証エラーが発生します:(とにかく、多分誰かが助けることができます...

field_supplemental_item.install:

/**
 * Implements hook_field_schema().
 */
function field_supplemental_item_field_schema() {
  $columns = array(
    'product' => array('type' => 'int', 'not null' => FALSE),
    'color' => array('type' => 'int', 'not null' => FALSE),
    'qty' => array('type' => 'int', 'not null' => FALSE),
  );
  return array(
    'columns' => $columns,
  );
}

field_supplemental_item.module:

<?php

/**
 * Implements hook_field_info().
 */
function field_supplemental_item_field_info() {
  return array(
    'field_supplemental_item' => array(
      'label' => t('Supplemental Item'),
      'description' => t('A field that references additional products to add to an order.'),
      'settings' => array(),
      'instance_settings' => array(),
      'default_widget' => 'field_supplemental_item_default_widget',
      'default_formatter' => 'field_supplemental_item_default_formatter',
    ),
  );
}

/**
 * Implements hook_field_settings_form().
 */
function field_supplemental_item_field_settings_form($field, $instance, $has_data) {
  $form = array();
  return $form;
}

/**
 * Implements hook_field_instance_settings_form().
 */
function field_supplemental_item_field_instance_settings_form($field, $instance) {
  $form = array();
  return $form;
}

/**
 * Implements hook_field_formatter_info().
 */
function field_supplemental_item_field_formatter_info() {
  return array(
    'field_supplemental_item_default_formatter' => array(
      'label' => t('Default'),
      'field types' => array('field_supplemental_item'),
    ),
  );
}

/**
 * Implements hook_field_formatter_view().
 */
function field_supplemental_item_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  foreach ($items as $delta => $item) {
    $element[] = array('#markup' => "here i am!");
  }
  return $element;
}

/**
 * Implements hook_field_widget_info().
 */
function field_supplemental_item_field_widget_info() {
  return array(
    'field_supplemental_item_default_widget' => array(
      'label' => t('Default'),
      'field types' => array('field_supplemental_item'),
      'settings' => array(),
      'behaviors' => array(
        'multiple values' => FIELD_BEHAVIOR_DEFAULT,
        'default value' => FIELD_BEHAVIOR_DEFAULT,
      ),
    ),
  );
}

/**
 * Implements hook_field_widget_form().
 */
function field_supplemental_item_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  $element_key = drupal_html_id('field-supplemental-item');

  $element['#prefix'] = "<div id='{$element_key}'>";
  $element['#suffix'] = "</div>";
  $element['element_key'] = array(
    '#type' => 'value',
    '#value' => $element_key
  );

  if (isset($form_state['field_supplemental_item'][$element_key]['product'])) {
    $product_id = $form_state['field_supplemental_item'][$element_key]['product'];
  } else if (isset($items[$delta]['product'])) {
    $product_id = $items[$delta]['product'];
  } else {
    $product_id = NULL;
  }

  // Product
  $element['product'] = array(
    '#type' => 'select',
    '#title' => 'Product',
    '#options' => array('_none' => '- Select -') + _field_supplemental_item_get_data('products'),
    '#default_value' => $product_id,
    '#ajax' => array(
      'callback' => 'field_supplemental_item_field_widget_product_callback',
      'wrapper' => $element_key,
      'method' => 'replace',
    ),
    '#element_validate' => array('field_supplemental_item_field_widget_product_validator'),
    '#limit_validation_errors' => array(), // Don't validate any element when the product is changed.
  );

  // Color
  $element['color'] = array(
    '#type' => 'select',
    '#title' => 'Color',
    '#options' => (($product_id)) ? _field_supplemental_item_get_data('colors', $product_id) : array(),
    '#limit_validation_errors' => array(), // Don't validate any element when the product/color is changed.
  );

  // Quantities
  // ...

  return $element;
}

/**
 * Element validate callback: rebuilds the form on value change and stores the current value in the $form_state for retrieval on rebuild.
 */
function field_supplemental_item_field_widget_product_validator($element, &$form_state) {
  // If the product was changed, rebuild the form.
  if ($element['#default_value'] != $element['#value']) {
    $form_state['rebuild'] = TRUE;
  }

  // Store the value in the form state for retrieval by the
  // widget form regardless of where the widget sits in the $form array.
  $form_state['field_supplemental_item'][$element['#ajax']['wrapper']]['product'] = $form_state['triggering_element']['#value'];
}

function field_supplemental_item_field_widget_product_callback($form, $form_state) {
  $product_id = $form_state['triggering_element']['#value'];

  // The target element is one element below the triggering selector.
  $array_parents = $form_state['triggering_element']['#array_parents'];
  array_pop($array_parents);

  // Iterate over the form parents to find the element.
  $element = $form;
  foreach ($array_parents as $name) {
    $element = &$element[$name];
  }

  $element['product']['#default_value'] = $product_id;
  $element['color']['#options'] = _field_supplemental_item_get_data('colors', $product_id);

  // Return the element, but remove the '_weight' element inserted
  // by the field API.
  unset($element['_weight']);

  return $element;
}

/**
 * Implements hook_field_validate().
 */
function field_supplemental_item_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {}

/**
 * Implements hook_field_widget_error().
 */
function field_supplemental_item_field_widget_error($element, $error, $form, &$form_state) {}

/**
 * Implements hook_field_is_empty().
 */
function field_supplemental_item_field_is_empty($item, $field) {
  return (empty($item['product']));
}

EDIT 9/16/12:現在、フォーム全体を置き換えることでこれを実現できることを付け加えます。フォーム全体をばかげていると感じ、大きくてかさばるエンティティ編集フォームにかなり集中的になる可能性があります–そして、すべての$ deltaに必要なデータを取得するために$ form_stateを調べる必要があります...:

function hook_field_widget_form(...) {
  $form['#prefix'] = '<div id="mymodule-field-widget-form-wrapper">';
  $form['#suffix'] = '</div>';
  ...
}
function mymodule_field_widget_form_ajax(&$form, &$form_state) {
  return drupal_rebuild_form($form_state['build_info']['form_id'], $form_state, $form);
}
6

Hierarchal Selectモジュールは、直面している問題に最適なソリューションです。 HSプラグインを使用して、カテゴリに関連付けられたグループの最初の選択ボックスと2番目の選択ボックスのリストにカテゴリをリストするためにこれを行いました。

以下のコードを参照してください:

function category_groups_hr_form_alter(&$form, &$form_state, $form_id) {
  global $user;
  $account = clone($user);
  if ($form_id == "question_node_form") {
    $category_group_hs = array();
    $group_categories = taxonomy_get_tree(3, 0, 1, FALSE);
    asort($group_categories);
    foreach ($group_categories as $item) {
      $category_group_hs[$item->tid]['label'] = $item->name;
      $query = db_select('taxonomy_index', 't');
      $query->addTag('node_access');
      $query->join('node', 'n', 'n.nid = t.nid');
      $query->join('og', 'og', 'n.nid = og.etid');
      $query->condition('t.tid', $item->tid);
      $query->condition('n.type', 'groups');
      $query->addField('t', 'nid');
      $query->addField('t', 'tid');
      $query->addField('n', 'title');
      $query->addField('og', 'gid');
      $query->addField('n', 'uid');
      $result = $query->execute();
      while ($group = $result->fetchObject()) {
          $category_group_hs[$item->tid]['children'][$group->nid] = array('label' => $group->title);
      }
    $category_id = '';
    $group_nid = '';
    $title = t('Select Category > Group :');
    $form['hs_category_group'] = array(
      '#type' => 'hierarchical_select',
      '#title' => $title,
      '#size' => 1,
      '#required' => TRUE,
      '#config' => array(
        'module' => 'hs_smallhierarchy',
        'params' => array(
          'hierarchy' => $category_group_hs,
          'id' => 'hs-category-group',
          'separator' => '|',
        ),
        'save_lineage' => 0,
        'enforce_deepest' => 0,
        'entity_count' => 0,
        'require_entity' => 0,
        'resizable' => 1,
        'level_labels' => array(
          'status' => 0,
          'labels' => array(
            0 => t('Group Category'),
            1 => t('Group')
          ),
        ),
        'dropbox' => array(
          'status' => 0,
          'title' => t('All selections'),
          'limit' => 0,
          'reset_hs' => 1,
        ),
        'editability' => array(
          'status' => 0,
          'item_types' => array(),
          'allowed_levels' => array(
            0 => 0,
            1 => 0,
            2 => 1,
          ),
          'allow_new_levels' => 0,
          'max_levels' => 2,
        ),
        'animation_delay' => 400,
        'exclusive_lineages' => array(),
        'render_flat_select' => 0,
      ),      
      '#default_value' => $category_id . '|' . $group_nid,
    );
    $form['#validate'][] = 'hs_category_and_group_validate';
    $form['#submit'][] = 'hs_category_and_group_submit';
  }
}

使ってます hs_smallhierarchy重い作業を行うモジュール... HSプラグインAPIの詳細については、hierarchical_selectモジュール内のAPI.txtファイルを確認してください。..

HSモジュールを使用してクラックできることを願っています。

2
Anil Sagar