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.

Comments

BR0kEN created an issue. See original summary.

br0ken’s picture

Status: Active » Needs review
StatusFileSize
new4.25 KB

Tests to expose the problem.

br0ken’s picture

StatusFileSize
new5.08 KB
new735 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
StatusFileSize
new6.95 KB
new5.45 KB

Another try.

br0ken’s picture

Assigned: br0ken » Unassigned
br0ken’s picture

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

StatusFileSize
new6.84 KB
new817 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

StatusFileSize
new3.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
StatusFileSize
new6.41 KB
new1.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.