Overview

In #3467954: META: Evolve XB UI's data model to allow non-SDC components' inputs, DynamicPropSource support, etc. we've identified that we need to keep the raw model values as well as the hydrated values.

#3499550: Support server-side massaging and validating of ComponentInputsForm values implemented this, but named "raw" source and "hydrated" resolved.

  • The hydrated resolved values are what end up in the UI for rendering the component.
  • The raw source values are what end up in the component tree field item.

E.g. for a media image reference that might be

[
            'alt' => 'This is a random image.',
            'width' => 100,
            'height' => 100,
            'target_id' => (int) $this->mediaEntity->id(),
]

But the hydrated resolved value the page builder needs to show the preview is

[
            'src' => '/path/to/some/image.webp',
            'alt' => 'This is a random image.',
            'width' => 100,
            'height' => 100,
],

Proposed resolution

  1. Split the component model values into source and resolved. Already done in #3499550: Support server-side massaging and validating of ComponentInputsForm values. Does not apply to the block ComponentSource plugin. See BlockComponent::inputToClientModel() + GeneratedFieldExplicitInputUxComponentSourceBase::inputToClientModel().
  2. Apply the same split to the default_values that GeneratedFieldExplicitInputUxComponentSourceBase::getClientSideInfo() provides for the sdc and js ComponentSource plugins, used to create new component instances.
  3. This will allow removing \Drupal\experience_builder\Plugin\ExperienceBuilder\ComponentSource\JsComponent::rewriteExampleUrl(), which is a work-around that #3507929: Allow adding "image" props in the code component editor introduced because this issue was not yet solved:
      public function rewriteExampleUrl(string $url): string {
        // @todo Remove this method/interface implementation in https://www.drupal.org/project/experience_builder/issues/3493943, which will make it obsolete.
        return $url;
      }
    

User interface changes

None.

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

larowlan created an issue. See original summary.

wim leers’s picture

Split the component model values into raw and resolved.

To make sure I don't misunderstand: you're saying that this is necessary on the client-side, i.e. in the UI's data model?

wim leers’s picture

Assigned: Unassigned » larowlan
Status: Active » Postponed (maintainer needs more info)
Issue tags: +Needs issue summary update
Related issues: +#3492511: Move form state into the global store

Also, AFAICT this relates to #3492511: Move form state into the global store too? Is that a third kind of value?

… or am I misinterpreting things now? 😅

larowlan’s picture

Status: Postponed (maintainer needs more info) » Active

Yes, client side - we post a model to the preview and form endpoints and there's some hard-coded stuff happening with e.g. images to flip them back from a file URL to a target ID. Look at \Drupal\experience_builder\Controller\ClientServerConversionTrait::findTargetForProps for example - that can only go away if the client-side model stores a reference to

[
            'alt' => 'This is a random image.',
            'width' => 100,
            'height' => 100,
            'target_id' => (int) $this->mediaEntity->id(),
]

As well as

[
            'src' => '/path/to/some/image.webp',
            'alt' => 'This is a random image.',
            'width' => 100,
            'height' => 100,
],

In terms of the form state, that is only a global place to store the current form values before we commit them to the model. Previously it was done via a context provider but that didn't work for ajax form additions - the global state does

wim leers’s picture

Priority: Normal » Major
Issue tags: +clean-up, +DX (Developer Experience)

I believe this change is critical for XB in the future, because it'll make a lot of magic (see the method mentioned by Lee) explicit. Bumping priority.

effulgentsia’s picture

#3499279: Make link widget autocomplete work (for uri and uri-reference props) could also be a good example for verifying what gets implemented here.

larowlan’s picture

Title: Split model values into resolved and raw » [PP-2] Split default values into resolved and raw
Issue summary: View changes
Status: Active » Postponed

This is blocked on #3493941: Maintain a per-component set of prop expressions/sources which is also blocked, updating status.

