phphtmldrupaldrupal-8drupal-fapi

Disable Arrows on input type="number" without javascript


<input type="number"> fields have show up/down arrows (increment/decrement), how do I remove these (in Drupal 8) without using javascript?

A client asked to remove the up/down arrows to prevent accidental changing of values. I've tried adding a custom fieldwidget which uses $element['#type'] = 'textfield';.

This leads to errors when changing values because Drupal expects a integer but a string is provided by the custom fieldwidget.

So I'm looking for a way (probably a hook_form_alter) to change the display (removing the up/down arrows) while not changing the output (int vs string).

Below is the code of my custom fieldwidget.

<?php

namespace Drupal\fieldwidgets\Plugin\Field\FieldWidget;

use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\Plugin\Field\FieldWidget\StringTextareaWidget;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\Validator\ConstraintViolationInterface;

/**
 * Plugin implementation of the 'number No Increment' widget.
 *
 * @FieldWidget(
 *   id = "number_no_increment",
 *   label = @Translation("Number without -+ buttons"),
 *   field_types = {
 *     "decimal",
 *     "integer",
 *   }
 * )
 */
class NumberNoIncrementWidget extends StringTextareaWidget {

  /**
   * {@inheritdoc}
   */
  public function settingsForm(array $form, FormStateInterface $form_state) {
    $element = parent::settingsForm($form, $form_state);
    $element['rows']['#description'] = $this->t('Number widget without increment buttons.');

    return $element;
  }

  /**
   * {@inheritdoc}
   */
  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    $main_widget = parent::formElement($items, $delta, $element, $form, $form_state);
    $element = $main_widget['value'];
    $element['#default_value'] = (isset($items[$delta]->value) && isset($countries[$items[$delta]->value])) ? $items[$delta]->value : NULL;
    $element['#type'] = 'textfield';
    $element['#format'] = $items[$delta]->format;
    $element['#base_type'] = $main_widget['value']['#type'];

    return $element;
  }

  /**
   * {@inheritdoc}
   */
  public function errorElement(array $element, ConstraintViolationInterface $violation, array $form, FormStateInterface $form_state) {
    if ($violation->arrayPropertyPath == ['format'] && isset($element['format']['#access']) && !$element['format']['#access']) {
      // Ignore validation errors for formats if formats may not be changed.
      return FALSE;
    }
    return $element;
  }

}

Solution

  • You want to extend StringTextfieldWidget class, not StringTextareaWidget.

    Then you need a validation method :

    public static function validate($element, FormStateInterface $form_state) {
      $value = $element['#value'];
      if (!is_numeric($value)) {
        $form_state->setError($element, t('%field should be a number.', ['%field' => $element['#title']]));
      }
    }
    

    Reference this validator in the fomElement method :

    '#element_validate' => [
      [static::class, 'validate'],
    ]
    

    Also, if you have to store an integer (for example, I mean not a varchar) in database you will need to cast the value in the validation callback since is_numeric() allows numeric strings.