The trimmed version (crucial for this issue) of \Drupal\Core\Render\Element\Weight::processWeight() looks the following:

    if ($element['#delta'] <= $max_elements) {
      $element['#type'] = 'select';
      $element += $element_info_manager->getInfo('select');
    }
    else {
      $element['#type'] = 'number';
      $element += $element_info_manager->getInfo('number');
    }

Now imagine the case when $element['#delta'] exceeds system.site.weight_select_max so the weight element gets transformed to input[type=number] instead of select. The important part of that transformation is \Drupal\Core\Render\Element\Number::preRenderNumber(), that is declared as one of the #pre_render callbacks for number.

However, the next hook may prevent input[type=number] from being rendered at all and users will see undefined inputs without any attributes (i.e. <input />):

function multiversion_element_info_alter(array &$types) {
  foreach ($types as &$type) {
    if (!isset($type['#pre_render'])) {
      $type['#pre_render'] = [];
    }
    $type['#pre_render'][] = 'multiversion_element_pre_render';
  }
}

* The code above has actually been taken from the multiversion module.

The described behavior is caused by the array concatenation ($element += $element_info_manager->getInfo('number');) that adds only the values the keys of which are missing in the initial array.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

BR0kEN created an issue. See original summary.

BR0kEN’s picture

Status: Active » Needs review
FileSize
4.25 KB

Tests to expose the problem.

BR0kEN’s picture

FileSize
5.08 KB
735 bytes

The attempt to adjust the behavior to normal.

The last submitted patch, 2: 2979044-2.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 3: 2979044-3.patch, failed testing. View results

BR0kEN’s picture

Status: Needs work » Needs review
FileSize
6.95 KB
5.45 KB

Another try.

BR0kEN’s picture

Assigned: BR0kEN » Unassigned
BR0kEN’s picture

Assigned: Unassigned » BR0kEN
Issue tags: +DevDaysLisbon
BR0kEN’s picture

FileSize
6.84 KB
817 bytes
mitsuroseba’s picture