Updating the title to reflect this is about default values now as #3493941: Maintain a per-component set of prop expressions/sources includes values in the prop source data.

Once that is in, we will need default values to cover both. e.g. If we've got an image reference, the default value of the hydrated props might be a URL, alt tag and width and height, but the stored value would be the media or file ID.

wim leers’s picture

Title: [PP-2] Split default values into resolved and raw » [PP-1] Split default values into resolved and raw
wim leers’s picture

Currently reviewing #3493941, and now #7's second paragraph makes sense to me: that part of this MR's scope was merged into #3493941. 👍

wim leers’s picture

Title: [PP-1] Split default values into resolved and raw » Split default values into resolved and raw
Status: Postponed » Active
wim leers’s picture

wim leers’s picture

Assigned: larowlan » Unassigned

@larowlan is out this week 🏝️

wim leers’s picture

Assigned: Unassigned » larowlan
wim leers’s picture

wim leers’s picture

wim leers’s picture

Issue summary: View changes
wim leers’s picture

Component: Page builder » Data model
Issue tags: -clean-up, -DX (Developer Experience) +sprint, +blocker

This blocks #3487284. See #3487284-12.

longwave’s picture

Been thinking about this on and off for a while now. I agree that we need the raw values available to the client side UI, because in the case of

[
            'alt' => 'This is a random image.',
            'width' => 100,
            'height' => 100,
            'target_id' => (int) $this->mediaEntity->id(),
]

we need to send and receive the target_id for the media library form. This is the root cause of #3511451: Cannot publish image or save as section in a particular component - see Felix's investigation in #7.

Do we only need resolved values for component preview and final render? I have concerns that if we are storing/sending both raw and resolved then there is a chance of them getting out of sync - either accidentally, or deliberately if someone is trying to mess with the values, which might have security implications.

So in the layout API we would only send the layout + raw values to the client, the client stores and sends raw values back for forms (which is all we need anyway) and previews (which we translate at preview time into the resolved values). If this is correct, where do we need resolved values in the client side model?

larowlan’s picture

If we can get rid of findTargetForProps then I'm open to all ideas

wim leers’s picture

#21: right, but as you already wrote in #4 >3 months ago and again confirmed in #7, that's exactly what this issue will make possible.

Let's get this done ASAP, this would've prevented me having spent hours on #3508077: Default image for SDC with optional image is lost after changing value for another SDC prop today. 😬

wim leers’s picture

As described in #3508077-28: Default image for SDC with optional image is lost after changing value for another SDC prop: there's a front-end bug lurking that causes some props' sources to not be passed from the client to the server, even though they should be set to the default as returned by /xb/api/config/component: https://git.drupalcode.org/project/experience_builder/-/merge_requests/7...

larowlan’s picture

Re #22 sorry, will focus on this today

effulgentsia’s picture

Do we only need resolved values for component preview and final render? I have concerns that if we are storing/sending both raw and resolved then there is a chance of them getting out of sync

I agree. Ideally only the raw values would need to be passed from the client to the server. As to where within the React code the resolved values are needed at all, as well as whether or not it's needed for resolved values (in addition to raw values) to be passed from server to client, I opened #3514910: Refinements of real-time preview for props changes of JS components. That issue should be relatively straightforward to do, and I wonder if it would help with this issue's implementation to do that one first, to make sure resolved values are being used somewhere within the React code, even if not as part of the HTTP API.

wim leers’s picture

Title: Split default values into resolved and raw » SDC+JS Component Sources: split default values into `resolved` and `source`
Component: Data model » Component sources

First: please check #3499550: Support server-side massaging and validating of ComponentInputsForm values for why @larowlan introduced source in addition to the pre-existing resolved. @longwave in #20's comment seems to suggest XB stores both, but that's not the case! We only store the source in the DB — the resolved really just dates back to the super simple client-side data model we had until before that issue.


I don't see how #3514910: Refinements of real-time preview for props changes of JS components would A) help this issue, B) be relatively straightforward?

