@@ -2081,6 +2081,8 @@ then render it manually after:
20812081
20822082 {{ form_widget(form.todoItems.vars.button_add, { label: '+ Add Item', attr: { class: 'btn btn-outline-primary' } }) }}
20832083
2084+ .. _validation :
2085+
20842086Validation (without a Form)
20852087---------------------------
20862088
@@ -2302,6 +2304,130 @@ You can also trigger a specific "action" instead of a normal re-render:
23022304 #}
23032305 >
23042306
2307+ Changing the URL when a LiveProp changes
2308+ ----------------------------------------
2309+
2310+ .. versionadded :: 2.14
2311+
2312+ The ``url `` option was introduced in Live Components 2.14.
2313+
2314+ If you want the URL to update when a ``LiveProp `` changes, you can do that with the ``url `` option::
2315+
2316+ // src/Components/SearchModule.php
2317+ namespace App\Components;
2318+
2319+ use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
2320+ use Symfony\UX\LiveComponent\Attribute\LiveProp;
2321+ use Symfony\UX\LiveComponent\DefaultActionTrait;
2322+
2323+ #[AsLiveComponent]
2324+ class SearchModule
2325+ {
2326+ use DefaultActionTrait;
2327+
2328+ #[LiveProp(writable: true, url: true)]
2329+ public string $query = '';
2330+ }
2331+
2332+ Now, when the user changes the value of the ``query `` prop, a query parameter in the URL will be updated to reflect the
2333+ new state of your component, for example: ``https://my.domain/search?query=my+search+string ``.
2334+
2335+ If you load this URL in your browser, the ``LiveProp `` value will be initialized using the query string
2336+ (e.g. ``my search string ``).
2337+
2338+ .. note ::
2339+
2340+ The URL is changed via ``history.replaceState() ``. So no new entry is added.
2341+
2342+ .. warning ::
2343+
2344+ You can use multiple components with URL bindings in the same page, as long as bound field names don't collide.
2345+ Otherwise, you will observe unexpected behaviors.
2346+
2347+ Supported Data Types
2348+ ~~~~~~~~~~~~~~~~~~~~
2349+
2350+ You can use scalars, arrays and objects in your URL bindings:
2351+
2352+ ============================================ =================================================
2353+ JavaScript ``prop `` value URL representation
2354+ ============================================ =================================================
2355+ ``'some search string' `` ``prop=some+search+string ``
2356+ ``42 `` ``prop=42 ``
2357+ ``['foo', 'bar'] `` ``prop[0]=foo&prop[1]=bar ``
2358+ ``{ foo: 'bar', baz: 42 } `` ``prop[foo]=bar&prop[baz]=42 ``
2359+
2360+
2361+ When a page is loaded with a query parameter that's bound to a ``LiveProp`` (e.g. ``/search?query=my+search+string``),
2362+ the value - ``my search string `` - goes through the hydration system before it's set onto the property. If a value can't
2363+ be hydrated, it will be ignored.
2364+
2365+ Multiple Query Parameter Bindings
2366+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2367+
2368+ You can use as many URL bindings as you want in your component. To ensure the state is fully represented in the URL,
2369+ all bound props will be set as query parameters, even if their values didn't change.
2370+
2371+ For example, if you declare the following bindings::
2372+
2373+ // ...
2374+ #[AsLiveComponent]
2375+ class SearchModule
2376+ {
2377+ #[LiveProp(writable: true, url: true)]
2378+ public string $query = '';
2379+
2380+ #[LiveProp(writable: true, url: true)]
2381+ public string $mode = 'fulltext';
2382+
2383+ // ...
2384+ }
2385+
2386+
2387+ And you only set the ``query `` value, then your URL will be updated to
2388+ ``https://my.domain/search?query=my+query+string&mode=fulltext ``.
2389+
2390+ Validating the Query Parameter Values
2391+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2392+
2393+ Like any writable ``LiveProp ``, because the user can modify this value, you should consider adding
2394+ :ref: `validation <validation >`. When you bind a ``LiveProp `` to the URL, the initial value is not automatically
2395+ validated. To validate it, you have to set up a `PostMount hook `_::
2396+
2397+ // ...
2398+ use Symfony\Component\Validator\Constraints as Assert;
2399+ use Symfony\UX\LiveComponent\ValidatableComponentTrait;
2400+ use Symfony\UX\TwigComponent\Attribute\PostMount;
2401+
2402+ #[AsLiveComponent]
2403+ class SearchModule
2404+ {
2405+ use ValidatableComponentTrait;
2406+
2407+ #[LiveProp(writable: true, url: true)]
2408+ public string $query = '';
2409+
2410+ #[LiveProp(writable: true, url: true)]
2411+ #[Assert\NotBlank]
2412+ public string $mode = 'fulltext';
2413+
2414+ #[PostMount]
2415+ public function postMount(): void
2416+ {
2417+ // Validate 'mode' field without throwing an exception, so the component can be mounted anyway and a
2418+ // validation error can be shown to the user
2419+ if (!$this->validateField('mode', false)) {
2420+ // Do something when validation fails
2421+ }
2422+ }
2423+
2424+ // ...
2425+ }
2426+
2427+ .. note ::
2428+
2429+ You can use `validation groups `_ if you want to use specific validation rules only in the PostMount hook.
2430+
23052431.. _emit :
23062432
23072433Communication Between Components: Emitting Events
@@ -3315,3 +3441,5 @@ bound to Symfony's BC policy for the moment.
33153441.. _`Symfony's built-in form theming techniques` : https://symfony.com/doc/current/form/form_themes.html
33163442.. _`pass content to Twig Components` : https://symfony.com/bundles/ux-twig-component/current/index.html#passing-blocks
33173443.. _`Twig Component debug command` : https://symfony.com/bundles/ux-twig-component/current/index.html#debugging-components
3444+ .. _`PostMount hook` : https://symfony.com/bundles/ux-twig-component/current/index.html#postmount-hook
3445+ .. _`validation groups` : https://symfony.com/doc/current/form/validation_groups.html
0 commit comments