@@ -2081,6 +2081,8 @@ then render it manually after:
2081
2081
2082
2082
{{ form_widget(form.todoItems.vars.button_add, { label: '+ Add Item', attr: { class: 'btn btn-outline-primary' } }) }}
2083
2083
2084
+ .. _validation :
2085
+
2084
2086
Validation (without a Form)
2085
2087
---------------------------
2086
2088
@@ -2302,6 +2304,130 @@ You can also trigger a specific "action" instead of a normal re-render:
2302
2304
#}
2303
2305
>
2304
2306
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
+
2305
2431
.. _emit :
2306
2432
2307
2433
Communication Between Components: Emitting Events
@@ -3315,3 +3441,5 @@ bound to Symfony's BC policy for the moment.
3315
3441
.. _`Symfony's built-in form theming techniques` : https://symfony.com/doc/current/form/form_themes.html
3316
3442
.. _`pass content to Twig Components` : https://symfony.com/bundles/ux-twig-component/current/index.html#passing-blocks
3317
3443
.. _`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