[2.0.0-beta3] InvalidComponentException: [attributes] String value found, but an object is required in ComponentValidator->validateProps()

Created on 17 September 2024, 7 months ago
Updated 21 September 2024, 7 months ago

Problem/Motivation

Hello team, I encountered this error after upgrading from beta1 -> beta2

Drupal\Core\Render\Component\Exception\InvalidComponentException: [attributes] String value found, but an object is required in Drupal\Core\Theme\Component\ComponentValidator->validateProps() (line 203 of core/lib/Drupal/Core/Theme/Component/ComponentValidator.php).
Drupal\Core\Template\ComponentsTwigExtension->doValidateProps(Array, 'careerforce_skin:sdc-button') (Line: 109)
Drupal\Core\Template\ComponentsTwigExtension->validateProps(Array, 'careerforce_skin:sdc-button') (Line: 43)
__TwigTemplate_2e7947f54dec3d43cd615960f13e0186->doDisplay(Array, Array) (Line: 360)
Twig\Template->yield(Array, Array) (Line: 174)
__TwigTemplate_2963595ff175389c52b90bc1b95d0f52___1421564442->doDisplay(Array, Array) (Line: 360)
Twig\Template->yield(Array) (Line: 56)
__TwigTemplate_2963595ff175389c52b90bc1b95d0f52->doDisplay(Array, Array) (Line: 360)
Twig\Template->yield(Array) (Line: 335)
Twig\Template->render(Array) (Line: 38)
Twig\TemplateWrapper->render(Array) (Line: 33)
twig_render_template('themes/custom/careerforce_skin/templates/form/input--submit.html.twig', Array) (Line: 348)

I created a SDC button with this YML

name: Careerforce_Skin - SDC Button
description: A button draws attention to important actions with a large selectable surface.
variants:
  default:
    title: Default
  secondary:
    title: Secondary
  inverse:
    title: Inverse
slots:
  children:
    title: Children
    description: 'The button children.'
props:
  type: object
  properties:
    text:
      title: Text
      type: string
      default: Read more
    url:
      title: URL
      type: string
      description: 'The button URL. Optional.'
      default: https://www.govwebworks.com/
      $ref: 'ui-patterns://url'

This is my input__submit preprocess

function careerforce_skin_preprocess_input__submit(&$variables) {
  if (isset($variables['element']['#is_secondary_button'])) {
    $variables['is_secondary_button'] = $variables['element']['#is_secondary_button'];
  }
  if (isset($variables['element']['#is_inverse_button'])) {
    $variables['is_inverse_button'] = $variables['element']['#is_inverse_button'];
  }
}

And the input--submit.html.twig

{% set variant = 'default' %}
{% if is_secondary_button %}
  {% set variant = 'secondary' %}
{% elseif is_inverse_button %}
  {% set variant = 'inverse' %}
{% endif %}

{% embed 'careerforce_skin:sdc-button' with { text: attributes.value|raw, variant: variant} %}
  {% block children %}
    {{ children }}
  {% endblock %}
{% endembed %}
πŸ’¬ Support request
Status

Postponed: needs info

Version

2.0

Component

Code

Created by

