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] Missleading doc between Passing & Rendering Attributes
and PreMount validation
#1704
Comments
Do you need the array_merge ? As multiple preMount are allowed, i'd just add a commented line in the code block #[PreMount]
public function preMount(array $data): array
{
// validate data
$resolver = new OptionsResolver();
+ // Uncomment following line to allow extra attributes
+ // $resolver->setIgnoreUndefined();
+ wdyt ? |
I understand the problem with the multiple PreMount... 🤯 When using So the resolve result will never contains those "extra data".
With the same Alert from above, [
"id" => "alert_id",
"message" => "My message",
] OptionsResolver resolve it to: [
"type" => "success",
"message" => "My message",
] Without the [
"id" => "alert_id",
"message" => "My message",
"type" => "success",
]
Maybe we need an extra PreMount Hook with different priority when using OptionsResolver and extra attributes? 🤔 Edit:Just tested with two PreMount hook, and the priority doesn't matter. #[PreMount(1)]
public function resolveOptions(array $data): array
{
// validate data
$resolver = new OptionsResolver();
$resolver->setIgnoreUndefined();
$resolver->setDefaults(['type' => 'success']);
$resolver->setAllowedValues('type', ['success', 'danger']);
$resolver->setRequired('message');
$resolver->setAllowedTypes('message', 'string');
return $resolver->resolve($data);
}
#[PreMount(2)]
public function extraData(array $data): array
{
return $data;
} Result to: [
"type" => "success",
"message" => "My message",
] |
And could a final PostMount (it should receive the additional data i think) be used ? |
Ok you pointed out an even more weirder case 😆
PostMount alone#[AsTwigComponent]
final class Alert
{
public string $message;
public string $type = 'success';
#[PostMount]
public function postMount(array $data): array
{
dump($data);
return $data;
}
} PostMount $data: [
"id" => "alert_id"
] With PreMount and resolve only#[AsTwigComponent]
final class Alert
{
public string $message;
public string $type = 'success';
#[PreMount]
public function preMount(array $data): array
{
// validate data
$resolver = new OptionsResolver();
$resolver->setIgnoreUndefined();
$resolver->setDefaults(['type' => 'success']);
$resolver->setAllowedValues('type', ['success', 'danger']);
$resolver->setRequired('message');
$resolver->setAllowedTypes('message', 'string');
return $resolver->resolve($data);
}
#[PostMount]
public function postMount(array $data): array
{
dump($data);
return $data;
}
} PostMount $data: [] With PreMount and resolve with array_merge#[AsTwigComponent]
final class Alert
{
public string $message;
public string $type = 'success';
#[PreMount]
public function preMount(array $data): array
{
// validate data
$resolver = new OptionsResolver();
$resolver->setIgnoreUndefined();
$resolver->setDefaults(['type' => 'success']);
$resolver->setAllowedValues('type', ['success', 'danger']);
$resolver->setRequired('message');
$resolver->setAllowedTypes('message', 'string');
return array_merge($data, $resolver->resolve($data));
}
#[PostMount]
public function postMount(array $data): array
{
dump($data);
return $data;
}
} PostMount $data: [
"id" => "alert_id"
] |
🤯 |
At the end.. Do we:
|
That's also a pattern I've introduced in our codebase, however we use #[PreMount]
public function preMount(array $data): array
{
$optionsResolver = new OptionsResolver();
$optionsResolver
->setIgnoreUndefined()
->setRequired(['id'])
->setAllowedTypes('id', 'string')
;
return $optionsResolver->resolve($data) + $data;
} If we keep the point of merging data/options, I would be in favor to document |
Good catch.
I agree. Do we add a new paragraph about "extra data" inside the |
So this issue points out multiple things we should work on:
Do I miss something? |
I think
ATM, no 👍 |
@cavasinf are you up for doing these PRs? I think we should do this in two PRs one for the doc and one for the PreMount hook. You can say no, there is no pressure 😁 |
I'm actually in R&D on the Live Components AND Turbo (since the SymfonyLive thanks to your demo @WebMamba 😉) which is now stable. |
Great there is no hurry! It can be next week |
With the current state, OptionsResolver will also remove arguments for #[AsTwigComponent]
final class Alert
{
public string $message;
public string $type = 'success';
public function mount(bool $isSuccess): void // <--- This argument without default value
{
$this->type = $isSuccess ? 'success' : 'danger';
}
#[PreMount(2)]
public function optionsResolver(array $data): array
{
// validate data
$resolver = new OptionsResolver();
$resolver->setIgnoreUndefined();
return $resolver->resolve($data);
}
} Used like that: <twig:Alert id="alert_id" :isSuccess="false" message="My message"/> Will produce: Caution Error rendering "Alert" component: App\Twig\Components\Alert::mount() has a required $isSuccess parameter. Make sure this is passed or make give a default value. |
Ok so this seems expected
And the data you return from PreMount is the data passed to the mount() method. So if i go back to your first example #[AsTwigComponent]
final class Alert
{
public string $message;
public string $type = 'success';
#[PreMount]
public function preMount(array $data): array
{
// validate data
$resolver = new OptionsResolver();
$resolver->setDefaults(['type' => 'success']);
$resolver->setAllowedValues('type', ['success', 'danger']);
$resolver->setRequired('message');
$resolver->setAllowedTypes('message', 'string');
return $resolver->resolve($data);
}
} You want to
So i suggest to do this #[AsTwigComponent]
final class Alert
{
public string $message;
public string $type = 'success';
#[PreMount]
public function preMount(array $data): array
{
// validate data
$resolver = new OptionsResolver();
$resolver->setAllowedValues('type', ['success', 'danger']);
$resolver->setRequired('message');
$resolver->setAllowedTypes('message', 'string');
// This will throw as you expect if type/message is invalid
$resolver->resolve($data);
// So here you can return all data (and keep your ID)
return $data;
}
} WDYT ? |
No Bueno. First, if the component is used with the I've made a PR that simply concat those data variables. |
Oops i forgot the line indeed ^^ Update:
|
Still missing that part I think 🤔 |
But the default values should be in your properties, no ? What would be the advantage of defining them in the resolver ? |
You can and the doc says to, so people will 🤷♂️
This is exactly why I've asked the question in this "issue". |
Oh, I didn't mean it as a criticism. I was just thinking aloud, trying to come up with a positive solution. And i 100% agree with you at least the doc should be updated :) |
I wasn't either, sorry if I misspoke |
So remains this one =) |
Yeah 😄 For today's "how to use" it missing from the doc:
Do not use: $resolver->resolve($data);
return $data; As it will ignore the default values from OptionsResolver (which at the end is not recommended to use by @smnandre) |
Rendering "extra" Attributes with a
PreMount
hook +OptionsResolver
will not work if followed as the doc explains it.As from the example, if the Alert component looks like this:
Calling it like this:
Will throw an
UndefinedOptionsException
error:Caution
Error rendering "Alert" component: The option "id" does not exist. Defined options are: "message", "type".
We may need to resolve this incoherence by:
PreMount
hook part, to explain how to do it with extra attributes.I could have done a PR directly, but I want to talk about it first and discuss if it is necessary or not.
The text was updated successfully, but these errors were encountered: