[LiveComponent] Multiple URL query params in one LiveProp DTO #2907
Replies: 20 comments
-
Possibilities:
|
Beta Was this translation helpful? Give feedback.
-
I'm having doubt here... are DTO as LiveProp supposed to work already, even with URL .... https://symfony.com/bundles/ux-live-component/current/index.html#supported-data-types |
Beta Was this translation helpful? Give feedback.
-
Interesting point, did you achieve any minimal working example? Because I try since 1hour, and I always throw errors:
class ArticleResults
{
use DefaultActionTrait;
use ComponentToolsTrait;
// #[LiveProp(url: true)]
// public ?string $name = null;
// #[LiveProp(url: true)]
// public ?string $status= null;
// #[LiveProp(writable: true, url: true)]
// public ?int $page = 1;
#[LiveProp(writable: true, url: true)]
public ?Filter $filter = null;
private ItemsPage $articlesPage;
public functiondoStuff(): foo {...}
}
class Filter
{
public function __construct(
public ?int $page = 1,
public ?string $name = null,
public ?string $status = null,
) {}
} I may miss something goofy here, but for me and based also on the DTO part of the doc, I can't make it work 😢 |
Beta Was this translation helpful? Give feedback.
-
The error message you quote may indicate you try to mount "name" directly on ArticleResults in your template, no ? |
Beta Was this translation helpful? Give feedback.
-
search-filters.mov |
Beta Was this translation helpful? Give feedback.
-
(sorry for the support page ... i was working on something else 😅 ) |
Beta Was this translation helpful? Give feedback.
-
<?php
namespace App\Twig;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\DefaultActionTrait;
#[AsLiveComponent]
final class Search
{
use DefaultActionTrait;
#[LiveProp(writable: ['search', 'size', 'type'], fieldName: 's', url: true)]
public SearchFilters $filters;
public function mount(): void
{
$this->filters ??= new SearchFilters();
}
public function getResults(): array
{
$results = [
['name' => 'Gorilla', 'size' => 'lg', 'type' => 'animal'],
['name' => 'Banana', 'size' => 'md', 'type' => 'fruit'],
['name' => 'Apple', 'size' => 'sm', 'type' => 'fruit'],
['name' => 'Lion', 'size' => 'lg', 'type' => 'animal'],
['name' => 'Strawberry', 'size' => 'sm', 'type' => 'fruit'],
['name' => 'Elephant', 'size' => 'lg', 'type' => 'animal'],
['name' => 'Orange', 'size' => 'md', 'type' => 'fruit'],
['name' => 'Tiger', 'size' => 'lg', 'type' => 'animal'],
['name' => 'Pineapple', 'size' => 'md', 'type' => 'fruit'],
['name' => 'Zebra', 'size' => 'lg', 'type' => 'animal'],
];
$results = array_filter($results, function($result) {
return $this->filters->search === '' || str_contains($result['name'], $this->filters->search);
});
$results = array_filter($results, function($result) {
return $this->filters->size === '' || $this->filters->size === $result['size'];
});
$results = array_filter($results, function($result) {
return $this->filters->type === '' || $this->filters->type === $result['type'];
});
return $results;
}
} <?php
namespace App\Twig;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\DefaultActionTrait;
final class SearchFilters
{
public string $search = '';
public string $type = '';
public string $size = '';
} <div{{ attributes }}>
<h2>dump(this.filters)</h2>
{{ dump(this.filters) }}
<hr />
<h2>Filters</h2>
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr;">
<div>
<label>Search</label>
<input type="search" class="form-control" data-model="s[search]" />
</div>
<div>
<label>Size</label>
<select class="form-control" data-model="s[size]">
<option value="">Size</option>
<option value="sm">Small</option>
<option value="md">Medium</option>
<option value="lg">Large</option>
</select>
</div>
<div>
<label>Type</label>
<label>
<input type="radio" class="form-check" data-model="s[type]" value="animal" />
Animal
</label>
<label>
<input type="radio" class="form-check-input" data-model="s[type]" value="fruit" />
Fruit
</label>
</div>
</div>
<hr />
<div>
<h2>Results</h2>
{% for result in this.results %}
<div id="{{ result.name }}" style="padding: 1rem 0; border-bottom: 2px dotted #eee;">
<div class="d-flex justify-content-between">
<h5>{{ result.name }}</h5>
<span>{{ result.size }}</span>
<span>{{ result.type }}</span>
</div>
</div>
{% endfor %}
</div>
</div> |
Beta Was this translation helpful? Give feedback.
-
(please do not look too much at the code quality 😆 ) |
Beta Was this translation helpful? Give feedback.
-
Thank you very much for taking the time to create example, I made some tries and here's my thoughts:
class FacetMenu
{
use DefaultActionTrait;
#[LiveProp(writable: ['page'], onUpdated: 'emitChange', url: true)]
public Filter $filter;
public function mount() { $this->filter ??= new Filter; }
public function emitChange(): void
{
dd('hello');
$this->emit('facetSetted', ['filter' => $this->filter]); // for the next point
}
}
class EventResults
{
use DefaultActionTrait;
#[LiveProp]
public Filter $filter;
#[LiveListener('facetSetted')]
public function reload(#[LiveArg] array $filter): void
{
dd($filter);
}
} All those constraints make the move quite complicated; I feel like using a DTO is way less intuitive, but I frankly don't know how to handle those issues 😶 |
Beta Was this translation helpful? Give feedback.
-
You're very right on all these points. So, now... would you want to help improving one ? And see where that leads ? I can help, pin in the direction if you need, and probably help you figuring "what's not gonna stay that way" and "what is needed because this or that features"... What do you think ? |
Beta Was this translation helpful? Give feedback.
-
I definitely accept the challenge of open source!
|
Beta Was this translation helpful? Give feedback.
-
Would you agree to take some time to discuss them (slack / meet / anything / ?) when you have the time ? Just quickly
At the same time of all your very relevant ideas and feedback... I also started to split more clearly Live and Twig component.. so i huess the work on Attributes, Metadata, Events is a must-have to clarify some behaviours, and free some place for new features :) |
Beta Was this translation helpful? Give feedback.
-
I would be pleased to speak about this on slack for example; this week my planning is a wreck, I just write issues when I have time, but next week for sure. Let me advocate for
|
Beta Was this translation helpful? Give feedback.
-
Point 4, about DTO initialization: I feel like we should left this one behind, unless someone comes with a veeery clever trick. Maybe the most optimal version will be in the language, being able to declare properties like Anyway, it can be done also in constructor, I didn't think about it, nice trick 👌 |
Beta Was this translation helpful? Give feedback.
-
Points 2 and 3 about LiveProp, needs to be clear about 5, 'general design' considerations. We agree that DX is not optimal, that TC and LC need more separation, and new features I am requesting (except Mount that is a bit stand alone) must integrate well with general design. I will take time to read your links about the split, but let me give some food here:
Based on those highly debatable points, I feel like in an ideal world:
That's some work and impossible wishes, but that's my statement for now 😆 |
Beta Was this translation helpful? Give feedback.
-
To answer about request, some reasons as I mix good & bad ideas:
You're right for this part, I feel like none of my arguments are killer ones, and a good shaped LC can get rid of any Request need, on classical use cases (if some nerds want to inject it by constructor, so be it). |
Beta Was this translation helpful? Give feedback.
-
To be honest, I think we share the same vision on the vast majority of things here... i may have not expressed my personal ideas here with enough talent 😅 The remaining differences are purely philosophical and won't be a problem here :) |
Beta Was this translation helpful? Give feedback.
-
Thank you for this issue. |
Beta Was this translation helpful? Give feedback.
-
Friendly ping? Should this still be open? I will close if I don't hear anything. |
Beta Was this translation helpful? Give feedback.
-
Hey, I didn't hear anything so I'm going to close it. Feel free to comment if this is still relevant, I can always reopen! |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Hello,
Part of the discussion about faceted search menu and results page combo (challenge 1), I'm searching to map different url parameters into one object.
In a symfony controller, you can welcome several query params in a single DTO with the MapQueryString attribute.
I wonder if we can, also in live components, group several query params into one property class?
In a live component, currently, when you have multiple query parameters to welcome, you must declare them one by one; then eventually, in a method, regroup them into an array or a DTO to do some logic. It can be cumbersome if you have a lot of potential query parameters. Plus, and I feel like it's more important, aligning behavior of component controllers (like a LC), with regular controllers is very important as it drastically lowers the mind burden and learning curve.
Context: you got a FacetedSearchMenu component, with multiple parameters that you want to reflect on url, and you group those in a FacetFilter DTO to pass to repository or whatever.
If I may help in any way,
Best regards,
Beta Was this translation helpful? Give feedback.
All reactions