πŸ‡ΊπŸ‡ΈUnited States sea2709 Texas

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @sea2709
  • πŸ‡«πŸ‡·France pdureau Paris
  • Assigned to Christian.wiedemann
  • πŸ‡«πŸ‡·France pdureau Paris
  • πŸ‡©πŸ‡ͺGermany Christian.wiedemann

    christian.wiedemann β†’ made their first commit to this issue’s fork.

  • πŸ‡©πŸ‡ͺGermany Christian.wiedemann

    This is a core issue. The component is rendered inside a form field which is not mapped by ui patterns.

    Or I am wrong?

  • Status changed to Postponed: needs info 7 months ago
  • πŸ‡«πŸ‡·France pdureau Paris

    Indeed, it doesn't look like an UI Patterns issue, because this snippet uses stuff we are trying to avoid with UI Patterns 2:

    {% embed 'careerforce_skin:sdc-button' with { text: attributes.value|raw, variant: variant} %}
      {% block children %}
        {{ children }}
      {% endblock %}
    {% endembed %}
    

    So this may be a "support request".

    What are the careerforce_skin:sdc-button component definition & template?

  • Issue was unassigned.
  • πŸ‡«πŸ‡·France pdureau Paris
  • πŸ‡ΊπŸ‡ΈUnited States sea2709 Texas

    Thanks for your response. Not sure if my implementation is correct. The idea is I would like the submit buttons are rendered by using a button SDC component, so that's why I created a twig template file input__submit.html.twig and rendered the button component here.

    This piece of code went well on UI Patterns 2.0.0-beta1, I only encounter the invalid component exception after I upgraded to beta2.

    {% embed 'careerforce_skin:sdc-button' with { text: attributes.value|raw, variant: variant} %}
      {% block children %}
        {{ children }}
      {% endblock %}
    {% endembed %}
    

    I did some debugging and this is what I found

    On beta-1, during the component validation process, the props variable has 2 properties variant and text

    On beta-2, during the component validation process, the props variable has 3 properties variant, text and attributes

    I think attributes should be an object or an array, not a string. I guess maybe somewhere on UI Patterns 2-beta2, the attributes prop is converted to a string

  • πŸ‡«πŸ‡·France pdureau Paris

    You are getting an error which was "fixed" by πŸ“Œ [2.0.0-beta2] Attributes normalization Fixed

    The root cause is triggered SDC's ComponentValidator::validateProps() which is calling a dependency method which is executing json_encode() which is casting the attribute object into a string.

    So, this issue is fixed with a SDC render element alteration by UI Patterns 2, but you are still getting it. Why?

    Maybe because you are using {% embed 'careerforce_skin:sdc-button' with { text: attributes.value|raw, variant: variant} %} which is not triggering the SDC render element.

    In this order:

    1. Because you are not passing attributes, you can start by adding the only keyword. Non tested proposal: {% embed 'careerforce_skin:sdc-button' with { text: attributes.value|raw, variant: variant} only %} . See: https://twig.symfony.com/doc/3.x/tags/embed.html

    2. If you still have the issue or if you want to pass attributes to the component, make it explicit and convert to array. Non tested proposal: {% embed 'careerforce_skin:sdc-button' with { attributes: attributes.toArray(), text: attributes.value|raw, variant: variant} only %}

    3. If you still have the issue, replace Twig embed and block by a component() function. Non tested proposal:

    {{ component( 'careerforce_skin:sdc-button', 
       {
         text: attributes.value|raw,
         variant: variant
       },
      {
        children: children
      }
    ) }}
    

    Is it helping?

  • πŸ‡ΊπŸ‡ΈUnited States sea2709 Texas

    Thanks @pdureau,

    It's helping! This code works for my case on beta2, with and without only!

    {% embed 'careerforce_skin:sdc-button' with { attributes: attributes.toArray(), text: attributes.value|raw, variant: variant} only %}

    Thank you for your help! I guess I need some time to digest your response, I'm a little bit exhausted at this moment, but I'm glad that it works :-)

  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    So I'm reopening this one as I'm noticing this when using patterns from a view

    Using ui_suite_uswds 4.0.x branch
    ui_patterns latest beta.
    For ease I duplicated the admin content view and removed the admin part from the new url so it uses the front end theme.
    ui_pattern-devel must be uninstalled.

    I now get a fatal error

    The error I believe is because the #value key is an attribute object now.

  • πŸ‡«πŸ‡·France pdureau Paris

    Let's target the next beta

    However, I am still not reproducing the issue.

    • ui_suite_uswds 4.0.x branch >> OK, i am at commit 544dfaa4704bf2b3dca5faeb794277777264dee1 (HEAD -> 4.0.x, origin/HEAD, origin/4.0.x)
    • duplicated the admin content view and removed the admin part from the new url so it uses the front end theme. >> OK, I have duplicate_of_content view with /content page display
    • ui_pattern-devel must be uninstalled >> OK, ui_pattern_devel is not installed

    Other information about my environment:

    • Drupal version : 11.0.4
    • PHP version : 8.3.12
    • DB driver : sqlite
    • Drush version : 13.2.0.0
    • Web server: PHP Built-in web server
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Will try and gather additional steps but it's reproducible for me 100% of the time.

    Though I believe the bug lies with core or views

  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Fresh install

    Drupal 11.0.5
    Drush 13
    Php 8.3
    Ui suite uswds 4.0.x
    Ui patterns beta3

    Install all needed files
    Leave ui patterns devel off

    Enable theme, using CDN to pull USWDS library in
    Go to admin/structure/views/view/content
    Duplicate the page
    Change URL from admin/content/node to content/node

    Need to have at least 1 piece of content

    Go to the page and see the errors.

  • πŸ‡«πŸ‡·France just_like_good_vibes PARIS

    hello,
    i am debugging your problem,
    first thing i found, in file form-element-label.html.twig,

     {%- set attributes = attributes.setAttribute('title', title) -%}
    

    the title is provoking a bug on my instance. if you replace "title" by "title|render", it works.

    Second thing i found, in file views-view-table.html.twig,
    line 140

    {% set prepared_columns = prepared_columns|merge([
          pattern('table_cell', {
            'attributes': column.attributes.addClass(column_classes),
            'content': {
              '#markup': column_content
            },
            'active': active
          })
        ]) %}
      {% endfor %}
    

    the attributes injected are containing (at twig render time), an invalida value at key "headers".
    add .removeAttribute('headers') at line 142, after .addClass(column_classes) and it will work.

  • πŸ‡ΊπŸ‡ΈUnited States sea2709 Texas

    Not sure if it's helpful. In my case, I noticed that the component SDC-button I created hasn't been processed by the function "processAttributesProp" in ComponentElementAlter.php , while the other components have. I only see only button component encounters this issue. In my case, I don't think it's because of views, will share more information when I figure out something!

  • πŸ‡«πŸ‡·France pdureau Paris

    Let's discuss this after beta4.

  • πŸ‡«πŸ‡·France pdureau Paris

    We will:

    • Merge LinksPropType::normalizeAttributes() in AttributePropType::normalize()
    • Call AttributePropType::normalize() from LinksPropType::normalize()
    • Add to AttributePropType::normalize() some logic about attribute value being list and not associative array
    • Add to AttributePropType::normalize() some logic about renderable interface & stringable interface inspired by SlotPropType::convertObject() (if possible, implement this in StringPropType:normalize() and call it)
  • πŸ‡«πŸ‡·France pdureau Paris

    So:

    DONE in commit b32351950f6:

    • Merge LinksPropType::normalizeAttributes() in AttributePropType::normalize() >>
    • Call AttributePropType::normalize() from LinksPropType::normalize()

    DONE in commit 4ed020f26eda:

    • Add to AttributePropType::normalize() some logic about attribute value being list and not associative array
    • Add to AttributePropType::normalize() some logic about renderable interface & stringable interface inspired by SlotPropType::convertObject()

    So, I hope we addressing the 2 issues met by Steven from this ticket:

    • the error because of the 3 levels attributes object structure (coming from Views I guess), it will be normalized according to Drupal attribute object normalization OR as a JSON value
    • the error because of a render array as attribute value, it will be normalize also be normalized as a JSON value (

    (JSON encoding is only for extreme use case, most iof the normalization do'nt rely on this workaround)

    About Dang Tran feedback:

    Not sure if it's helpful. In my case, I noticed that the component SDC-button I created hasn't been processed by the function "processAttributesProp" in ComponentElementAlter.php to convert the attributes object type '\Drupal\Core\Template\Attribute' into an array, while the other components have. I only see only button component encounters this issue. In my case, I don't think it's because of views, will share more information when I figure out something!

    Interesting, maybe the logic of ComponentElementAlter::processAttributesProp() can also move to AttributePropType::normalize().

    Anyway, I don't know what is happening with those {% embed %} thingy. Maybe we will need to revamp this hacky part of the SDC API next year, moving some logic from the TwigNodeVisitor to the Render Element.

  • πŸ‡«πŸ‡·France pdureau Paris
  • πŸ‡«πŸ‡·France pdureau Paris
  • πŸ‡«πŸ‡·France pdureau Paris
  • πŸ‡«πŸ‡·France pdureau Paris
  • πŸ‡«πŸ‡·France just_like_good_vibes PARIS

    super nice job ! i like the treatment attribute is receiving, because we know it will receives a lot of weird data over time,
    mainly calls from twig with variables having values not trivial.
    i just have a few remarks to help finish the attribute normalization/sanitization.

    when you normalize a list of values in normalizeList, only the case of an array is covered.
    i would add maybe two cases :
    - important : a is_object test inside this function to call normalizeObject
    - less important : when the value inside a list is an array, imagine this is render array. the caller intuitively would like it to be rendered. this may never happens :) why not try to trigger a rendering ?

    sometimes json_encode may produce unexpected results (memory error or ?), for example if there are objects inside the values we are trying to json_encode. This is maybe too much good attention and intentions to the persons which are adding bad values inside attributes.

    Why censuring the code to call "render" inside the normalizeMapping function ?

  • πŸ‡«πŸ‡·France pdureau Paris

    Why censuring the code to call "render" inside the normalizeMapping function ?

    Because I don't know how to test it in AttributesPropTypeNormalizationTest :)

    Sometimes json_encode may produce unexpected results (memory error or ?), for example if there are objects inside the values we are trying to json_encode. This is maybe too much good attention and intentions to the persons which are adding bad values inside attributes.

    Yes, I am nice guy here :) I add a depth limit?

    json_encode(mixed $value, int $flags = 0, int $depth = 512): string|false
    
    when you normalize a list of values in normalizeList, only the case of an array is covered.
    i would add maybe two cases :
    - important : a is_object test inside this function to call normalizeObject

    Great idea.

    - less important : when the value inside a list is an array, imagine this is render array. the caller intuitively would like it to be rendered. this may never happens :) why not try to trigger a rendering ?

    So, related to my issue with AttributesPropTypeNormalizationTest. I don't know how to mock a service.

  • πŸ‡ΊπŸ‡ΈUnited States sea2709 Texas

    Looks like the error is still presented on my local. I'll have sometime tomorrow, I'll dig into it!

  • πŸ‡«πŸ‡·France pdureau Paris
    Sometimes json_encode may produce unexpected results (memory error or ?), for example if there are objects inside the values we are trying to json_encode. This is maybe too much good attention and intentions to the persons which are adding bad values inside attributes.

    βœ… Depth limit added.

    when you normalize a list of values in normalizeList, only the case of an array is covered.
    i would add maybe two cases :
    - important : a is_object test inside this function to call normalizeObject

    βœ… Test added.

    Why censuring the code to call "render" inside the normalizeMapping function ?

    - less important : when the value inside a list is an array, imagine this is render array. the caller intuitively would like it to be rendered. this may never happens :) why not try to trigger a rendering ?

    Because I don't know how to test it in AttributesPropTypeNormalizationTest :)

  • πŸ‡ΊπŸ‡ΈUnited States sea2709 Texas

    I spent some time to debug the issue, and was able to reproduce this bug on a fresh D11 install.

    Drupal 11
    UI Patterns 2.0.x-dev
    Uninstall UI Patterns Devel off
    UI Suite DaisyUI 4.0.0-alpha2

    I updated the template file web/themes/contrib/ui_suite_daisyui/templates/menu/menu--account.html.twig as

    {% for item in items %}
      {{ include('ui_suite_daisyui:button', {
        label: item.title,
        url: item.url.toString()
      }, with_context = false) }}
    {% endfor %}
    

    I see that when I render a component in a twig template file, that component doesn't go through the #pre_render process, so attributes prop isn't processed. As a result, the component validation is failed since the attribute prop is a string, but in the schema, attribute prop is an object with the prop type as ui-patterns://attributes

  • πŸ‡«πŸ‡·France pdureau Paris
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    So I switch to this branch on 4.0.x branch of USWDS same scenario in #15 and no fatal error!

  • πŸ‡ΊπŸ‡ΈUnited States sea2709 Texas

    Looks like if I use include function in twig file, I still run into a fatal error. I saw your comment on https://www.drupal.org/project/ui_patterns/issues/3481860 πŸ’¬ Components require attributes prop defined Active about using component function instead of include and embed twig function?

  • πŸ‡«πŸ‡·France pdureau Paris

    Twig's include & embed are not currently a good way to embed components, because they don't trigger the SDC render element. Passing through the render element is an important par of the rendering pipeline.

    So, we have 2 possibilities:

  • πŸ‡ΊπŸ‡ΈUnited States sea2709 Texas

    Thanks @pdureau for the updates.

    Look forward to errors free when using functions from SDC core! In the meantime, I will use component function :-)

  • πŸ‡«πŸ‡·France pdureau Paris
Production build 0.71.5 2024