+++ b/core/lib/Drupal/Core/Render/Element/Weight.php
@@ -32,29 +32,32 @@ class Weight extends FormElement {
+    $element = array_merge($element, \Drupal::service('element_info')->getInfo($type));
+    $element['#type'] = $type;

Is it a good idea to use only one condition, and move those lines after it
Like
if ($element['#delta'] <= $max_elements) {
...
}
else {...}
then
$element = array_merge($element, \Drupal::service('element_info')->getInfo($type));
$element['#type'] = $type;

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

BR0kEN’s picture

FileSize
3.67 KB

Here is the patch for Drupal 8.4.x (without tests). Might be needed for some projects.

l0ke’s picture

Status: Needs review » Reviewed & tested by the community

#10 is more of a style question.
I opt for #9 to be RTBC, it is a good and pretty solution of the problem. Thanks @BR0kEN!

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/lib/Drupal/Core/Render/Element/Weight.php
    @@ -32,29 +32,32 @@ class Weight extends FormElement {
    -    $class = get_class($this);
    ...
    -        [$class, 'processWeight'],
    -        [$class, 'processAjaxForm'],
    +        [static::class, 'processWeight'],
    +        [static::class, 'processAjaxForm'],
    

    Is this change necessary? Hopefully not because a lot of render items do this. Well it seems it might be - https://3v4l.org/JKNf8 - but this is a separate issue no? Not actually necessary to fix the bug here is it?

  2. +++ b/core/lib/Drupal/Core/Render/Element/Weight.php
    @@ -32,29 +32,32 @@ class Weight extends FormElement {
    +    $type = $element['#delta'] <= \Drupal::config('system.site')->get('weight_select_max')
    +      // If the number of options is small enough, use a select field.
    +      ? 'select'
    +      // Otherwise, use a text field.
    +      : 'number';
    

    Placing this on multiple lines is very confusing for a ternary. If we're using a ternary I think it should be on one line. The comments can go on the line before.

  3. Also from reading the issue summary this fix might boil down to:
    diff --git a/core/lib/Drupal/Core/Render/Element/Weight.php b/core/lib/Drupal/Core/Render/Element/Weight.php
    index 649de315fd..3f67eae93e 100644
    --- a/core/lib/Drupal/Core/Render/Element/Weight.php
    +++ b/core/lib/Drupal/Core/Render/Element/Weight.php
    @@ -65,14 +65,14 @@ public static function processWeight(&$element, FormStateInterface $form_state,
             ksort($weights);
           }
           $element['#options'] = $weights;
    -      $element += $element_info_manager->getInfo('select');
    +      $element = array_merge($element, $element_info_manager->getInfo('select'));
         }
         // Otherwise, use a text field.
         else {
           $element['#type'] = 'number';
           // Use a field big enough to fit most weights.
           $element['#size'] = 10;
    -      $element += $element_info_manager->getInfo('number');
    +      $element = array_merge($element, $element_info_manager->getInfo('number'));
         }
     
         return $element;
    

    If that's the case can we limit the change to that because it makes it way easier to review.

BR0kEN’s picture

Status: Needs work » Needs review
FileSize
6.41 KB
1.85 KB

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

l0ke’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: +DevDaysTransylvania, +DevDaysCluj

I think all points from #14 have been addressed.

rosinegrean’s picture

Issue tags: -DevDaysCluj
alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed and pushed d90d0ebbda to 8.8.x and d77f9a680e to 8.7.x. Thanks!

diff --git a/core/lib/Drupal/Core/Render/Element/Weight.php b/core/lib/Drupal/Core/Render/Element/Weight.php
index e04d44903b..029c9e4a49 100644
--- a/core/lib/Drupal/Core/Render/Element/Weight.php
+++ b/core/lib/Drupal/Core/Render/Element/Weight.php
@@ -48,11 +48,9 @@ public function getInfo() {
    * Expands a weight element into a select/number element.
    */
   public static function processWeight(&$element, FormStateInterface $form_state, &$complete_form) {
-    // If the number of options is small enough, use a select field.
-    // Otherwise, use a text field.
+    // If the number of options is small enough, use a select field. Otherwise,
+    // use a number field.
     $type = $element['#delta'] <= \Drupal::config('system.site')->get('weight_select_max') ? 'select' : 'number';
-    // Merge arrays in order to ensure the keys will be overridden. The
-    // concatenation is prohibited.
     $element = array_merge($element, \Drupal::service('element_info')->getInfo($type));
     $element['#is_weight'] = TRUE;
 
@@ -68,7 +66,6 @@ public static function processWeight(&$element, FormStateInterface $form_state,
       }
       $element['#options'] = $weights;
     }
-    // Otherwise, use a text field.
     else {
       // Use a field big enough to fit most weights.
       $element['#size'] = 10;
diff --git a/core/tests/Drupal/KernelTests/Core/Render/Element/WeightTest.php b/core/tests/Drupal/KernelTests/Core/Render/Element/WeightTest.php
index bd00173278..b44b98cf12 100644
--- a/core/tests/Drupal/KernelTests/Core/Render/Element/WeightTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Render/Element/WeightTest.php
@@ -116,7 +116,7 @@ public function testProcessWeightSelectMax() {
       ],
       '#pre_render' => [
         [Number::class, 'preRenderNumber'],
-        // Our custom callback is just appended.
+        // The custom callback is appended.
         /* @see element_info_test_element_info_alter() */
         'element_info_test_element_pre_render',
       ],

Fixed up some comments on commit.

  • alexpott committed d90d0eb on 8.8.x
    Issue #2979044 by BR0kEN, alexpott: Unable to change menu items weight...

  • alexpott committed d77f9a6 on 8.7.x
    Issue #2979044 by BR0kEN, alexpott: Unable to change menu items weight...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.