I'm looking for any examples of creating a SHS enabled widget programatically via the Form API. I've looked in the documentation, the module code, and done some searches, but to no avail.

I have a two level vocabulary of options that I need to display on a Commerce checkout pane which visitors should be able to select from. Whether these options appear depends on the product in their cart, and this logic is handled in a custom module. Because it's custom code there isn't an entity to add a straight term reference field to.

I was hoping that maybe there is some way to create the widget via the Form API, something like (broken code):

// Create custom checkout pane for visitors without a referal code
$form['custom_referrer_field'] = array(
      '#type' => 'shs_default', // also tried 'shs'
      '#title' => t('Please select an option'),
      '#size' => 1,
      '#shs_settings' => array(
        'node_count' => 0,
        'create_new_terms' => 0,
        'create_new_levels' => 0,
        'force_deepest' => 1,
        'required' => TRUE,
      ),
      '#shs_vocabulary' => $vocabulary, // Vocabulary ID
      '#required' => 1,
      '#attributes' => array(
        'class' => array('shs-enabled'),
      ),
);

But I can't seem to find the right incantation of options to get the element to render.

Is this possible, are there any examples I can look at?

Comments

stBorchert’s picture

Status: Active » Closed (won't fix)

No, this isn't possible with Simple hierarchical select since it transforms term-reference widgets and doesn't create a widget on its own.

You'll need to create the elements using #ajax. See module Examples for in-depth details on how to do this.

garrettc’s picture

Issue summary: View changes

Changed to <?php

geophysicist’s picture

It is possible.

geophysicist’s picture

Main idea:

$field_name = 'field_product' // Existing field with configured shs widget.

$field = field_info_field($field_name) // here should be an array from. Also you can define this array in some custom function.
$field = productfield_base_field(); // Easiest way - to copy this array from feature..

$instance = field_info_instance('node', $field_name, 'product_bundle'); // Instance array.
$instance = productfield_field_instance(); // U can also define it in some custom function.

// TAAA DAAAA Here is our field
$field_form = field_default_form('node', NULL, $field, $instance, LANGUAGE_NONE, NULL, $form, $form_state);
dmudie’s picture

Hi - did you ever get this to work? I'm not having any luck.

Tаo’s picture

  $vocabulary = taxonomy_vocabulary_machine_name_load('site_section');
  $taxonomy_tree = taxonomy_get_tree($vocabulary->vid);
  $terms = array();
  foreach ($taxonomy_tree as $key => $term) {
    $terms[$term->tid] = $term->name;
  }

  $form['my_container'] = array(
    '#type' => 'container',
    '#attributes' => array(
      'class' => array(
        'field-widget-taxonomy-shs',
        ),
      ),
    '#tree' => 1,
  );
  $form['my_container']['my_field'] = array(
    '#type' => 'select',
    '#title' => t('My field'),
    '#options' => $terms,
    '#attributes' => array('class' => array('shs-enabled')),
    '#element_validate' => array('shs_field_widget_validate'),
    '#after_build' => array('shs_field_widget_afterbuild'),
    '#shs_settings' => array(
      'create_new_levels' => 0,
      'create_new_terms' => 0,
      'force_deepest' => 0,
      'node_count' => 0,
      'test_create_new_terms' => array(),
      'required' => TRUE,
    ),
    '#language' => NULL,
    '#field_name' => 'my_field',
    '#default_value' => 0,
    '#field_parents' => NULL,
    '#shs_vocabularies' => array($vocabulary),
  );
gmangones’s picture

How do that in drupal 8?

Thanks.

pratip.ghosh’s picture

Want to know too, how to do this in D8

@gmangones, did you find the way?

lykyd’s picture

For D8 integration it is tricky.
I had great pains to get the selected value sent in the submit callback, as options were set in JS and would disappear from the values of the form_state.
The solution was to manually send in the field declaration the options (that are also generated by SHS in JS) and split the declaration of the "container" and the "widget" form fields.

$field_name = 'term_field';
$vid = 'tags';
$tag_options = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->getQuery()->condition('vid', $vid)->execute();

$shs = [
  'settings' => [
    'required' => TRUE,
    'multiple' => FALSE,
    'anyLabel' => t('- Select a term -'),
    'anyValue' => '_none',
    'addNewLabel' => t('Add another item'),
    'force_deepest' => TRUE,
    'create_new_items' => FALSE,
    'create_new_levels' => FALSE,
    'display_node_count' => FALSE,
  ],
  'bundle' => $vid,
  'baseUrl' => \Drupal\Core\Url::fromUri('base:/shs-term-data')->toString(),
  'cardinality' => 1,
  'parents' => [[['parent' => 0, 'defaultValue' => '_none']]],
  'defaultValue' => NULL,
];

$form[$field_name] = [
  '#type' => 'container',
];

$form[$field_name]['widget'] = [
  '#type' => 'select',
  '#title' => t('Select a term'),
  '#key_column' => 'tid',
  '#field_parents' => [],
  '#field_name' => $field_name,
  '#shs' => $shs,
  '#options' => $tag_options,
  '#attributes' => [
    'class' => ['shs-enabled'],
  ],
  '#attached' => [
    'library' => ['shs/shs.form'],
  ],
  '#element_validate' => [[
    '\Drupal\shs\Plugin\Field\FieldWidget\OptionsShsWidget',
    'validateElement',
  ]],
  '#after_build' => [[
    '\Drupal\shs\Plugin\Field\FieldWidget\OptionsShsWidget',
    'afterBuild',
  ]],
];
khan_ahtesham’s picture

@pratip.ghosh did you find a way to implement this in D8?

khan_ahtesham’s picture

Anyone looking for the solution in D8 can try this :

Created two form fields and populated first field with parent items and based on that value wrote a callback to load all children items which will be populated to the second drop down using jquery.

Nikit’s picture

In comment #8 just change

  $shs = [ 
    // ...
   ]; 

to

    $shs = [
      'settings' => [
        'required' => TRUE,
        'multiple' => FALSE,
        'anyLabel' => t('- Any -'),
        'anyValue' => 'All',
        'addNewLabel' => '',
        'force_deepest' => FALSE,
        'create_new_items' => FALSE,
        'create_new_levels' => FALSE,
        'display_node_count' => FALSE,
      ],
      'bundle' => $vid,
      'baseUrl' => 'shs-term-data',
      'cardinality' => 1,
      'parents' => [[['parent' => 0, 'defaultValue' => 'All']]],
      'defaultValue' => NULL,
      'labels' => [
        t('Country'),
        t('Region'),
        t('City'),
      ],
    ];

and it will work, tested for shs-8.x-1.x-dev version.

DiegoUzc’s picture

DiegoUzc’s picture

Drupal 8 - Create SHS enabled widget programatically through Form API

For Drupal 8 with unlimited multi-value SHS field in Custom Form

buildForm() method in example of custom form

  public function buildForm(array $form, FormStateInterface $form_state) {
    $field_name = 'field_shs_field_name';
    $vid = 'voc_shs_taxonomy';
    $title_field = $this->t('SHS Field Name');
    $anyLabel = $this->t('- None -');
    $addNewLabel = $this->t('Add another item');
    $cardinality = -1;

    $terms =\Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree($vid);
    $term_data = array();
    $term_data['_none'] = $anyLabel;
    foreach ($terms as $term) {
      $term_name = ($term->parents[0]==0)? $term->name : '-' . $term->name;
      $term_data[$term->tid] = $term_name;
    }
		
    $form[$field_name]['#type'] = 'container';
		
    $settings_additional = [
      'force_deepest' => TRUE,
      'create_new_items' => FALSE,
      'create_new_levels' => FALSE,
      'display_node_count' => FALSE,
      'required' => FALSE,
      'multiple' => TRUE,
      'anyLabel' => $anyLabel,
      'anyValue' => '_none',
      'addNewLabel' => $addNewLabel,
    ];	
		
    $settings_shs = [
      'settings' => $settings_additional,
      'bundle' => $vid,
      'baseUrl' => 'shs-term-data',
      'cardinality' => $cardinality,
      'parents' => [[['parent' => 0, 'defaultValue' => '_none']]],
      'defaultValue' => NULL,
      'labels' => [ $this->t('Level 1'), $this->t('Level 2'), ],
    ];
		
    $form[$field_name]['widget'] = [
      '#title' => $title_field,
      '#description' => "",
      '#field_parents' => [],
      '#required' => FALSE,
      '#delta' => 0,
      '#weight' => 0,
      '#element_validate' => [[
        'Drupal\shs\Plugin\Field\FieldWidget\OptionsShsWidget', 
        'validateElement',
      ]],
      '#key_column' => 'target_id',
      '#type' => 'select',
      '#options' => $term_data,
      '#default_value' => [],
      '#multiple' => TRUE,
      '#shs' => $settings_shs,
      '#attributes' => [ 'class' => ['shs-enabled'] ],
      '#attached' => [ 'library' => ['shs/shs.form'] ],
      '#after_build' => [[
        'Drupal\shs\Plugin\Field\FieldWidget\OptionsShsWidget', 
        'afterBuild',
      ]],
      '#field_name' => $field_name,
      '#parents' => [$field_name],
      '#tree' => TRUE,
    ];

    $form['actions']['#type'] = 'actions';
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Save'),
      '#button_type' => 'primary',
    ];
    return $form;
  }

submitForm() method in example of custom form

  public function submitForm(array &$form, FormStateInterface $form_state) {
    $field_name = 'field_shs_field_name';
    $shs_values = $form_state->getValue($field_name);
    $tids = [];
    foreach($shs_values AS $val_i) {
      $tids[] = $val_i['target_id'];
    }
    $this->messenger()->addStatus($this->t('Your ID terms are: "@tids"', ['@tids' => implode(', ',$tids) ]));
  }

Tested and works in version 8.x-1.0-alpha4

gmangones’s picture

Hi, comment #8 and #11 combined work for my.

aadeshvermaster@gmail.com’s picture

Hi,
comment #8 and #11 combined work for me.
And i also checked #13, its also working if we need multiple values, only issue when we add more all values hide then show again.
Thanks