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 %}
Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

sea2709 created an issue. See original summary.

pdureau’s picture

Title: Drupal\Core\Render\Component\Exception\InvalidComponentException: [attributes] String value found, but an object is required in Drupal\Core\Theme\Component\ComponentValidator->validateProps() » [2.0.0-beta3] InvalidComponentException: [attributes] String value found, but an object is required in ComponentValidator->validateProps()
pdureau’s picture

Assigned: Unassigned » christian.wiedemann

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

christian.wiedemann’s picture

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

Or I am wrong?

christian.wiedemann’s picture

Status: Active » Postponed (maintainer needs more info)
pdureau’s picture

Category: Bug report » Support request

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?

pdureau’s picture

Assigned: christian.wiedemann » Unassigned
sea2709’s picture

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 checking at Drupal\Core\Theme\Component\ComponentValidator, the props variable has 2 properties variant and text
Debug screenshot on beta1

On beta-2, during the component validation checking at Drupal\Core\Theme\Component\ComponentValidator, the props variable has 3 properties variant, text and attributes
Debug screenshot on beta1
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

pdureau’s picture

You are getting an error which was "fixed" by #3470231: [2.0.0-beta2] Attributes normalization

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?

sea2709’s picture

Status: Postponed (maintainer needs more info) » Closed (works as designed)

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 :-)

smustgrave’s picture

Version: 2.0.0-beta2 » 2.0.x-dev
Category: Support request » Bug report
Status: Closed (works as designed) » Active
StatusFileSize
new120.83 KB

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

error

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

pdureau’s picture

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

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
smustgrave’s picture

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

smustgrave’s picture

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.

just_like_good_vibes’s picture

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.

sea2709’s picture

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!

pdureau’s picture

Title: [2.0.0-beta4] InvalidComponentException: [attributes] String value found, but an object is required in ComponentValidator->validateProps() » [2.0.0-beta5] InvalidComponentException: [attributes] String value found, but an object is required in ComponentValidator->validateProps()
Category: Bug report » Support request
Status: Active » Postponed

Let's discuss this after beta4.

pdureau’s picture

Title: [2.0.0-beta5] InvalidComponentException: [attributes] String value found, but an object is required in ComponentValidator->validateProps() » [2.0.0-beta4] InvalidComponentException: [attributes] String value found, but an object is required in ComponentValidator->validateProps()
Assigned: Unassigned » pdureau
Category: Support request » Task
Status: Postponed » Needs work

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)

pdureau’s picture

Title: [2.0.0-beta4] InvalidComponentException: [attributes] String value found, but an object is required in ComponentValidator->validateProps() » [2.0.0-beta4] Normalize attributes values.
Assigned: pdureau » Unassigned
Status: Needs work » Needs review

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.

pdureau’s picture

Status: Needs review » Needs work
pdureau’s picture

Assigned: Unassigned » pdureau
pdureau’s picture

pdureau’s picture

Assigned: pdureau » just_like_good_vibes
Status: Needs work » Needs review
just_like_good_vibes’s picture

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 ?

just_like_good_vibes’s picture

Assigned: just_like_good_vibes » pdureau
Status: Needs review » Needs work
pdureau’s picture

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.

sea2709’s picture

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

pdureau’s picture

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 :)

sea2709’s picture

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
UI Suite DaisyUI 4.0.0-alpha2

In order to reproduce the bug, 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

pdureau’s picture

Assigned: pdureau » just_like_good_vibes
Status: Needs work » Needs review
smustgrave’s picture

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

just_like_good_vibes’s picture

Assigned: just_like_good_vibes » Unassigned
Status: Needs review » Fixed
sea2709’s picture

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 about using component function instead of include and embed twig function?

pdureau’s picture

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:

sea2709’s picture

Thanks @pdureau for the updates.

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

pdureau’s picture

Status: Fixed » Closed (fixed)