-
-
Notifications
You must be signed in to change notification settings - Fork 356
[TwigComponent] merge props from template with class props #1652
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[TwigComponent] merge props from template with class props #1652
Conversation
You did not answer my comment: #1387 (comment) On my test it worked well ... could you update your message to show a concrete example of something that did not work before and will with your PR ? |
Hey @smnandre your example works because you set a default value to your props, remove the default value and you should have an error |
Ok thanks, indeed it triggers an error. For me to better understand, could you also explain me the code changes then ? I don't understand what's in stake in the merge array.. you're changing the order right ? |
It's only for props not defined in the php class, right ? |
// add attributes | ||
[$metadata->getAttributesVar() => $mounted->getAttributes()], | ||
$props, | ||
['__props' => $props] | ||
['__props' => array_merge($mounted->getInputProps(), $props)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
['__props' => array_merge($mounted->getInputProps(), $props)] | |
['__props' => $props], |
Line 120, you wrote
$props = array_merge($mounted->getInputProps(), ....)
So here it's as if you wrote
['__props' => array_merge($mounted->getInputProps(), $mounted->getInputProps(), ....
:)
// add attributes | ||
[$metadata->getAttributesVar() => $mounted->getAttributes()], | ||
$props, | ||
['__props' => $props] | ||
['__props' => array_merge($mounted->getInputProps(), $props)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changes a lot the content of $variables (even for component not concerned by this change)
Before:
"outerScope" => []
"this" => Symfony\UX\TwigComponent\Tests\Fixtures\Component\WithAttributes {#3194
+prop: "prop value 1"
}
"computed" => Symfony\UX\TwigComponent\ComputedPropertiesProxy {#3224
-cache: []
-component: Symfony\UX\TwigComponent\Tests\Fixtures\Component\WithAttributes {#3194}
}
"attributes" => Symfony\UX\TwigComponent\ComponentAttributes {#3222
-rendered: []
-attributes: array:4 [
"class" => "bar"
"style" => "color:red;"
"value" => ""
"autofocus" => true
]
}
"prop" => "prop value 1"
"__props" => array:1 [
"prop" => "prop value 1"
]
]
After
[
"outerScope" => []
"this" => Symfony\UX\TwigComponent\Tests\Fixtures\Component\WithAttributes {#3194
+prop: "prop value 1"
}
"computed" => Symfony\UX\TwigComponent\ComputedPropertiesProxy {#3224
-cache: []
-component: Symfony\UX\TwigComponent\Tests\Fixtures\Component\WithAttributes {#3194}
}
"attributes" => Symfony\UX\TwigComponent\ComponentAttributes {#3222
-rendered: []
-attributes: array:4 [
"class" => "bar"
"style" => "color:red;"
"value" => ""
"autofocus" => true
]
}
"prop" => "prop value 1"
"class" => "bar"
"style" => "color:red;"
"value" => ""
"autofocus" => true
"__props" => array:5 [
"prop" => "prop value 1"
"class" => "bar"
"style" => "color:red;"
"value" => ""
"autofocus" => true
]
]
I know tests are green, but that seems to me really strange and not needed
I made some tries, and there is a little case that make me 🤔 If your component class contains properties without default values, props from the container seems to override them. // in your component class
public string|null $nullableStringWithoutDefault;
public string|null $nullableStringDefaultNull = null;
public string $uninitializedString;
public $untypedPropDefaultNull;
public $untypedPropWithoutDefault; # in your component template
{% props
nullableStringWithoutDefault=123,
nullableStringDefaultNull=123,
uninitializedString=123,
untypedPropDefaultNull=123,
untypedPropWithoutDefault=123,
%}
<div ...>
<li>nullableStringDefaultNull: {{ nullableStringDefaultNull }}
<li>nullableStringDefaultFalse: {{ nullableStringDefaultFalse }}
<li>nullableStringWithoutDefault: {{ nullableStringWithoutDefault }}
<li>uninitializedString: {{ uninitializedString }}
<li>untypedPropDefaultNull: {{ untypedPropDefaultNull }}
<li>untypedPropWithoutDefault: {{ untypedPropWithoutDefault }}
</div> Renders <li>nullableStringWithoutDefault: 123
<li>nullableStringDefaultNull: 123
<li>uninitializedString: 123
<li>untypedPropDefaultNull: 123
<li>untypedPropWithoutDefault: 123
</div> We need to find a way to forbid this kind of override (like you did it seems for props with values). |
Last question, the DX could be improved i think in case of ignored props... Maybe we could throw an exception/error, as the developper may not understand why its props "does not work" ? WDYT ? Finally, did you test (even on a minimal case) if more complex situations are impacted ?
|
Yes, totally right
Hummmm, I am not sure about that I like the current behavior it sounds more obvious to me. But maybe we should trigger an error if props with the same name are defined in the class and in the template, WDYT ?
I gonna double check, but I am not sure if we can know if props come from the template or the class. |
Yes that's exactly what i want (and missexpressed apparently haha)... we cannot accept the template to change the strong type of a props :|
I'd really would like any change to be explicited and justified... because there is a big side effect there, and we should not accept things because we did not find "better" .. while creating major technical debt for the future 😅 How could i help you on this ? Distinct initial props and runtime args ? That's what you miss ? |
5d352b8
to
234a7dd
Compare
Hey @smnandre, I rework this PR. To avoid side effects I keep the __props variable with the same data. So props from the template are directly merged into the context of the component. To make it work I added __context variable that allows me to know what is the initial context of the component. |
After some thinking, I am not sure about that... I thing props from the template should be able to overwrite class props. I think the template should become the source of truth for the component. WDYT? |
I'm wondering if we're not creating some confusion between "props" (things that can be passed from the calling template) and their default values (when they are not passed from the calling template). Overriding a default value is one thing, and i have no strong opinion on this. But if you change the type of a props in the template (let say from bool to string)... the PHP class will try to mount a string.. when it's expecting a bool for example). So i suggest we write down what situation/use case we want to improve (and i'm 100% on the general "concept" here), while beeing attentive at the special cases we want to be very strict about. Lastly, i wonder if we should not extract those earlier (handle them in Factory/Renderer/etc...) to avoid any "conflict" situation -- A few (new) questions i'm thinking there:
(not all must be answered now... but i'd like if possible we have a vision on how this is gonna work in edge cases so all is clear for us, in the code, when we will be asked about it on slack, etc... 👼 ) WDYT ? (not sure if i'm being clear enough, please tell me if not ;) ) |
@smnandre the solution I choose here, is to throw an error where there are conflicts between the class props and the template props. This is a radical solution, but I think this is reducing the complexity a lot. Tell me what you think |
Really like this choice, allowing us to move this forward while not closing the door at future improvments. Do you want a last "proofread" on this PR ? |
Yes, please! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the patience / the discussions, i am really happy we can send this one :)
#webmanbaforever
c40abb1
to
345833d
Compare
Thank you Matheo. |
…context variables (squrious) This PR was merged into the 2.x branch. Discussion ---------- [TwigComponent] Allow input props to have the same name as context variables | Q | A | |--------------|-----| | Bug fix? | yes | | New feature? | no | | Issues | - | | License | MIT | It looks like #1652 introduced an issue with input props and parent context. If: - A component prop has the same name as a variable in the parent context - The context variable is used to set the prop value as an input prop - The component is rendered with the embed strategy Then, the default value of the prop is used instead of the one passed to the component. Example: ```twig {# templates/components/Hello.html.twig #} {% props name = 'John' %} <div {{ attributes }}> Hello {{ name }}! </div> ``` ### Render embedded ```twig {% set name = 'Bryan' %} <twig:Hello :name="name"></twig:Hello> ``` Wrong output: ```html <div> Hello John! </div> ``` ### Render with function ```twig {% set name = 'Bryan' %} <twig:Hello :name="name" /> ``` Correct output: ```html <div> Hello Bryan! </div> ``` Commits ------- 7c4030b [TwigComponent] Allow input props to have the same name as context variables
This PR gives you the ability to have props in your component template and in your component PHP class.