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