Yes, the resolved value is currently not yet actively used on the client-side, and we could in theory remove it. But there's little value in doing that, because #3453690: [META] Real-time preview: supporting back-end infrastructure will need both resolved and source. Yes, #3514910: Refinements of real-time preview for props changes of JS components would/could/should be the first thing to actually use resolved for client-side functionality rather than sending it back to the server! 👍

But #3514910: Refinements of real-time preview for props changes of JS components kinda can't be straightforward — you already mentioned the auto-save implications, I just added another: undo/redo → #3514910-2: Refinements of real-time preview for props changes of JS components.

wim leers’s picture

Issue summary: View changes

Overhauled issue summary to reflect the names that have been chosen in #3499550: Support server-side massaging and validating of ComponentInputsForm values, and what the remaining work here is.

effulgentsia’s picture

I don't see how #3514910: Real-time preview for props changes of JS components would A) help this issue...Yes, the resolved value is currently not yet actively used on the client-side, and we could in theory remove it.

My thinking was that the help it would offer this issue is by having an implemented use case (with tests) of the resolved values being used. So that, a) it prevents them from being removed, and b) it allows refactoring of where they're kept if such refactoring is desired per #20 while continuing to ensure that such refactoring is compatible with the use-case they need to serve.

But I'm totally okay with this issue being worked on first and not postponing it on #3514910: Refinements of real-time preview for props changes of JS components.

larowlan’s picture

Assigned: larowlan » wim leers
Status: Active » Needs review

Annotated the MR

Two open threads, one to file a dedicated issue for the data corruption issue and another to review the use of empty

larowlan’s picture

The only open thread is to open a dedicated issue, this is green and ready for review

larowlan’s picture

longwave’s picture

Status: Needs review » Reviewed & tested by the community

One nit with test formatting but otherwise no notes, this looks great - good find on that stored data vs later shape change bug, thanks also for the detailed self-review.

wim leers’s picture

🥳 Reviewing!

larowlan’s picture

Status: Reviewed & tested by the community » Needs review
longwave’s picture

Status: Needs review » Reviewed & tested by the community

Feedback addressed, added another followup as per discussion, back to RTBC.

Waiting on this to land before I try to move the other image issues forward because this is intertwined with some of them.

longwave’s picture

Status: Reviewed & tested by the community » Needs work

Found a bug when testing this with xb-demo, see MR.

However the server side performance issues with xb-demo are just gone with this patch, the 70 calls to GeneratedFieldExplicitInputUxComponentSourceBase::getClientSideInfo() now take 92ms down from 21,158ms - a 230x speed improvement :D

larowlan’s picture

Status: Needs work » Needs review

Fixed the issue spotted by @longwave

larowlan’s picture

Addressed all feedback, thanks for the review

longwave’s picture

Status: Needs review » Reviewed & tested by the community

All points addressed, no further feedback - over to Wim for hopefully the final review.

wim leers’s picture

Assigned: wim leers » Unassigned

We fixed a complex set of problems by net-deleting code: MR diffstat is +208, −325 🥳😄

Plus, now section 3.2.1 Source vs resolved input in docs/redux-integrated-field-widgets.md is actually completely accurate and doesn't even need to spell out that the same is true for the default values — that just makes sense, it's the situation before this MR that didn't make sense! (Well, just was unfinished/inconsistent.)

Phenomenal work here! 🤩

wim leers’s picture

Status: Reviewed & tested by the community » Fixed

  • wim leers committed 3535fb59 on 0.x authored by longwave
    Issue #3493943 by larowlan, wim leers, longwave: SDC+JS Component...
wim leers’s picture

The regression I reported in #46 makes it so that I shouldn't tag another release until that's fixed; it's likely too disruptive.

I should've done manual testing, if I'd done that, I'd have caught this. I was too eager to land this 🙈

nagwani’s picture

Issue tags: -sprint

Status: Fixed » Closed (fixed)

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