From 1b573628702bf72d20f3b968f3560461f4709f55 Mon Sep 17 00:00:00 2001 From: Petter Holmstrom Date: Wed, 26 Nov 2025 17:10:58 +0200 Subject: [PATCH 01/13] Edit Add a View guide --- .../building-apps/views/add-view/flow.adoc | 315 ------------------ .../building-apps/views/add-view/hilla.adoc | 171 ---------- .../building-apps/views/add-view/index.adoc | 193 ++++++++++- .../buildingapps/addview/HelloWorldView.java | 14 + 4 files changed, 195 insertions(+), 498 deletions(-) delete mode 100644 articles/building-apps/views/add-view/flow.adoc delete mode 100644 articles/building-apps/views/add-view/hilla.adoc create mode 100644 src/main/java/com/vaadin/demo/buildingapps/addview/HelloWorldView.java diff --git a/articles/building-apps/views/add-view/flow.adoc b/articles/building-apps/views/add-view/flow.adoc deleted file mode 100644 index e0012fe0f1..0000000000 --- a/articles/building-apps/views/add-view/flow.adoc +++ /dev/null @@ -1,315 +0,0 @@ ---- -title: Flow -page-title: How to add a Flow view to a Vaadin application -meta-description: Learn about defining routes, naming conventions, using route aliases, and structuring views into Java packages. Includes a hands-on tutorial to apply key concepts. -order: 5 ---- - - -= Flow Views -:toclevels: 2 - -In this guide, you'll learn how to create and name views in Java, assign multiple routes to a single view, and organize views into Java packages. To reinforce your learning, a hands-on mini-tutorial at the end helps you apply these concepts in a real Vaadin application. - - -== Creating Views - -Flow views are Java classes that are annotated with [annotationname]`@Route` and extend [classname]`com.vaadin.flow.component.Component` -- or any of its subclasses. The default parameter of the [annotationname]`@Route` annotation is the path of the view. - -For example, you can define the [classname]`HelloWorld` component as the main view like this: - -[source,java] ----- -// tag::snippet[] -@Route("") -// end::snippet[] -public class HelloWorld extends Div { - public HelloWorld() { - setText("Hello world"); - } -} ----- - -If the application is running from the root context, users would be able to access this view by navigating to `\https://example.com`. - -Likewise, you can define the [classname]`CustomerListView` component as the target for the `customer/list` route: - -[source,java] ----- -// tag::snippet[] -@Route("customer/list") -// end::snippet[] -public class CustomerListView extends Main { - public CustomerListView() { - //... - } -} ----- - -Users would be able to access this view by navigating to `\https://example.com/customer/list`. - -[NOTE] -Don't include a leading `/` when you specify the path of a view. - -Navigation between views is covered in more detail in the <<../navigate#,Navigate to a View>> guide. - - -== View Naming - -If you do not specify a default parameter for [annotationname]`@Route`, the path is derived from the name of the view. The derived name is the class name in lower case, with the `View` suffix removed if there is one. Also, [classname]`MainView` and [classname]`Main` are mapped to root. - -For example: - -* [classname]`MyEditor` becomes `"myeditor"` -* [classname]`PersonView` becomes `"person"` -* [classname]`MainView` becomes `""` - -If you specify the path explicitly, you can name your views any way you like. - - -== Route Aliases - -You can create multiple routes that target the same view. You do this with the `@RouteAlias` annotation. - -For example, you can map `\https://example.com`, `\https://example.com/home`, and `\https://example.com/main` to [classname]`HomeView` like this: - -[source,java] ----- -@Route("") -// tag::snippet[] -@RouteAlias("home") -@RouteAlias("main") -// end::snippet[] -public class HomeView extends Div { - public HomeView() { - //... - } -} ----- - -Whenever you use route aliases, you have to create a primary route by using the [annotationname]`@Route` annotation. Only adding [annotationname]`@RouteAlias` to a view does not work. - - -== Page Title - -By default, Flow views do not set a page title. You can define the page title either declaratively or dynamically. - - -=== Declarative Page Title - -To set a page title declaratively, use the [annotationname]`@PageTitle` annotation: - -[source,java] ----- -@Route("") -// tag::snippet[] -@PageTitle("Home Page") -// end::snippet[] -public class HomeView extends Div { - public HomeView() { - //... - } -} ----- - - -=== Dynamic Page Title - -To set the page title dynamically, implement the [interfacename]`HasDynamicTitle` interface: - -[source,java] ----- -@Route("") -// tag::snippet[] -public class HomeView extends Div implements HasDynamicTitle { -// end::snippet[] - - public HomeView() { - //... - } - -// tag::snippet[] - @Override - public String getPageTitle() { - return "Home Page"; - } -// end::snippet[] -} ----- - -The title is determined when the router navigates to the view. Any changes made after navigation will not affect the title. - -[IMPORTANT] -A view cannot use both the [annotationname]`@PageTitle` annotation and implement the [interfacename]`HasDynamicTitle` interface simultaneously. - - -== Navigation Menu Item - -Most business applications include a navigation menu. This menu can be generated dynamically using Vaadin's [classname]`MenuConfiguration`. To make a Flow view appear in the menu, add the `@Menu` annotation: - -[source,java] ----- -@Route() -// tag::snippet[] -@Menu(title = "Dashboard", order = 1, icon = "vaadin:dashboard") -// end::snippet[] -public class DashboardView extends Main { - public DashboardView() { - //... - } -} ----- - -The [annotationname]`@Menu` annotation has the following attributes: - -`title` :: The menu title. Defaults to the page title if not specified. -`order` :: Determines the menu item's position. Items with a defined order appear above unordered items. -`icon` :: Specifies the menu icon. This is a string, allowing flexibility in interpretation. It could be an <<{articles}/components/icons#,Icon>> name or an SVG source, depending on the menu implementation. - -For more information on building a navigation menu, see <<../add-router-layout/flow#the-navigation-menu,Add a Router Layout>>. -// TODO Do we need a separate guide on building a navigation menu? - -== Package Naming - -The recommended naming convention for Java packages containing views is [packagename]`[feature].ui.view`, where `[feature]` is the name of the full-stack feature that the view belongs to. - -If the view consists of a single class only, you can store it directly in the `ui.view` package, like this: - -[source] ----- -com.example.application -└── crm <1> - └── ui - └── view - ├── CustomerOnboardingView.java <2> - ├── CustomerListView.java - └── CustomerDetailsView.java ----- -<1> The example feature is "customer relationship management". -<2> All the views are in the same `ui.view` package. - -If the view consists of more than one class, consider creating a separate package for it, like this: - -[source] ----- -com.example.application -└── crm - └── ui - └── view - ├── onboarding - │ ├── CustomerOnboardingView.java <1> - │ └── ... - ├── CustomerListView.java <2> - └── CustomerDetailsView.java ----- -<1> The onboarding view consists of multiple classes and has its own package. -<2> The other views remain in the `ui.view` package. - -If you don't know whether your new view is going to be small or large, start by putting it in the `ui.view` package. You can always refactor it into its own package later. - - -[.collapsible-list] -== Try It - -In this mini-tutorial, you'll explore both derived and explicit routes. You'll also create a new, simple view and specify multiple routes for it. - - -.Set Up the Project -[%collapsible] -==== -First, generate a new application at http://start.vaadin.com[start.vaadin.com], <> it in your IDE, and <> it with hotswap enabled. -==== - - -.Create a Dashboard View -[%collapsible] -==== -Next, you'll create a new view. Create a new package [packagename]`[application package].tutorial.ui.view`, and inside it a new class called [classname]`DashboardView`, like this: - -.DashboardView.java -[source,java] ----- -import com.vaadin.flow.component.html.Main; -import com.vaadin.flow.router.Route; - -@Route -public class DashboardView extends Main { - public DashboardView() { - setText("Dashboard View"); - } -} ----- - -The path is derived from the class name, which means you can access the view at: http://localhost:8080/dashboard -==== - - -.Delete the Main View -[%collapsible] -==== -You'll now make the dashboard view the default landing page of the application. To do this, you first have to delete the old main view. Locate the class [classname]`MainView` inside the package [packagename]`[application package].base.ui.view` and delete it. -==== - -.Add a Route Alias -[%collapsible] -==== -Next, add a `@RouteAlias("")` annotation to [classname]`DashboardView`, like this: - -.DashboardView.java -[source,java] ----- -import com.vaadin.flow.component.html.Main; -import com.vaadin.flow.router.Route; -// tag::snippet[] -import com.vaadin.flow.router.RouteAlias; -// end::snippet[] - -@Route -// tag::snippet[] -@RouteAlias("") -// end::snippet[] -public class DashboardView extends Main { - - public MainView() { - setText("Dashboard View"); - } -} ----- - -You can now access the dashboard view also at: http://localhost:8080/ -==== - - -.Try a Route with Multiple Segments -[%collapsible] -==== -Now open [classname]`TaskListView` and change the path to `manage/tasks/with/vaadin`, like this: - -.TaskListView.java -[source,java] ----- -// tag::snippet[] -@Route("manage/tasks/with/vaadin") -// end::snippet[] -@PageTitle("Task List") -@Menu(order = 0, icon = "vaadin:clipboard-check", title = "Task List") -public class TaskListView extends Main { - ... -} ----- - -You can now access the task list view at: http://localhost:8080/manage/tasks/with/vaadin -==== - - -.Final Thoughts -[%collapsible] -==== -Now you've explored how to define and organize Flow views in a Vaadin application. You've learned how to: - -* Use both derived and explicit routes to structure your application's navigation. -* Define multiple routes for a single view, making navigation more flexible. -* Work with multi-segment routes to create more readable and meaningful URLs. - -Next, see the <<../navigate#,Navigate to a View>> guide to learn how to navigate from one view to another. -==== diff --git a/articles/building-apps/views/add-view/hilla.adoc b/articles/building-apps/views/add-view/hilla.adoc deleted file mode 100644 index 41454dafd7..0000000000 --- a/articles/building-apps/views/add-view/hilla.adoc +++ /dev/null @@ -1,171 +0,0 @@ ---- -title: Hilla -page-title: How to add a Hilla view to a Vaadin application -meta-description: Learn to create, name, and organize views in Hilla - routing conventions, directory structures, route aliases, and best practices for defining explicit routes. -order: 10 ---- - - -= Hilla Views -:toclevels: 2 - -In this guide, you'll learn how to create and name views in Hilla, assign multiple routes to a single view, and organize views into view directories. - - -== Creating Views - -Hilla views are React components that are returned by the default exported function defined in a TSX file: - -[source,tsx] -.hello.tsx ----- -export default function HelloView() { - return

Hello, World!

; -} ----- - -The default way for creating routes for views in Hilla is based on the file system structure. The route for a view is determined based on: - -1. where the respective file for a view is located, relative to the `src/main/frontend/views` directory, and - -2. how the respective file for a view is named. - -For example, if the [filename]`hello.tsx` file is located directly under `src/main/frontend/views`, then the route for `HelloView` component (default export from this file) is `/hello`, which means the file name [filename]`hello.tsx` excluding the `.tsx`. If the application is running from the root context, users would be able to access this view by navigating to `\https://example.com/hello`. Moreover, moving this file under `src/main/frontend/views/greeting` directory changes the route to `/greeting/hello`, so the users can access this view by navigating to `\https://example.com/greeting/hello`. - -Likewise, naming the view file as [filename]`hello-world.tsx` or [filename]`HelloWorld.tsx` results in the route `/hello-world` or `/HelloWorld`. - -To avoid repeating the `src/main/frontend/views` throughout this guide, `views` is used as an alias to refer to this directory. - -If a view is supposed to target the root route, the file should be named as [filename]`@index.tsx` and located directly under the `views` directory: - -[source,tsx] -.@index.tsx ----- -export default function HomeView() { - return

Home

; -} ----- - -Then, the route for the above component is `/`, and if the application is running from the root context, users can access this view by navigating to `\https://example.com/`. - - -== View Naming - -React only recognizes an exported function as a component if the function name starts with an uppercase letter. For example, the following component is recognized as a valid React component: - -[source,tsx] -.customers.tsx ----- -export default function CustomersView() { - return

Customers

; -} ----- - -Defining the function name as `customersView` or `customerList` does not result in a compile or runtime error, but is not recognized as a React component either. - -The routing uses the React component's name for creating the default automatic title for the view. For example, the title for the `CustomersView` component is `Customers`, and the title for the `HelloWorldView` component is `Hello World`. This automatically-determined title is used when creating the navigation menu based on utilities from the routing API. - -Another important convention to consider while naming the views and directories is to use the `_` (underscore) character at the beginning of the file or directory name to instruct the routing system to ignore it. For example, a file named as `_AddressFormComponent.tsx` is ignored when creating routes for views. This is useful for creating utility files and reusable components that are not intended to be available as navigation targets. - -The details about the automatic title and the navigation menu are covered in more detail in the <<../navigate#,Navigate to a View>> guide. - - -== Route Aliases - -In Hilla, there is no shortcut for creating multiple routes that target the same view. However, you can create a new view file that exports a component returning the target component. You can place this alias component in any directory to create desired route alias. For example, the following view targets the root route (`/`): - -[source,tsx] -.@index.tsx ----- -export default function RootView() { - return

Home

; -} ----- - -Now to have the same view accessible via `/home` and `/main`, you can create two additional view files: - -[source,tsx] -.home.tsx ----- -export default function HomeView() { - return RootView(); -} ----- - -and - -[source,tsx] -.main.tsx ----- -export default function MainView() { - return RootView(); -} ----- - - -== Directory Structure - -As mentioned earlier, the file system structure determines the route for a view. Therefore, organizing views into directories helps maintain a clear structure for views and routes. - -Simple views that consist of only one file can be placed directly under the `views` directory. For example, "About", "Contact Us", and the "Home" view do not need a complex composition of components, nor should have a long route, so it is a good practice to place them directly under the `views` directory: - -[source] ----- -src -└── main - └── frontend - └── views - ├── @index.tsx - ├── about.tsx - └── contact-us.tsx ----- - -This way, the routes for these views are `/`, `/about`, and `/contact-us`, respectively. - -However, for more complex views that consist of other components, or if they should accept route parameters, it is recommended to place all the files related to that view under a directory, named by the functionality they provide. For example, views related to customers can be grouped under a `customers` directory, and views related to products under a `products` directory. The following is an example of a directory structure for view files that handle the customer related functionalities: - -[source] ----- -src -└── main - └── frontend - └── views - └── customers - ├── {id} <1> - │ ├── edit.tsx <2> - │ └── index.tsx <3> - ├── @index.tsx <4> - └── new.tsx <5> ----- -1. The `{id}` directory is a placeholder for the route parameter. You will learn more about route parameters in the <<../navigate#,Navigate to a View>> guide. -2. The [filename]`edit.tsx` file is responsible for editing a specified customer details. The route for this view is `/customers/:id/edit`. -3. The [filename]`@index.tsx` file is responsible for displaying the details of a specified customer. The route for this view is `/customers/:id`. -4. The [filename]`index.tsx` file is responsible for displaying the list of customers. The route for this view is `/customers`. -5. The [filename]`new.tsx` file is responsible for adding a new customer. The route for this view is `/customers/new`. - -As this guide focuses on basics of creating views in Hilla, further details about routing conventions are covered in the <<{articles}/hilla/guides/routing#, Routing>> guide. - - -== Defining Explicit Routes - -So far, you have learned how to create views and how routes are automatically resolved based on the file system structure and file name. However, if you want to have a custom route for a view, you can export a `ViewConfig` object named `config` from the view file. The path specified for the `route` overrides the automatically-resolved path according to the routing conventions. For example, the following view has a custom route `/custom-route`: - -[source,tsx] -.hello.tsx ----- -import { ViewConfig } from "@vaadin/hilla-file-router/types.js"; - -export const config: ViewConfig = { - route: "/custom-route", -}; - -export default function HelloView() { - return

Hello, World!

; -} ----- - -Now, users can access this view by navigating to `\https://example.com/custom-route`. - -[NOTE] -Avoid using explicit routes unless absolutely necessary. The routing system is designed to automatically resolve the routes based on the file system structure and the file name, which helps to keep the routes consistent and predictable. - diff --git a/articles/building-apps/views/add-view/index.adoc b/articles/building-apps/views/add-view/index.adoc index 8fc0ed009f..a53bb7ed8b 100644 --- a/articles/building-apps/views/add-view/index.adoc +++ b/articles/building-apps/views/add-view/index.adoc @@ -2,34 +2,203 @@ title: Add a View page-title: How to add a view to a Vaadin application description: Learn how to add a view to a Vaadin application. -meta-description: Learn how to add a view to a Vaadin application using either Vaadin Flow or Hilla. -layout: tabbed-page -tab-title: Overview +meta-description: Learn how to add a view to a Vaadin application. order: 10 --- = Add a View +:toclevels: 2 + +In this guide, you'll learn how to create and name views in Java, assign multiple routes to a single view, and organize views into Java packages. + +.Copy-Paste into Your Project +[TIP] +==== +If you want to quickly try out a view in your Vaadin application, copy-paste the following code into a new Java class named [classname]`HelloWorldView` in your project's main package: + +[source,java] +---- +include::{root}/src/main/java/com/vaadin/demo/buildingapps/addview/HelloWorldView.java[tags=snippet,indent=0] +---- + +For more detailed instructions on how to add a view, continue reading below. +==== + +== What Is a View? In a Vaadin application, a _view_ is a user interface component that forms a logical whole of the user interface. A page can contain other components than a view, but only one view at a time. Typically, the view takes up most of the page. -Each view is associated with its own URL path. When the user navigates from one view to another, the URL of the page changes. The mapping between a URL path and a view is called a _route_, where the view is the _target_ of the route. +=== Views or Routes? + +Each view is associated with its own URL path. When the user navigates from one view to another, the URL of the page changes. *The mapping between a URL path and a view is called a _route_*, where the *view is the _target_ of the route*. -[NOTE] All routes must be unique to allow the routing logic to determine the view to render without any disambiguation. When conflicts are detected, the application fails to start and logs a message explaining the reason for the conflict. -In this screenshot, the visible view is the _main_ view. It is mapped to the `""` path: +The main view of an application is mapped to the root path (`""`). It is sometimes also called the _root_ view. +.Screenshot of a main or root view image::images/root-view.png[Example of a main view] +.Screenshot of a view mapped to the "customers" path +image::images/customers-view.png[Example of a customers view] + +== Creating Views + +Vaadin views are Java classes that are annotated with [annotationname]`@Route` and extend [classname]`com.vaadin.flow.component.Component` -- or any of its subclasses. The default parameter of the [annotationname]`@Route` annotation is the path of the view. + +For example, you can define the [classname]`HelloWorld` component as the main view like this: + +[source,java] +---- +// tag::snippet[] +@Route("") +// end::snippet[] +public class HelloWorld extends Div { + public HelloWorld() { + setText("Hello world"); + } +} +---- + +If the application is running from the root context, users would be able to access this view by navigating to `\https://example.com`. + +Likewise, you can define the [classname]`CustomerListView` component as the target for the `customer/list` route: + +[source,java] +---- +// tag::snippet[] +@Route("customer/list") +// end::snippet[] +public class CustomerListView extends Main { + public CustomerListView() { + //... + } +} +---- + +Users would be able to access this view by navigating to `\https://example.com/customer/list`. + [NOTE] -The main view is sometimes also called the _root_ view. The terms are interchangeable. +Don't include a leading `/` when you specify the path of a view. + +Navigation between views is covered in more detail in the <<../navigate#,Navigate to a View>> guide. + +.Which Package to Put Views In? +[TIP] +For information about package structures for Vaadin applications, see the <> architecture deep-dive. + +== View Naming + +If you do not specify a default parameter for [annotationname]`@Route`, the path is derived from the name of the view. The derived name is the class name in lower case, with the `View` suffix removed if there is one. Also, [classname]`MainView` and [classname]`Main` are mapped to root. + +For example: + +* [classname]`MyEditor` becomes `"myeditor"` +* [classname]`PersonView` becomes `"person"` +* [classname]`MainView` becomes `""` + +If you specify the path explicitly, you can name your views any way you like. + + +== Route Aliases + +You can create multiple routes that target the same view. You do this with the `@RouteAlias` annotation. + +For example, you can map `\https://example.com`, `\https://example.com/home`, and `\https://example.com/main` to [classname]`HomeView` like this: + +[source,java] +---- +@Route("") +// tag::snippet[] +@RouteAlias("home") +@RouteAlias("main") +// end::snippet[] +public class HomeView extends Div { + public HomeView() { + //... + } +} +---- + +Whenever you use route aliases, you have to create a primary route by using the [annotationname]`@Route` annotation. Only adding [annotationname]`@RouteAlias` to a view does not work. + + +== Page Title + +By default, Vaadin views do not set a page title. You can define the page title either declaratively or dynamically. + + +=== Declarative Page Title + +To set a page title declaratively, use the [annotationname]`@PageTitle` annotation: + +[source,java] +---- +@Route("") +// tag::snippet[] +@PageTitle("Home Page") +// end::snippet[] +public class HomeView extends Div { + public HomeView() { + //... + } +} +---- + + +=== Dynamic Page Title + +To set the page title dynamically, implement the [interfacename]`HasDynamicTitle` interface: + +[source,java] +---- +@Route("") +// tag::snippet[] +public class HomeView extends Div implements HasDynamicTitle { +// end::snippet[] + + public HomeView() { + //... + } + +// tag::snippet[] + @Override + public String getPageTitle() { + return "Home Page"; + } +// end::snippet[] +} +---- + +The title is determined when the router navigates to the view. Any changes made after navigation will not affect the title. + +[IMPORTANT] +A view cannot use both the [annotationname]`@PageTitle` annotation and implement the [interfacename]`HasDynamicTitle` interface simultaneously. + + +== Navigation Menu Item + +Most business applications include a navigation menu. This menu can be generated dynamically using Vaadin's [classname]`MenuConfiguration`. To make a view appear in the menu, add the `@Menu` annotation: -In this screenshot, the visible view is mapped to the `"customers"` path: +[source,java] +---- +@Route() +// tag::snippet[] +@Menu(title = "Dashboard", order = 1, icon = "vaadin:dashboard") +// end::snippet[] +public class DashboardView extends Main { + public DashboardView() { + //... + } +} +---- -image::images/customers-view.png[] +The [annotationname]`@Menu` annotation has the following attributes: -You can write your views using either Flow and Java, or Hilla, React and TypeScript -- in the same application. The following guides show you how: +`title` :: The menu title. Defaults to the page title if not specified. +`order` :: Determines the menu item's position. Items with a defined order appear above unordered items. +`icon` :: Specifies the menu icon. This is a string, allowing flexibility in interpretation. It could be an <<{articles}/components/icons#,Icon>> name or an SVG source, depending on the menu implementation. -* <> -* <> +For more information on building a navigation menu, see <<../add-router-layout/flow#the-navigation-menu,Add a Router Layout>>. +// TODO Do we need a separate guide on building a navigation menu? diff --git a/src/main/java/com/vaadin/demo/buildingapps/addview/HelloWorldView.java b/src/main/java/com/vaadin/demo/buildingapps/addview/HelloWorldView.java new file mode 100644 index 0000000000..ac32ff9132 --- /dev/null +++ b/src/main/java/com/vaadin/demo/buildingapps/addview/HelloWorldView.java @@ -0,0 +1,14 @@ +package com.vaadin.demo.buildingapps.addview; + +// tag::snippet[] +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.Route; + +@Route("building-apps/add-view/hello") +public class HelloWorldView extends VerticalLayout { + + HelloWorldView() { + add("Hello, World!"); + } +} +// end::snippet[] \ No newline at end of file From 11ed7f6c45a047f70f9c1170c801f9567f89a742 Mon Sep 17 00:00:00 2001 From: Petter Holmstrom Date: Wed, 26 Nov 2025 17:11:26 +0200 Subject: [PATCH 02/13] Edit Navigate to a View --- .../building-apps/views/navigate/flow.adoc | 359 ------------------ .../building-apps/views/navigate/hilla.adoc | 66 ---- .../building-apps/views/navigate/index.adoc | 225 ++++++++++- .../demo/buildingapps/navigate/AboutView.java | 19 + .../demo/buildingapps/navigate/HomeView.java | 17 + 5 files changed, 253 insertions(+), 433 deletions(-) delete mode 100644 articles/building-apps/views/navigate/flow.adoc delete mode 100644 articles/building-apps/views/navigate/hilla.adoc create mode 100644 src/main/java/com/vaadin/demo/buildingapps/navigate/AboutView.java create mode 100644 src/main/java/com/vaadin/demo/buildingapps/navigate/HomeView.java diff --git a/articles/building-apps/views/navigate/flow.adoc b/articles/building-apps/views/navigate/flow.adoc deleted file mode 100644 index 14ffb1ed50..0000000000 --- a/articles/building-apps/views/navigate/flow.adoc +++ /dev/null @@ -1,359 +0,0 @@ ---- -title: Flow -page-title: How to navigate to a view in Flow | Vaadin -meta-description: Learn how to navigate between views in Vaadin Flow using RouterLink and UI.navigate(), and improve code readability by encapsulating navigation logic. -order: 5 ---- - - -= Navigation in Flow -:toclevels: 2 - -In this guide, you'll learn how to use [classname]`RouterLink` and [methodname]`UI.navigate()` to navigate between views. You'll also learn how to improve the readability of your code by encapsulating some of the navigation logic into your own API. At the end, a mini-tutorial helps you to apply these concepts in a real Vaadin application. - - -== Router Links - -[classname]`RouterLink` is a component that creates a clickable link for navigation. In HTML, it corresponds to an anchor (``) element. - -[TIP] -Links are preferable to programmatic navigation because they *improve accessibility*. They also allow users to open links in new browser tabs. - -The following example creates a link to the [classname]`MainView`: - -[source,java] ----- -var link = new RouterLink("Home", MainView.class); -myLayout.add(link); ----- - -If the view is accepting a single route parameter, you can pass the parameter value to the [classname]`RouterLink` constructor. - -In the following example, [classname]`CustomerDetailsView` implements the [interfacename]`HasUrlParameter` interface and takes a single string parameter - the customer's ID. The link navigates to the details of the customer with ID `"cu1234"`: - -[source,java] ----- -var link = new RouterLink("Customer Details", CustomerDetailsView.class, "cu1234"); -myLayout.add(link); ----- - -If the view is accepting multiple route parameters, you need to construct an instance of [classname]`RouteParameters` and pass it to the [classname]`RouterLink` constructor. You can construct it in different ways; see its https://vaadin.com/api/platform/current/com/vaadin/flow/router/RouteParameters.html[API documentation] for details. -// TODO Should the API link be versioned? - -The following example creates a link to the customer details view with two route parameters; `customerId` with the value of `"cu1234"`, and `mode` with the value of `"edit"`: - -[source,java] ----- -var link = new RouterLink("Edit Customer", CustomerDetailsView.class, - new RouteParameters(Map.of("customerId", "cu1234", "mode", "edit"))); -myLayout.add(link); ----- - -For more information about route parameters, see the <<../pass-data/route-parameters#,Route Parameters>> guide. - - -== Programmatic Navigation - -Using links is not the only way to navigate from a view to another. You can trigger a navigation in Java by calling any of the [methodname]`UI.navigate()` methods. You typically do this in response to user actions, such as inside button click listeners, but this is not a requirement. You could also trigger navigation from a background thread using server push, for instance. -// TODO Add links to background thread and server push - -The API of [methodname]`UI.navigate()` is similar to that of [classname]`RouterLink`. In the following example, the router attempts to navigate to the home view when the user clicks the button: - -[source,java] ----- -var button = new Button("Home"); -button.addClickListener(event -> - UI.getCurrent().navigate(MainView.class) -); ----- - -If the view is accepting a single route parameter, you can pass the parameter value to [methodname]`UI.navigate()`, like this: - -[source,java] ----- -var button = new Button("Customer Details"); -button.addClickListener(event -> - UI.getCurrent().navigate(CustomerDetailsView.class, "cu1234") -); ----- - -If the view is accepting multiple route parameters, you need to construct an instance of [classname]`RouteParameters` and pass it to [methodname]`UI.navigate()`, like this: - -[source,java] ----- -var button = new Button("Edit Customer"); -button.addClickListener(event -> - UI.getCurrent().navigate(CustomerDetailsView.class, - new RouteParameters(Map.of("customerId", "cu1234", "mode", "edit")) - ) -); ----- - - -== Your Own API - -Instead of scattering [methodname]`UI.navigate()` calls throughout your codebase, it's a good practice to encapsulate navigation logic within dedicated methods. This makes the code more readable, maintainable, and easier to refactor. - -In the following example, the [classname]`CustomerDetailsView` has a static method for navigating to the details of the customer with the given ID: - -[source,java] ----- -@Route("customer") -public class CustomerDetailsView extends Main implements HasUrlParameter { - - public static void showCustomerDetails(String customerId) { - UI.getCurrent().navigate(CustomerDetailsView.class, customerId); - } - ... -} ----- - -When you want to navigate to the view, you call the method, like this: - -[source,java] ----- -var button = new Button("Customer Details"); -button.addClickListener(event -> - CustomerDetailsView.showCustomerDetails("cu1234") -); ----- - -If you use multiple route parameters, or custom parameter types, this approach becomes even more useful. - -In the following example, the [classname]`CustomerDetailsView` accepts two route parameters; a value object [classname]`CustomerId` and an enum [classname]`Mode`: - -[source,java] ----- -@Route("customer/:customerId/:mode?(edit|view)") // <1> -public class CustomerDetailsView extends Main implements HasUrlParameter { - - public enum Mode { - edit, view - } - - private static RouteParameters createRouteParameters( - CustomerId customerId, Mode mode) { - return new RouteParameters( - Map.of( - "customerId", customerId.toString(), - "mode", mode.toString() - ) - ); - } - - public static RouterLink createEditLinkTo(String text, CustomerId customerId) { - return new RouterLink(text, CustomerDetailsView.class, - createRouteParameters(customerId, Mode.edit)); - } - - public static void editCustomerDetails(CustomerId customerId) { - UI.getCurrent().navigate(CustomerDetailsView.class, - createRouteParameters(customerId, Mode.edit)); - } - ... -} ----- -<1> This is a route template with two route parameters. - -With an API like this, you can create a new router link like this: - -[source,java] ----- -CustomerId customerId = ...; -myLayout.add(CustomerDetailsView.createEditLinkTo("Edit Customer", customerId)); ----- - -If you want to programmatically navigate to the view, you can do it like this: - -[source,java] ----- -CustomerId customerId = ...; -var button = new Button("Edit Customer"); -button.addClickListener(event -> - CustomerDetailsView.editCustomerDetails(customerId) -); ----- - - -== React Views - -So far, all the examples have covered navigating from one Flow view to another. However, you can also navigate from a Flow view to a React view. Unlike Flow views, which use Java class references for navigation, React views require string-based routes because they don't have a corresponding Java class. - -You can use anchor elements for navigation, or trigger programmatic navigation using [methodname]`UI.navigate()`. - -In Flow, you create anchors like this: - -[source,java] ----- -var link = new Anchor("path/to/react/view", "Link to React View"); -myLayout.add(link); ----- - -[NOTE] -Vaadin sets the https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base[base URL] of the application to the path of the main view. All relative links are resolved against this URL. This means that you don't have to worry about the context path when you create `Anchor` objects. - -You can also programmatically navigate to React views, like this: - -[source,java] ----- -var button = new Button("Go to React view"); -button.addClickListener(event -> UI.getCurrent().navigate("path/to/react/view")); ----- - - -[.collapsible-list] -== Try It - -In this mini-tutorial, you'll learn how to navigate between Flow views using both *links* and *programmatic navigation*. Route parameters are not covered here, as they have their own dedicated guides. - - -.Set Up the Project -[%collapsible] -==== -First, generate a new application at http://start.vaadin.com[start.vaadin.com], <> it in your IDE, and <> it with hotswap enabled. - -[NOTE] -If you completed the mini-tutorial on <<../add-view/flow#try-it,adding views>>, you can continue using the same project. - -==== - - -.Create the Links View -[%collapsible] -==== -Now, you'll create a new view that provides multiple ways to navigate to the task list view. Create a new package [packagename]`[application package].tutorial.ui.view` package, and in it a class called `LinksView`: - -.LinksView.java -[source,java] ----- -import com.vaadin.flow.component.html.Main; -import com.vaadin.flow.router.Route; - -@Route("links") -public class LinksView extends Main { - public LinksView() { - } -} ----- -==== - - -.Add a Router Link -[%collapsible] -==== -A [classname]`RouterLink` creates a clickable link to another view. Modify [classname]`LinksView` to include a link to the [classname]`TaskListView`: - -.LinksView.java -[source,java] ----- -import com.vaadin.flow.component.html.Main; -import com.vaadin.flow.router.Route; -// tag::snippet[] -import com.vaadin.flow.router.RouterLink; -// end::snippet[] - -@Route("links") -public class LinksView extends Main { - - public LinksView() { -// tag::snippet[] - add(new RouterLink("Task List", TaskListView.class)); -// end::snippet[] - } -} ----- - -Now, open your browser and go to: http://localhost:8080/links - -Hover on the "Task List" link to see that it points to `\http://localhost:8080`. Click the link to navigate to the task list view, then use the *browser's back button* to return. -==== - - -.Navigate Programmatically -[%collapsible] -==== -Next, you'll add a button that navigates to the task list view when clicked. Modify [classname]`LinksView` to include a [classname]`Button`: - -.LinksView.java -[source,java] ----- -// tag::snippet[] -import com.vaadin.flow.component.UI; -import com.vaadin.flow.component.button.Button; -// end::snippet[] -import com.vaadin.flow.component.html.Main; -import com.vaadin.flow.router.Route; -import com.vaadin.flow.router.RouterLink; - -@Route("links") -public class LinksView extends Main { - - public LinksView() { - add(new RouterLink("Task List", TaskListView.class)); - // tag::snippet[] - add(new Button("Task List", - event -> UI.getCurrent().navigate(TaskListView.class))); - // end::snippet[] - } -} ----- - -Switch back to the browser. Thanks to *hotswap*, the new [guibutton]*Task List* button should appear automatically. Click it to navigate to the task list view. -==== - - -.Create an API -[%collapsible] -==== -To make navigation more reusable and readable, you'll now create a dedicated method for navigating to the task list view. - -Open [classname]`TaskListView` and add this method: - -.TaskListView.java -[source,java] ----- -@Route("") -@PageTitle("Task List") -@Menu(order = 0, icon = "vaadin:clipboard-check", title = "Task List") -public class TaskListView extends Main { - ... - // tag::snippet[] - public static void showTasks() { - UI.getCurrent().navigate(TaskListView.class); - } - // end::snippet[] -} ----- - -Now, update [classname]`LinksView` to use this method instead of calling [methodname]`UI.getCurrent().navigate()` directly: - -.LinksView.java -[source,java] ----- -@Route("links") -public class LinksView extends Main { - - public LinksView() { - add(new RouterLink("Task List", TaskListView.class)); - // tag::snippet[] - add(new Button("Task List", event -> TaskListView.showTasks())); - // end::snippet[] - } -} ----- - -Go back to the browser and click the button. It works the same as before, but your code is *cleaner and easier to maintain*. -==== - - -.Final Thoughts -[%collapsible] -==== -You've now explored different ways to navigate between Flow views. Here's what you've learned: - -* Creating a navigation link using [classname]`RouterLink`. -* Programmatically navigating using [methodname]`UI.navigate()`. -* Building a reusable navigation API, improving code readability. - -Now that you know how to navigate between views, check out the <<../pass-data#,Pass Data to a View>> guide to learn how to pass data to a view while navigating to it. -==== \ No newline at end of file diff --git a/articles/building-apps/views/navigate/hilla.adoc b/articles/building-apps/views/navigate/hilla.adoc deleted file mode 100644 index 6b52f58d91..0000000000 --- a/articles/building-apps/views/navigate/hilla.adoc +++ /dev/null @@ -1,66 +0,0 @@ ---- -title: Hilla -page-title: How to navigate to a view in Hilla | Vaadin -description: Learn how to navigate between Hilla views in a Vaadin application. -meta-description: This guide covers how to navigate to a Hilla or Flow view in Vaadin applications, including creating links and programmatic navigation. -order: 10 ---- - - -= Navigation in Hilla - -In this guide, you'll learn how to use `` component and `useNavigate` hook of `react-router` to navigate between views. - - -== Navigating Between Views Using Links - -The `` component from `react-router` renders a clickable link for navigation. In HTML, it corresponds to an anchor (``) element. - -[TIP] -Links are preferable to programmatic navigation because they *improve accessibility*. They also allow users to open links in new browser tabs. - - -For example, to create a link to a view located at `/some-view`, you can use the following code: - -[source,tsx] ----- -import { NavLink } from 'react-router'; - -Some View ----- - -This code creates a clickable link labeled "Some View" that navigates to the `/some-view` route when clicked. - - -== Programmatic Navigation - -In some scenarios, you may need to navigate between views programmatically, such as after a form submission or in response to user interactions. For this you can use the `useNavigate` hook of `react-router` to achieve this. - -Here's an example of how to use `useNavigate` for programmatic navigation: - -[source,tsx] ----- -import { useNavigate } from 'react-router'; - -function MyComponent() { -const navigate = useNavigate(); - - const handleClick = () => { - navigate('/target-view'); - }; - - return ( - - ); -} ----- - -In the above example, clicking the button navigates the user to `/target-view`. - - -== Flow Views - -To navigate from a Hilla view to a Flow view that is implemented in Java, the same principles apply. You can use the `NavLink` component or the `useNavigate` hook to navigate to the Flow view. - diff --git a/articles/building-apps/views/navigate/index.adoc b/articles/building-apps/views/navigate/index.adoc index b0a0519948..fed4e0a093 100644 --- a/articles/building-apps/views/navigate/index.adoc +++ b/articles/building-apps/views/navigate/index.adoc @@ -3,23 +3,232 @@ title: Navigate to a View page-title: How to navigate to a view in a Vaadin application description: Learn how to navigate between the views of a Vaadin application. meta-description: Learn how to navigate between views in Vaadin using links, APIs, or direct URL changes, with a router that handles view rendering and parameter passing. -layout: tabbed-page -tab-title: Overview order: 15 --- = Navigate to a View +:toclevels: 2 + +In this guide, you'll learn how to use [classname]`RouterLink` and [methodname]`UI.navigate()` to navigate between views. You'll also learn how to improve the readability of your code by encapsulating some of the navigation logic into your own API. + +.Copy-Paste into Your Project +[TIP] +==== +If you want to quickly try out navigating between views, you can copy-paste the following two classes into your Vaadin project: + +[source,java] +---- +include::{root}/src/main/java/com/vaadin/demo/buildingapps/navigate/HomeView.java[tags=snippet,indent=0] +---- +[source,java] +---- +include::{root}/src/main/java/com/vaadin/demo/buildingapps/navigate/AboutView.java[tags=snippet,indent=0] +---- + +For more detailed instructions on how to navigate between views, continue reading below. +==== You can navigate to a view either programmatically through an API, by clicking a link, or by changing the URL of the browser. -In a Vaadin application, navigation is handled by a _router_. The router takes care of rendering the correct view (i.e. the route target) and keeping the URL in sync. If the view accepts parameters, the router also extracts them from the URL and passes them on to the view. +In a Vaadin application, navigation is handled by a _router_. The router takes care of rendering the correct view (i.e. the route target) and keeping the URL in sync. + +If the view accepts parameters, the router also extracts them from the URL and passes them on to the view. This is covered in the <<../pass-data#,Pass Data to a View>> guide. + + +== Router Links + +[classname]`RouterLink` is a component that creates a clickable link for navigation. In HTML, it corresponds to an anchor (``) element. + +[TIP] +Links are preferable to programmatic navigation because they *improve accessibility*. They also allow users to open links in new browser tabs. + +The following example creates a link to the [classname]`MainView`: + +[source,java] +---- +var link = new RouterLink("Home", MainView.class); +myLayout.add(link); +---- + +=== Route Parameters in Links + +If the view is accepting a single route parameter, you can pass the parameter value to the [classname]`RouterLink` constructor. + +In the following example, [classname]`CustomerDetailsView` implements the [interfacename]`HasUrlParameter` interface and takes a single string parameter - the customer's ID. The link navigates to the details of the customer with ID `"cu1234"`: + +[source,java] +---- +var link = new RouterLink("Customer Details", CustomerDetailsView.class, "cu1234"); +myLayout.add(link); +---- + +If the view is accepting multiple route parameters, you need to construct an instance of [classname]`RouteParameters` and pass it to the [classname]`RouterLink` constructor. You can construct it in different ways; see its https://vaadin.com/api/platform/{moduleMavenVersion:com.vaadin:vaadin}/com/vaadin/flow/router/RouteParameters.html[API documentation] for details. +// TODO Should the API link be versioned? + +The following example creates a link to the customer details view with two route parameters; `customerId` with the value of `"cu1234"`, and `mode` with the value of `"edit"`: + +[source,java] +---- +var link = new RouterLink("Edit Customer", CustomerDetailsView.class, + new RouteParameters(Map.of("customerId", "cu1234", "mode", "edit"))); +myLayout.add(link); +---- + +For more information about route parameters, see the <<../pass-data/route-parameters#,Route Parameters>> guide. + + +== Programmatic Navigation + +Using links is not the only way to navigate from a view to another. You can trigger a navigation in Java by calling any of the [methodname]`UI.navigate()` methods. You typically do this in response to user actions, such as inside button click listeners, but this is not a requirement. You could also trigger navigation from a background thread using server push, for instance. +// TODO Add links to background thread and server push + +The API of [methodname]`UI.navigate()` is similar to that of [classname]`RouterLink`. In the following example, the router attempts to navigate to the home view when the user clicks the button: + +[source,java] +---- +var button = new Button("Home"); +button.addClickListener(event -> + UI.getCurrent().navigate(MainView.class) +); +---- + +=== Route Parameters in Programmatic Navigation + +If the view is accepting a single route parameter, you can pass the parameter value to [methodname]`UI.navigate()`, like this: + +[source,java] +---- +var button = new Button("Customer Details"); +button.addClickListener(event -> + UI.getCurrent().navigate(CustomerDetailsView.class, "cu1234") +); +---- + +If the view is accepting multiple route parameters, you need to construct an instance of [classname]`RouteParameters` and pass it to [methodname]`UI.navigate()`, like this: + +[source,java] +---- +var button = new Button("Edit Customer"); +button.addClickListener(event -> + UI.getCurrent().navigate(CustomerDetailsView.class, + new RouteParameters(Map.of("customerId", "cu1234", "mode", "edit")) + ) +); +---- + + +== Your Own API + +Instead of scattering [methodname]`UI.navigate()` calls throughout your codebase, it's a good practice to encapsulate navigation logic within dedicated methods. This makes the code more readable, maintainable, and easier to refactor. + +In the following example, the [classname]`CustomerDetailsView` has a static method for navigating to the details of the customer with the given ID: + +[source,java] +---- +@Route("customer") +public class CustomerDetailsView extends Main implements HasUrlParameter { + + public static void showCustomerDetails(String customerId) { + UI.getCurrent().navigate(CustomerDetailsView.class, customerId); + } + ... +} +---- + +When you want to navigate to the view, you call the method, like this: + +[source,java] +---- +var button = new Button("Customer Details"); +button.addClickListener(event -> + CustomerDetailsView.showCustomerDetails("cu1234") +); +---- + + +=== Multiple Route Parameters + +If you use multiple route parameters, or custom parameter types, this approach becomes even more useful. + +In the following example, the [classname]`CustomerDetailsView` accepts two route parameters; a value object [classname]`CustomerId` and an enum [classname]`Mode`: + +[source,java] +---- +@Route("customer/:customerId/:mode?(edit|view)") // <1> +public class CustomerDetailsView extends Main implements HasUrlParameter { + + public enum Mode { + edit, view + } + + private static RouteParameters createRouteParameters( + CustomerId customerId, Mode mode) { + return new RouteParameters( + Map.of( + "customerId", customerId.toString(), + "mode", mode.toString() + ) + ); + } + + public static RouterLink createEditLinkTo(String text, CustomerId customerId) { + return new RouterLink(text, CustomerDetailsView.class, + createRouteParameters(customerId, Mode.edit)); + } + + public static void editCustomerDetails(CustomerId customerId) { + UI.getCurrent().navigate(CustomerDetailsView.class, + createRouteParameters(customerId, Mode.edit)); + } + ... +} +---- +<1> This is a route template with two route parameters. + +With an API like this, you can create a new router link like this: + +[source,java] +---- +CustomerId customerId = ...; +myLayout.add(CustomerDetailsView.createEditLinkTo("Edit Customer", customerId)); +---- + +If you want to programmatically navigate to the view, you can do it like this: + +[source,java] +---- +CustomerId customerId = ...; +var button = new Button("Edit Customer"); +button.addClickListener(event -> + CustomerDetailsView.editCustomerDetails(customerId) +); +---- + + +== React Views + +So far, all the examples have covered navigating from one Java view to another. However, you can also navigate from a Java view to a React view. Unlike Java views, which use class references for navigation, React views require string-based routes because they don't have a corresponding Java class. + +You can use anchor elements for navigation, or trigger programmatic navigation using [methodname]`UI.navigate()`. + +You create anchors like this: + +[source,java] +---- +var link = new Anchor("path/to/react/view", "Link to React View"); +myLayout.add(link); +---- -Flow and Hilla views are handled by the same router. You can navigate to a Flow view from a Hilla view, and vice versa. +[NOTE] +Vaadin sets the https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base[base URL] of the application to the path of the main view. All relative links are resolved against this URL. This means that you don't have to worry about the context path when you create `Anchor` objects. -The following guides teach you how to navigate between views in Vaadin: +You can also programmatically navigate to React views, like this: -* <> -* <> +[source,java] +---- +var button = new Button("Go to React view"); +button.addClickListener(event -> UI.getCurrent().navigate("path/to/react/view")); +---- -// TODO Write a deep-dive about routing +For more information about using React views in Vaadin, see the <> guides. \ No newline at end of file diff --git a/src/main/java/com/vaadin/demo/buildingapps/navigate/AboutView.java b/src/main/java/com/vaadin/demo/buildingapps/navigate/AboutView.java new file mode 100644 index 0000000000..f808a8d848 --- /dev/null +++ b/src/main/java/com/vaadin/demo/buildingapps/navigate/AboutView.java @@ -0,0 +1,19 @@ +package com.vaadin.demo.buildingapps.navigate; + +// tag::snippet[] +import com.vaadin.flow.component.UI; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.Route; + +@Route("building-apps/navigate/about") +public class AboutView extends VerticalLayout{ + + AboutView() { + add("You'll navigate to this view."); + } + + public static void showView() { + UI.getCurrent().navigate(AboutView.class); + } +} +// end::snippet[] diff --git a/src/main/java/com/vaadin/demo/buildingapps/navigate/HomeView.java b/src/main/java/com/vaadin/demo/buildingapps/navigate/HomeView.java new file mode 100644 index 0000000000..36b7629091 --- /dev/null +++ b/src/main/java/com/vaadin/demo/buildingapps/navigate/HomeView.java @@ -0,0 +1,17 @@ +package com.vaadin.demo.buildingapps.navigate; + +// tag::snippet[] +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.Route; +import com.vaadin.flow.router.RouterLink; + +@Route("building-apps/navigate/home") +public class HomeView extends VerticalLayout { + + HomeView() { + add(new RouterLink("Go to About (link)", AboutView.class)); + add(new Button("Go to About (button)", e -> AboutView.showView())); + } +} +// end::snippet[] From de807d84fd74e57aebd4c8767e57ce378914d654 Mon Sep 17 00:00:00 2001 From: Petter Holmstrom Date: Wed, 26 Nov 2025 17:36:13 +0200 Subject: [PATCH 03/13] Edit Route Parameters --- .../views/pass-data/route-parameters.adoc | 141 ++++++++++ .../pass-data/route-parameters/flow.adoc | 259 ------------------ .../pass-data/route-parameters/hilla.adoc | 201 -------------- .../pass-data/route-parameters/index.adoc | 39 --- .../passdata/RouteParameterView.java | 42 +++ 5 files changed, 183 insertions(+), 499 deletions(-) create mode 100644 articles/building-apps/views/pass-data/route-parameters.adoc delete mode 100644 articles/building-apps/views/pass-data/route-parameters/flow.adoc delete mode 100644 articles/building-apps/views/pass-data/route-parameters/hilla.adoc delete mode 100644 articles/building-apps/views/pass-data/route-parameters/index.adoc create mode 100644 src/main/java/com/vaadin/demo/buildingapps/passdata/RouteParameterView.java diff --git a/articles/building-apps/views/pass-data/route-parameters.adoc b/articles/building-apps/views/pass-data/route-parameters.adoc new file mode 100644 index 0000000000..9cffd5dae4 --- /dev/null +++ b/articles/building-apps/views/pass-data/route-parameters.adoc @@ -0,0 +1,141 @@ +--- +title: Route Parameters +page-title: How to use route parameters in a Vaadin application +description: Learn how to use route parameters in Vaadin. +meta-description: Learn how to use route parameters in Vaadin to pass dynamic data in URLs, personalize responses, and modify application behavior. +order: 10 +--- + + += Route Parameters +:toclevels: 2 + +In this guide, you'll learn how to create a view that accepts a single route parameter. You'll also explore the differences between optional and wildcard route parameters. + +.Copy-Paste into Your Project +[TIP] +==== +If you want to quickly try out route parameters in your Vaadin application, copy-paste the following code into a new Java class named [classname]`RouteParameterView` in your project's main package: + +[source,java] +---- +include::{root}/src/main/java/com/vaadin/demo/buildingapps/passdata/RouteParameterView.java[tags=snippet,indent=0] +---- + +For more detailed instructions on how to use route parameters, continue reading below. +==== + + +== What Are Route Parameters? + +Route parameters are dynamic segments in a URL that allow extra information to be passed to a view. They are appended to the route path and can be used to personalize responses or modify application behavior. + +For example, if an application has a `greet` route that accepts a string parameter, users can call it with URLs like: + +* `/greet/John` +* `/greet/Jane` +* `/greet/World` + +Here, `"John"`, `"Jane"`, and `"World"` are parameter values that the `greet` route can use to generate a response. + + +== Route Parameters vs Static Routes + +Static routes always take precedence over dynamic ones. For instance, consider an application with both a `customer` route (which accepts a string parameter), and a `customer/list` route: + +* `/customer/cu1234` -> Calls the `customer` route with `"cu1234"` as the parameter value. +* `/customer/details` -> Calls the `customer` route with `"details"` as the parameter value. +* `/customer/list` -> Calls the dedicated `customer/list` route. +* `/customer` -> Returns a `404 Not Found` error, unless the parameter is explicitly declared optional. + + +== Accessing the Route Parameter Value + +To pass a route parameter to a Vaadin view, implement the [interfacename]`HasUrlParameter` interface. This interface requires a type argument, which can be `Long`, `Integer`, `String`, or `Boolean`. It defines the [methodname]`setParameter()` method, which receives the route parameter when the view is navigated to. + +For example, to create a [classname]`CustomerView` that takes a customer ID as a parameter, use the following code: + +[source,java] +---- +@Route("customers") +public class CustomerView extends Main implements HasUrlParameter { + + @Override + public void setParameter(BeforeEvent event, String parameter) { + setText("Customer ID: " + parameter); + } +} +---- + +Now, if you navigate to `/customers/cu12345`, the router calls [methodname]`setParameter()` with `"cu12345"` as the parameter value. + + +== Optional Parameters + +By default, route parameters are required. In the example above, if you try to navigate to `/customers`, the router returns a `404 Not Found` error. + +To make a route parameter optional, add the [annotationname]`@OptionalParameter` annotation: + +[source,java] +---- +@Route("customers") +public class CustomerView extends Main implements HasUrlParameter { + + @Override +// tag::snippet[] + public void setParameter(BeforeEvent event, @OptionalParameter String parameter) { +// end::snippet[] + if (parameter == null) { + showCustomerList(); + } else { + showCustomerWithId(parameter); + } + } + + private void showCustomerList() { + // ... + } + + private void showCustomerWithId(String customerId) { + // ... + } +} +---- + +Now, if you now navigate to `/customers`, the router calls [methodname]`setParameter()` with `null` as the parameter value. + + +== Wildcard Parameters + +By default, a route parameter captures only a single URL segment. For example, if your `customers` route expects a string parameter, navigating to `/customers/cu1234/edit` would result in a `404 Not Found` error. + +To capture multiple URL segments as a single parameter, use the [annotationname]`@WildcardParameter` annotation: + +[source,java] +---- +@Route("customers") +public class CustomerView extends Main implements HasUrlParameter { + + @Override +// tag::snippet[] + public void setParameter(BeforeEvent event, @WildcardParameter String parameter) { +// end::snippet[] + if (parameter.isEmpty()) { + showCustomerList(); + } else { + // Extract the and process the segments from the parameter + } + } + + private void showCustomerList() { + // ... + } +} +---- + +Now, navigating to `/customers/cu1234/edit` passes `"cu1234/edit"` as the parameter value. + +[NOTE] +An empty wildcard parameter is an empty string (`""`), while an empty optional parameter is `null`. So, navigating to `/customers` calls [methodname]`setParameter()` with `""` instead of `null`. + +If you’re considering wildcard parameters because you need multiple route parameters, <> may be a better solution. diff --git a/articles/building-apps/views/pass-data/route-parameters/flow.adoc b/articles/building-apps/views/pass-data/route-parameters/flow.adoc deleted file mode 100644 index 5ee14245bc..0000000000 --- a/articles/building-apps/views/pass-data/route-parameters/flow.adoc +++ /dev/null @@ -1,259 +0,0 @@ ---- -title: Flow -page-title: How to use route parameters in Flow | Vaadin -meta-description: Learn how to use route parameters in Vaadin Flow to pass data between views, including required, optional, and wildcard parameters for flexible navigation. -order: 5 ---- - - -= Route Parameters in Flow -:toclevels: 2 - -In this guide, you'll learn how to create a view that accepts a single route parameter. You'll also explore the differences between optional and wildcard route parameters. Finally, a mini-tutorial will help you apply these concepts in a real Vaadin application. - - -== Accessing the Route Parameter Value - -To pass a route parameter to a Flow view, implement the [interfacename]`HasUrlParameter` interface. This interface requires a type argument, which can be `Long`, `Integer`, `String`, or `Boolean`. It defines the [methodname]`setParameter()` method, which receives the route parameter when the view is navigated to. - -For example, to create a [classname]`CustomerView` that takes a customer ID as a parameter, use the following code: - -[source,java] ----- -@Route("customers") -public class CustomerView extends Main implements HasUrlParameter { - - @Override - public void setParameter(BeforeEvent event, String parameter) { - setText("Customer ID: " + parameter); - } -} ----- - -Now, if you navigate to `/customers/cu12345`, the router calls [methodname]`setParameter()` with `"cu12345"` as the parameter value. - - -== Optional Parameters - -By default, route parameters are required. In the example above, if you try to navigate to `/customers`, the router returns a `404 Not Found` error. - -To make a route parameter optional, add the [annotationname]`@OptionalParameter` annotation: - -[source,java] ----- -@Route("customers") -public class CustomerView extends Main implements HasUrlParameter { - - @Override -// tag::snippet[] - public void setParameter(BeforeEvent event, @OptionalParameter String parameter) { -// end::snippet[] - if (parameter == null) { - showCustomerList(); - } else { - showCustomerWithId(parameter); - } - } - - private void showCustomerList() { - // ... - } - - private void showCustomerWithId(String customerId) { - // ... - } -} ----- - -Now, if you now navigate to `/customers`, the router calls [methodname]`setParameter()` with `null` as the parameter value. - - -== Wildcard Parameters - -By default, a route parameter captures only a single URL segment. For example, if your `customers` route expects a string parameter, navigating to `/customers/cu1234/edit` would result in a `404 Not Found` error. - -To capture multiple URL segments as a single parameter, use the [annotationname]`@WildcardParameter` annotation: - -[source,java] ----- -@Route("customers") -public class CustomerView extends Main implements HasUrlParameter { - - @Override -// tag::snippet[] - public void setParameter(BeforeEvent event, @WildcardParameter String parameter) { -// end::snippet[] - if (parameter.isEmpty()) { - showCustomerList(); - } else { - // Extract the and process the segments from the parameter - } - } - - private void showCustomerList() { - // ... - } -} ----- - -Now, navigating to `/customers/cu1234/edit` passes `"cu1234/edit"` as the parameter value. - -[NOTE] -An empty wildcard parameter is an empty string (`""`), while an empty optional parameter is `null`. So, navigating to `/customers` calls [methodname]`setParameter()` with `""` instead of `null`. - -If you’re considering wildcard parameters because you need multiple route parameters, <<../route-templates#,Route Templates>> may be a better solution. - - -[.collapsible-list] -== Try It - -In this mini-tutorial, you'll create a view that implements [interfacename]`HasUrlParameter` and experiment with [annotationname]`@OptionalParameter` and [annotationname]`@WildcardParameter`. - - -.Set Up the Project -[%collapsible] -==== -First, generate a new application at http://start.vaadin.com[start.vaadin.com], <> it in your IDE, and <> it with hotswap enabled. -==== - - -.Create the View -[%collapsible] -==== -Create a new package [packagename]`[application package].tutorial.ui.view`, and in it a new class called `ParameterView`: - -.ParameterView.java -[source,java] ----- -import com.vaadin.flow.component.html.Main; -import com.vaadin.flow.router.BeforeEvent; -import com.vaadin.flow.router.HasUrlParameter; -import com.vaadin.flow.router.Route; - -@Route("parameter") -public class ParameterView extends Main implements HasUrlParameter { - - @Override - public void setParameter(BeforeEvent event, String parameter) { - setText("Parameter: " + parameter); - } -} ----- - -Open your browser and go to: http://localhost:8080/parameter/hello - -You should see: - -[source] ----- -Parameter: hello ----- -==== - - -.Make the Parameter Optional -[%collapsible] -==== -Now navigate to: http://localhost:8080/parameter - -You should see a `Could not navigate to 'parameter'` error. - -To fix this, modify the class by adding [annotationname]`@OptionalParameter`: - -.ParameterView.java -[source,java] ----- -import com.vaadin.flow.component.html.Main; -import com.vaadin.flow.router.BeforeEvent; -import com.vaadin.flow.router.HasUrlParameter; -// tag::snippet[] -import com.vaadin.flow.router.OptionalParameter; -// end::snippet[] -import com.vaadin.flow.router.Route; - -@Route("parameter") -public class ParameterView extends Main implements HasUrlParameter { - - @Override -// tag::snippet[] - public void setParameter(BeforeEvent event, @OptionalParameter String parameter) { -// end::snippet[] - setText("Parameter: " + parameter); - } -} ----- - -Refresh the browser at: http://localhost:8080/parameter - -Now you should see: - -[source] ----- -Parameter: null ----- -==== - - -.Capture Multiple Segments with a Wildcard -[%collapsible] -==== -Try navigating to: http://localhost:8080/parameter/hello/world - -You'll get a `Could not navigate to 'parameter/hello/world'` error. - -Now, replace [annotationname]`@OptionalParameter` with [annotationname]`@WildcardParameter`: - -.ParameterView.java -[source,java] ----- -import com.vaadin.flow.component.html.Main; -import com.vaadin.flow.router.BeforeEvent; -import com.vaadin.flow.router.HasUrlParameter; -import com.vaadin.flow.router.Route; -// tag::snippet[] -import com.vaadin.flow.router.WildcardParameter; -// end::snippet[] - -@Route("parameter") -public class ParameterView extends Main implements HasUrlParameter { - - @Override -// tag::snippet[] - public void setParameter(BeforeEvent event, @WildcardParameter String parameter) { -// end::snippet[] - setText("Parameter: " + parameter); - } -} ----- - -Refresh the page, and now you'll see: - -[source] ----- -Parameter: hello/world ----- - -If you navigate to: http://localhost:8080/parameter - -You'll see: - -[source] ----- -Parameter: ----- - -Since it's a wildcard parameter, it is treated as an empty string instead of `null`. -==== - - -.Final Thoughts -[%collapsible] -==== -Now you've successfully implemented route parameters in Flow. You learned how to: - -* Pass a single route parameter to a view. -* Make parameters optional. -* Use wildcard parameters to capture multiple URL segments. - -You're now ready to use route parameters in real Vaadin applications. Try customizing the [classname]`ParameterView` class to do something more than printing the parameter value! -==== diff --git a/articles/building-apps/views/pass-data/route-parameters/hilla.adoc b/articles/building-apps/views/pass-data/route-parameters/hilla.adoc deleted file mode 100644 index a97191b52a..0000000000 --- a/articles/building-apps/views/pass-data/route-parameters/hilla.adoc +++ /dev/null @@ -1,201 +0,0 @@ ---- -title: Hilla -page-title: How to use route parameters in Hilla | Vaadin -meta-description: Learn to use route parameters in Hilla to pass data between views, including required, optional, and wildcard parameters, for dynamic and flexible navigation. -tab-title: Hilla -order: 10 ---- - - -= Route Parameters in Hilla -:toclevels: 2 - -In this guide, you'll learn how to create a view that accepts a single route parameter. You'll also explore the differences between required, optional, and wildcard route parameters. - - -== Required Route Parameters - -To create a Hilla view that accepts a required route parameter, you need to define the parameter name as the view name placed between curly braces (`{}`) followed by the `.tsx` suffix. For example, to create a view that accepts a required `productId` parameter, you should name the view `{productId}.tsx`. The directory structure under the `views` looks like this: - -[source] ----- -views/ -└── products/ - └── {productId}.tsx <1> ----- -<1> The view file that accepts the `productId` parameter. The route is in the form of `/products/{productId}`, where `productId` is a required parameter. Routes such as `/products/123`, `/products/prd222`, etc., match this view. Trying to navigate to `/products` results in a 404 Not Found error unless a view matching the route is defined. - -Though it is technically possible to define the views with parameters directly under the `views` directory, it is recommended to use subdirectories to keep the views organized. - -The routing system also supports having parameters in the middle of the route, which means the directories can also have their names defined with a `{param-name}` convention. Here is an example of a view that accepts a required `productId` parameter: - -[source] ----- -views/ -└── products - └── {productId} <1> - └── edit.tsx ----- -<1> The directory where all its children files have the `productId` parameter in the middle of their routes. The route is in the form of `/products/{productId}/edit`, where `productId` acts as a placeholder for the required parameter, in this case, the product ID. Routes such as `/products/123/edit`, `/products/prd222/edit`, etc., match this view. Trying to navigate to `/products/edit` results in a 404 Not Found error unless a view matching the route is defined. - - -=== Accessing Route Parameter Value - -To access the route parameter in the view, use the `useParams` hook from `react-router` in your component. Here is an example of a view that accepts a required `productId` parameter: - -[source,tsx] -.views/products/{productId}.tsx ----- -// tag::snippet[] -import { useParams } from 'react-router'; -// end::snippet[] - -export default function ProductView() { -// tag::snippet[] - const { productId } = useParams(); <1> -// end::snippet[] - - return ( - <> -

Product Details

-

Product ID: {productId}

- - ); -} ----- -<1> The `useParams` hook returns an object containing the route parameters. In this case, the `productId` parameter is extracted from the object and stored in a variable. - - -=== Passing Route Parameter - -To navigate to a view that accepts a route parameter, use the `NavLink` component from `react-router`. Here is an example of how to create a link to a view located at `/products/123`: - -[source,tsx] ----- -import { NavLink } from 'react-router'; -... -Product 123 ----- - -Another way is to use the `useNavigate` hook to navigate programmatically. Here is an example of how to use `useNavigate` for programmatic navigation: - -[source,tsx] ----- -// tag::snippet[] -import { useNavigate } from 'react-router'; -// end::snippet[] - -function MyComponent() { - const navigate = useNavigate(); - - const handleClick = () => { - navigate('/products/123'); - }; - - return ( - - ); -} ----- - -By using either of the above methods, you can navigate to the `/views/products/{productId}.tsx` view that accepts the `productId` route parameter. If you navigate to `/products/123`, the `productId` parameter is extracted and used in the view to fetch the product details for the given ID. - -If a parameter is expected in the middle of the route, the method for navigating to the view is the same. For example, to navigate to `/products/123/edit`, use the same methods as above. The `useParams` hook extracts the `productId` parameter from the route, and the view fetches the product details for the given ID. - - -== Optional Route Parameters - -Views can also have optional route parameters. This means that the view can be navigated to with or without the parameter. To make a route parameter optional, use double curly braces around the parameter name when naming the view file. For example, to create a view that accepts an optional `categoryName` parameter, name the view `{{categoryName}}.tsx`. The directory structure under the `views` looks like this: - -[source] ----- -views/ -└── products - └── {{categoryName}}.tsx <1> ----- - -<1> The view file that accepts the `categoryName` optional parameter. The route for this view is in the form of `/products/{{categoryName}}`, where `categoryName` is an optional parameter. Routes such as `/products`, `/products/electronics`, `/products/clothing`, etc., match this view. Note that a view with an optional parameter should be able to handle both cases when the parameter is present and when it is not. Here is an example of a view that accepts an optional `categoryName` parameter: - -[source,tsx] -.views/products/{{categoryName}}.tsx ----- -import { useParams } from "react-router"; -import { ProductService } from "Frontend/generated/endpoints.js"; -import { useEffect } from "react"; -import { useSignal } from "@vaadin/hilla-react-signals"; - -export default function ProductByCategoriesView() { -// tag::snippet[] - const { categoryName } = useParams(); <1> -// end::snippet[] - const products = useSignal([]); - - useEffect(() => { -// tag::snippet[] - if (categoryName == undefined) { <2> -// end::snippet[] - ProductService.allProducts().then((data) => products.value = data); - } else { - ProductService.productsInCategory(categoryName).then((data) => products.value = data); - } - }, []); - - return ( - <> -

Products from {categoryName ? `'${categoryName}' category` : "all categories"}:

-
-
    {products.value.map((product) => ( -
  • {product}
  • - ))}
-
- - ); -} ----- -<1> The `useParams` hook returns an object containing the route parameters. In this case, the `categoryName` parameter is extracted from the object and stored in a variable. -<2> The `categoryName` parameter is checked to determine whether it is present or not. If it is not present, all products are fetched. Otherwise, products in the specified category are fetched. - -In the above example, the `ProductByCategoriesView` fetches all products when the `categoryName` parameter is not present. When the `categoryName` parameter is present, it fetches the products in the specified category. The view displays the products in the specified category or all products if the `categoryName` parameter is not present. - - -== Wildcard Route Parameters - -Wildcard route parameters are used to match any number of URL segments. This means when a URL cannot be matched with the other defined routes, the wildcard route is picked as the fallback to handle the navigation. One of the common cases of defining wildcard route parameters when defining the routes in a Hilla application is to properly handle the navigation of users when the route is not found. As the default way of defining the routes is through defining the view files and proper directory structure, adding wildcard route parameters should be done by defining the file name as `{...wildcard}.tsx`. The literal value `wildcard` can be anything that is supported by the filesystem as the filename, but Vaadin recommends to use the `{...wildcard}.tsx` as a conventional standard to make it more readable and intuitive. An example of a view that accepts a wildcard route parameter is shown below: - -[source] ----- -views/ -├── @index.tsx -├── about.tsx -├── contact-us.tsx -└── {...wildcard}.tsx <1> ----- -<1> The view file that accepts the wildcard route parameter. - -The routes that are matched with this view depends on the other defined routes. In this case the `/`, `/about`, and `/contact-us` are mapped to their respective views, and if the user tries to navigate to any other routes such as `/123`, `/orders`, or even `/about/789`, then the `{...wildcard}.tsx` is matched as the fallback, accepting the whole unmatched segment of the URL as the wildcard parameter. The view can then handle the navigation and display a custom 404 Not Found page or redirect the user to the home page. Here is an example of a view that accepts a wildcard route parameter: - -[source, tsx] -.views/{...wildcard}.tsx ----- -import { NavLink, useParams } from "react-router"; - -export default function WildcardView() { - const wildcard = useParams()['*']; <1> - return ( - <> -

Page Not Found!

-
- The '/{wildcard}' route does not exist. - Go back to the home page. -
- - ); -} ----- -<1> The wildcard route parameter can be extracted using the `useParams` hook, which is stored in the `params` object with the `*` (asterisk) as the key. The matched wildcard parameters can have many segments, and all the segments is extracted at once when reading the `params` object with the `*` (asterisk) as the key. - - - -// == Try It diff --git a/articles/building-apps/views/pass-data/route-parameters/index.adoc b/articles/building-apps/views/pass-data/route-parameters/index.adoc deleted file mode 100644 index 91fbe618a3..0000000000 --- a/articles/building-apps/views/pass-data/route-parameters/index.adoc +++ /dev/null @@ -1,39 +0,0 @@ ---- -title: Route Parameters -page-title: How to use route parameters in a Vaadin application -description: Learn how to use route parameters in a Vaadin application. -meta-description: Learn how to use route parameters in Vaadin to pass dynamic data in URLs, personalize responses, and modify application behavior in Flow and Hilla views. -layout: tabbed-page -tab-title: Overview -order: 10 ---- - - -= Route Parameters - -Route parameters are dynamic segments in a URL that allow extra information to be passed to a view. They are appended to the route path and can be used to personalize responses or modify application behavior. - -For example, if an application has a `greet` route that accepts a string parameter, users can call it with URLs like: - -* `/greet/John` -* `/greet/Jane` -* `/greet/World` - -Here, `"John"`, `"Jane"`, and `"World"` are parameter values that the `greet` route can use to generate a response. - - -== Route Parameters vs Static Routes - -Static routes always take precedence over dynamic ones. For instance, consider an application with both a `customer` route (which accepts a string parameter), and a `customer/list` route: - -* `/customer/cu1234` -> Calls the `customer` route with `"cu1234"` as the parameter value. -* `/customer/details` -> Calls the `customer` route with `"details"` as the parameter value. -* `/customer/list` -> Calls the dedicated `customer/list` route. -* `/customer` -> Returns a `404 Not Found` error, unless the parameter is explicitly declared optional. - -== Using Route Parameters in Your Application - -You can use route parameters in both Flow and Hilla views. The following guides show you how to implement them: - -* <> -* <> diff --git a/src/main/java/com/vaadin/demo/buildingapps/passdata/RouteParameterView.java b/src/main/java/com/vaadin/demo/buildingapps/passdata/RouteParameterView.java new file mode 100644 index 0000000000..6ca422c204 --- /dev/null +++ b/src/main/java/com/vaadin/demo/buildingapps/passdata/RouteParameterView.java @@ -0,0 +1,42 @@ +package com.vaadin.demo.buildingapps.passdata; + +import org.jspecify.annotations.Nullable; + +import com.vaadin.flow.component.UI; +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.html.Paragraph; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.BeforeEvent; +import com.vaadin.flow.router.HasUrlParameter; +import com.vaadin.flow.router.OptionalParameter; +import com.vaadin.flow.router.Route; + +// tag::snippet[] +@Route("building-apps/pass-data/route-parameter") +public class RouteParameterView extends VerticalLayout + implements HasUrlParameter { + + private final Paragraph parameterValue; + + RouteParameterView() { + parameterValue = new Paragraph(); + add(parameterValue); + add(new Button("Set Parameter Value", e -> showView("Hello" + + System.currentTimeMillis()))); + } + + @Override + public void setParameter(BeforeEvent event, + @OptionalParameter String parameter) { + if (parameter == null) { + parameterValue.setText("No parameter value provided."); + } else { + parameterValue.setText("Parameter value: " + parameter); + } + } + + public static void showView(@Nullable String parameterValue) { + UI.getCurrent().navigate(RouteParameterView.class, parameterValue); + } +} +// end::snippet[] \ No newline at end of file From 73595e68ad9dcd521acecd7536185a834de59db2 Mon Sep 17 00:00:00 2001 From: Petter Holmstrom Date: Wed, 26 Nov 2025 17:36:53 +0200 Subject: [PATCH 04/13] Edit Route Templates --- .../views/pass-data/route-templates.adoc | 226 ++---------------- .../passdata/RouteTemplateView.java | 51 ++++ 2 files changed, 69 insertions(+), 208 deletions(-) create mode 100644 src/main/java/com/vaadin/demo/buildingapps/passdata/RouteTemplateView.java diff --git a/articles/building-apps/views/pass-data/route-templates.adoc b/articles/building-apps/views/pass-data/route-templates.adoc index 8a917c8128..f16bedb085 100644 --- a/articles/building-apps/views/pass-data/route-templates.adoc +++ b/articles/building-apps/views/pass-data/route-templates.adoc @@ -1,20 +1,31 @@ --- title: Route Templates -page-title: How to use route templates in Flow | Vaadin -description: Learn how to use route templates in Flow. -meta-description: Learn to use route templates in Flow to handle multiple route parameters, optional and wildcard parameters, and constrain values with regular expressions. -section-nav: badge-flow +page-title: How to use route templates in Vaadin +description: Learn how to use route templates in Vaadin. +meta-description: Learn to use route templates in Vaadin to handle multiple route parameters, optional and wildcard parameters, and constrain values with regular expressions. order: 20 --- -= Route Templates [badge-flow]#Flow# += Route Templates :toclevels: 2 -In this guide, you'll learn how to create a view that accepts multiple route parameters using route templates. You'll also learn how to use modifiers and regular expressions to tweak the behavior of the route parameters. Finally, a mini-tutorial will help you apply these concepts in a real Vaadin application. +In this guide, you'll learn how to create a view that accepts multiple route parameters using route templates. You'll also learn how to use modifiers and regular expressions to tweak the behavior of the route parameters. +.Copy-Paste into Your Project [TIP] -Using a single <> is easier than using a route template. If you can get the job done using the [interfacename]`HasUrlParameter` interface, use that instead. +==== +If you want to quickly try out route templates in your Vaadin application, copy-paste the following code into a new Java class named `RouteTemplateView` in your project's main package: + +[source,java] +---- +include::{root}/src/main/java/com/vaadin/demo/buildingapps/passdata/RouteTemplateView.java[tags=snippet,indent=0] +---- + +For more detailed instructions on how to use route templates, continue reading below. +==== + +Using a single <> is easier than using a route template. If you can get the job done using the [interfacename]`HasUrlParameter` interface, use that instead. == Specifying Multiple Route Parameters @@ -173,204 +184,3 @@ public class CustomerView extends Main { If you navigate to a URL that doesn't meet these constraints, you'll receive a `404 Not Found` error. When you specify constraints on wildcard route parameters, the regular expression is applied to every segment that would be captured by the route parameter. If any of the segments fails to match the expression, the whole route template fails to match the URL, and you'll get a `404 Not Found` error. - - -[.collapsible-list] -== Try It - -In this mini-tutorial, you'll create a view that uses a route template. You'll then change the route template and see how the view behaves. - - -.Set Up the Project -[%collapsible] -==== -First, generate a new application at http://start.vaadin.com[start.vaadin.com], <> it in your IDE, and <> it with hotswap enabled. -==== - - -.Create the View -[%collapsible] -==== -Create a new package [packagename]`[application package].tutorial.ui.view`. Then, in this package, create a new class called `TemplateView`: - -.TemplateView.java -[source,java] ----- -import com.vaadin.flow.component.html.Main; -import com.vaadin.flow.router.BeforeEnterEvent; -import com.vaadin.flow.router.BeforeEnterObserver; -import com.vaadin.flow.router.Route; - -@Route("template") -public class TemplateView extends Main implements BeforeEnterObserver { - - private static final String PARAM1 = "param1"; - private static final String PARAM2 = "param2"; - private static final String PARAM3 = "param3"; - - @Override - public void beforeEnter(BeforeEnterEvent event) { - var param1 = event.getRouteParameters().get(PARAM1).orElse("N/A"); - var param2 = event.getRouteParameters().get(PARAM2).orElse("N/A"); - var param3 = event.getRouteParameters().get(PARAM3).orElse("N/A"); - setText("param1: \"" + param1 + "\", param2: \"" + param2 - + "\", param3: \"" + param3 + "\""); - } -} ----- - -Open your browser and go to: http://localhost:8080/template - -You should see: - -[source] ----- -param1: "N/A", param2: "N/A", param3: "N/A" ----- -==== - - -.Define Required Route Parameters -[%collapsible] -==== -Next, introduce the three route parameters, like this: - -[source,java] ----- -// tag::snippet[] -@Route("template/:param1/:param2/:param3") -// end::snippet[] -public class TemplateView extends Main implements BeforeEnterObserver { - ... -} ----- - -Now, go back to your browser. Because the route parameters are required and you haven't provided any values, you should see a `Could not navigate to 'template'` error message. - -To give values for the route parameters, navigate to this URL: - -http://localhost:8080/template/say/hello/vaadin - -You should see: - -[source] ----- -param1: "say", param2: "hello", param3: "vaadin" ----- -==== - - -.Make Route Parameters Optional -[%collapsible] -==== -You're now going to make the first two route parameters optional. Add the `?` modifier to them: - -[source,java] ----- -// tag::snippet[] -@Route("template/:param1?/:param2?/:param3") -// end::snippet[] -public class TemplateView extends Main implements BeforeEnterObserver { - ... -} ----- - -Go back to your browser. You should still see all three route parameter values. However, watch what happens if you go to: - -http://localhost:8080/template/say - -Since the first two parameters are optional, the last parameter takes the first available value. You should see: - -[source] ----- -param1: "N/A", param2: "N/A", param3: "say" ----- - -Now add a second route parameter value by navigating to: - -http://localhost:8080/template/say/hello - -You should see: - -[source] ----- -param1: "say", param2: "N/A", param3: "hello" ----- -==== - - -.Mark a Route Parameter as Wildcard -[%collapsible] -==== -You're now going to make the last route parameter a wildcard. Add the `*` modifier to it: - -[source,java] ----- -// tag::snippet[] -@Route("template/:param1?/:param2?/:param3*") -// end::snippet[] -public class TemplateView extends Main implements BeforeEnterObserver { - ... -} ----- - -Go back to the browser. If you reload the page, you should now see the following: - -[source] ----- -param1: "say", param2: "hello", param3: "N/A" ----- - -The `"hello"` string has moved from `param3` to `param2`. The reason for this is that wildcard route parameters are also optional. Next, navigate to the following URL: - -http://localhost:8080/template/say/hello/to/vaadin - -The last route parameter now holds two segments instead of one. You should see: - -[source] ----- -param1: "say", param2: "hello", param3: "to/vaadin" ----- -==== - - -.Add Regular Expressions -[%collapsible] -==== -You're now going to add some regular expressions to constrain the route parameters. You'll force `param1` to consist of digits only, and all the segments of `param3` to be one of `hello`, `to`, and `vaadin`: - -[source,java] ----- -// tag::snippet[] -@Route("template/:param1?([0-9]*)/:param2?/:param3*(hello|to|vaadin)") -// end::snippet[] -public class TemplateView extends Main implements BeforeEnterObserver { - ... -} ----- - -Go back to the browser. You should now see a `Could not navigate to 'template/say/hello/to/vaadin`. This is because the first route parameter value is not a number. To fix this, navigate to: - -http://localhost:8080/template/123/say/hello/to/vaadin - -You should see: - -[source] ----- -param1: "123", param2: "say", param3: "hello/to/vaadin" ----- -==== - - -.Final Thoughts -[%collapsible] -==== -You've now successfully implemented route templates in Flow. You learned how to: - -* Specify multiple route parameters in a view. -* Make route parameters optional. -* Use a wildcard route parameter to capture multiple URL segments. -* Use regular expressions to constrain the values of route parameters. - -You're now ready to use route parameters in real Vaadin applications. Try specifying your own route template, and make a <<../navigate/flow#your-own-api,custom API>> for navigating to it! Include at least one numeric route parameter, and use [methodname]`RouteParameters.getInteger()` to retrieve it. -==== diff --git a/src/main/java/com/vaadin/demo/buildingapps/passdata/RouteTemplateView.java b/src/main/java/com/vaadin/demo/buildingapps/passdata/RouteTemplateView.java new file mode 100644 index 0000000000..e280845b27 --- /dev/null +++ b/src/main/java/com/vaadin/demo/buildingapps/passdata/RouteTemplateView.java @@ -0,0 +1,51 @@ +package com.vaadin.demo.buildingapps.passdata; + +import com.vaadin.flow.component.UI; +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.html.Paragraph; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.BeforeEnterEvent; +import com.vaadin.flow.router.BeforeEnterObserver; +import com.vaadin.flow.router.Route; +import com.vaadin.flow.router.RouteParam; +import com.vaadin.flow.router.RouteParameters; + +// tag::snippet[] +@Route("building-apps/pass-data/route-template/:id([0-9]{1,9})/:action?(view|edit)") +public class RouteTemplateView extends VerticalLayout implements BeforeEnterObserver { + + public enum Action { + VIEW, EDIT + } + + private final Paragraph parameterValues; + + RouteTemplateView() { + parameterValues = new Paragraph(); + add(parameterValues); + add(new Button("Set Parameter Values", e -> showView( + (int) (Math.random() * 1000), + Math.random() < 0.5 ? Action.VIEW : Action.EDIT))); + } + + @Override + public void beforeEnter(BeforeEnterEvent event) { + var id = event.getRouteParameters() + .getInteger("id") + .orElseThrow(); + var action = event.getRouteParameters() + .get("action") + .map(a -> Action.valueOf(a.toUpperCase())) + .orElse(Action.VIEW); + parameterValues.setText("Parameter values: id = " + id + + ", action = " + action); + } + + public static void showView(int id, Action action) { + var parameters = new RouteParameters( + new RouteParam("id", id), + new RouteParam("action", action.name())); + UI.getCurrent().navigate(RouteTemplateView.class, parameters); + } +} +// end::snippet[] \ No newline at end of file From 1a0d036ce7585d66870ea7397cafa150f2d8309f Mon Sep 17 00:00:00 2001 From: Petter Holmstrom Date: Wed, 26 Nov 2025 17:42:40 +0200 Subject: [PATCH 05/13] Edit Query Parameters (WIP) --- .../views/pass-data/query-parameters.adoc | 149 ++++++++++ .../pass-data/query-parameters/flow.adoc | 273 ------------------ .../pass-data/query-parameters/hilla.adoc | 148 ---------- .../pass-data/query-parameters/index.adoc | 34 --- .../passdata/QueryParameterView.java | 21 ++ 5 files changed, 170 insertions(+), 455 deletions(-) create mode 100644 articles/building-apps/views/pass-data/query-parameters.adoc delete mode 100644 articles/building-apps/views/pass-data/query-parameters/flow.adoc delete mode 100644 articles/building-apps/views/pass-data/query-parameters/hilla.adoc delete mode 100644 articles/building-apps/views/pass-data/query-parameters/index.adoc create mode 100644 src/main/java/com/vaadin/demo/buildingapps/passdata/QueryParameterView.java diff --git a/articles/building-apps/views/pass-data/query-parameters.adoc b/articles/building-apps/views/pass-data/query-parameters.adoc new file mode 100644 index 0000000000..b44e415cb6 --- /dev/null +++ b/articles/building-apps/views/pass-data/query-parameters.adoc @@ -0,0 +1,149 @@ +--- +title: Query Parameters +page-title: How to use query parameters in a Vaadin application +description: Learn how to manage state with query parameters in Vaadin. +meta-description: Learn how to use query parameters in Vaadin to store view state, manage sorting and filtering, and enhance navigation. +order: 30 +--- + + += Query Parameters +:toclevels: 2 + +In this guide, you'll learn how to access and set query parameters in a Vaadin view. + +.Copy-Paste into Your Project +[TIP] +==== +If you want to quickly try out query parameters in your Vaadin application, copy-paste the following code into a new Java class named [classname]`QueryParameterView` in your project's main package: + +[source,java] +---- +include::{root}/src/main/java/com/vaadin/demo/buildingapps/passdata/QueryParameterView.java[tags=snippet,indent=0] +---- + +For more detailed instructions on how to use route parameters, continue reading below. +==== + + +== What Are Query Parameters? + +Query parameters are key-value pairs appended to the end of a URL after a `?` character. Multiple parameters are separated by `&`. + +For example, this URL: `/orders?sort=date&filter=shipped` + +Contains the following query parameters: + +* `sort` -> `"date"` +* `filter` -> `"shipped"` + +Query parameters are commonly used to store view-related state, such as a grid's sort order or a search field's value. Since they are optional, always provide default values when a parameter is missing. + +[TIP] +If you need required parameters, consider using <> instead. + + +== Accessing Query Parameter Values + +You access query parameters similarly to <>. Your view must implement the [interfacename]`BeforeEnterObserver` interface, which defines the [methodname]`beforeEnter()` method. Inside this method, you can retrieve query parameters: + +[source,java] +---- +@Route +public class OrdersView extends Main implements BeforeEnterObserver { + + private static final String QUERY_PARAM_SORT = "sort"; // <1> + private static final String QUERY_PARAM_FILTER = "filter"; + + @Override + public void beforeEnter(BeforeEnterEvent event) { + event.getLocation().getQueryParameters().getSingleParameter(QUERY_PARAM_SORT) + .ifPresent(sort -> { + // Process the sort parameter + }); + event.getLocation().getQueryParameters().getSingleParameter(QUERY_PARAM_FILTER) + .ifPresent(filter -> { + // Process the filter parameter + }); + } + ... +} +---- +<1> *Tip:* To improve readability and maintainability, declare query parameter names as constants. + +Now, if you navigate to `/orders?sort=date&filter=shipped`, the query parameter values will be: + +* `sort` -> `"date"` +* `filter` -> `"shipped"` + +The [classname]`QueryParameters` class provides methods for accessing query parameter values. Familiarize yourself with its API if you frequently use query parameters in your application. + +[NOTE] +You can also retrieve query parameters using [methodname]`UI.getCurrent().getActiveViewLocation().getQueryParameters()`. + +Since query parameters are always strings and optional, they may be empty or contain invalid data. To handle this, you can provide default values, redirect users to a different view, or display an error message. + + +== Setting Query Parameters + +The [classname]`QueryParameters` class is immutable, meaning you can't modify existing query parameters. Instead, you must create a new [classname]`QueryParameters` object and pass it to [methodname]`UI.navigate()`. Query parameters are set when navigating to a view. + +In the following example, [classname]`OrdersView` provides a <<../navigate#your-own-api,custom API>> for navigating to itself while setting the `filter` query parameter: + +[source,java] +---- +@Route +public class OrdersView extends Main implements BeforeEnterObserver { + + private static final String QUERY_PARAM_SORT = "sort"; + private static final String QUERY_PARAM_FILTER = "filter"; + +// tag::snippet[] + public static void showOrders(@Nullable String filter) { + var queryParameters = filter == null ? QueryParameters.empty() + : QueryParameters.of(QUERY_PARAM_FILTER, filter); + UI.getCurrent().navigate(OrdersView.class, queryParameters); + } +// end::snippet[] + ... +} +---- + +[IMPORTANT] +You _cannot_ set query parameters to `null`. To clear a query parameter, exclude it from the [classname]`QueryParameters` object. + + +=== Updating Query Parameters Dynamically + +Query parameters are often set dynamically, such as when users apply filters or change sort options. In such cases, the view must navigate to itself while updating query parameters. + +When navigating to the _same_ view, the existing view instance is reused. The browser URL updates, and [methodname]`beforeEnter()` is invoked again. + +The following example updates the `filter` query parameter when a user types in a text field: + +[source,java] +---- +@Route +public class OrdersView extends Main implements BeforeEnterObserver { + + private static final String QUERY_PARAM_SORT = "sort"; + private static final String QUERY_PARAM_FILTER = "filter"; + +// tag::snippet[] + public OrdersView() { + var filterField = new TextField(); + add(filterField); + + filterField.addValueChangeListener(event -> { + var queryParameters = UI.getCurrent().getActiveViewLocation() + .getQueryParameters() + .merging(QUERY_PARAM_FILTER, event.getValue()); + UI.getCurrent().navigate(OrdersView.class, queryParameters); + }); + } +// end::snippet[] + ... +} +---- + +If the original URL was `/orders?sort=date&filter=shipped`, and the user enters "foobar" in the text field, the URL updates to `/orders?sort=date&filter=foobar`. diff --git a/articles/building-apps/views/pass-data/query-parameters/flow.adoc b/articles/building-apps/views/pass-data/query-parameters/flow.adoc deleted file mode 100644 index 5b8d0f4ca9..0000000000 --- a/articles/building-apps/views/pass-data/query-parameters/flow.adoc +++ /dev/null @@ -1,273 +0,0 @@ ---- -title: Flow -page-title: How to use query parameters in Flow | Vaadin -meta-description: Learn how to access, set, and dynamically update query parameters in Vaadin Flow to manage view state, filters, and sorting in your applications. -order: 5 ---- - - -= Query Parameters in Flow -:toclevels: 2 - -In this guide, you'll learn how to access and set query parameters in a Flow view. A mini-tutorial at the end will help you apply these concepts in a real Vaadin application. - - -== Accessing Query Parameter Values - -You access query parameters similarly to <<../route-templates#accessing-route-parameter-values,multiple route parameters>>. Your view must implement the [interfacename]`BeforeEnterObserver` interface, which defines the [methodname]`beforeEnter()` method. Inside this method, you can retrieve query parameters: - -[source,java] ----- -@Route -public class OrdersView extends Main implements BeforeEnterObserver { - - private static final String QUERY_PARAM_SORT = "sort"; // <1> - private static final String QUERY_PARAM_FILTER = "filter"; - - @Override - public void beforeEnter(BeforeEnterEvent event) { - event.getLocation().getQueryParameters().getSingleParameter(QUERY_PARAM_SORT) - .ifPresent(sort -> { - // Process the sort parameter - }); - event.getLocation().getQueryParameters().getSingleParameter(QUERY_PARAM_FILTER) - .ifPresent(filter -> { - // Process the filter parameter - }); - } - ... -} ----- -<1> *Tip:* To improve readability and maintainability, declare query parameter names as constants. - -Now, if you navigate to `/orders?sort=date&filter=shipped`, the query parameter values will be: - -* `sort` -> `"date"` -* `filter` -> `"shipped"` - -The [classname]`QueryParameters` class provides methods for accessing query parameter values. Familiarize yourself with its API if you frequently use query parameters in your application. - -[NOTE] -You can also retrieve query parameters using [methodname]`UI.getCurrent().getActiveViewLocation().getQueryParameters()`. - -Since query parameters are always strings and optional, they may be empty or contain invalid data. To handle this, you can provide default values, redirect users to a different view, or display an error message. - - -== Setting Query Parameters - -The [classname]`QueryParameters` class is immutable, meaning you can't modify existing query parameters. Instead, you must create a new [classname]`QueryParameters` object and pass it to [methodname]`UI.navigate()`. Query parameters are set when navigating to a view. - -In the following example, [classname]`OrdersView` provides a <<../../navigate/flow#your-own-api,custom API>> for navigating to itself while setting the `filter` query parameter: - -[source,java] ----- -@Route -public class OrdersView extends Main implements BeforeEnterObserver { - - private static final String QUERY_PARAM_SORT = "sort"; - private static final String QUERY_PARAM_FILTER = "filter"; - -// tag::snippet[] - public static void showOrders(@Nullable String filter) { - var queryParameters = filter == null ? QueryParameters.empty() - : QueryParameters.of(QUERY_PARAM_FILTER, filter); - UI.getCurrent().navigate(OrdersView.class, queryParameters); - } -// end::snippet[] - ... -} ----- - -[IMPORTANT] -You _cannot_ set query parameters to `null`. To clear a query parameter, exclude it from the [classname]`QueryParameters` object. - - -=== Updating Query Parameters Dynamically - -Query parameters are often set dynamically, such as when users apply filters or change sort options. In such cases, the view must navigate to itself while updating query parameters. - -When navigating to the _same_ view, the existing view instance is reused. The browser URL updates, and [methodname]`beforeEnter()` is invoked again. - -The following example updates the `filter` query parameter when a user types in a text field: - -[source,java] ----- -@Route -public class OrdersView extends Main implements BeforeEnterObserver { - - private static final String QUERY_PARAM_SORT = "sort"; - private static final String QUERY_PARAM_FILTER = "filter"; - -// tag::snippet[] - public OrdersView() { - var filterField = new TextField(); - add(filterField); - - filterField.addValueChangeListener(event -> { - var queryParameters = UI.getCurrent().getActiveViewLocation() - .getQueryParameters() - .merging(QUERY_PARAM_FILTER, event.getValue()); - UI.getCurrent().navigate(OrdersView.class, queryParameters); - }); - } -// end::snippet[] - ... -} ----- - -If the original URL was `/orders?sort=date&filter=shipped`, and the user enters "foobar" in the text field, the URL updates to `/orders?sort=date&filter=foobar`. - - -[.collapsible-list] -== Try It - -In this mini-tutorial, you'll create a view that accesses and dynamically updates two query parameters. - - -.Set Up the Project -[%collapsible] -==== -First, generate a new application at http://start.vaadin.com[start.vaadin.com], <> it in your IDE, and <> it with hotswap enabled. -==== - - -.Create the View -[%collapsible] -==== -Create a new package [packagename]`[application package].tutorial.ui.view`. Then, create a class named [classname]`QueryParameterView`: - -.QueryParameterView.java -[source,java] ----- -import com.vaadin.flow.component.checkbox.Checkbox; -import com.vaadin.flow.component.html.Main; -import com.vaadin.flow.component.textfield.TextField; -import com.vaadin.flow.router.BeforeEnterEvent; -import com.vaadin.flow.router.BeforeEnterObserver; -import com.vaadin.flow.router.Route; - -@Route -public class QueryParameterView extends Main implements BeforeEnterObserver { - - private static final String QUERY_PARAMETER_TEXT = "text"; - private static final String QUERY_PARAMETER_CHECK = "check"; - - private final TextField textField; - private final Checkbox checkbox; - - public QueryParameterView() { - textField = new TextField(); - checkbox = new Checkbox(); - add(textField, checkbox); - } - - @Override - public void beforeEnter(BeforeEnterEvent event) { - } -} ----- - -Open your browser and navigate to: http://localhost:8080/queryparameter - -You should see an empty text field and an unchecked checkbox. -==== - - -.Access Query Parameters -[%collapsible] -==== -Modify the [methodname]`beforeEnter()` method to populate the components: - -[source,java] ----- -@Override -public void beforeEnter(BeforeEnterEvent event) { - var queryParameters = event.getLocation().getQueryParameters(); - queryParameters.getSingleParameter(QUERY_PARAMETER_TEXT) - .ifPresent(textField::setValue); - queryParameters.getSingleParameter(QUERY_PARAMETER_CHECK) - .map(Boolean::parseBoolean).ifPresent(checkbox::setValue); -} ----- - -Navigate to: http://localhost:8080/queryparameter?text=hello+world&check=true - -The text field should now contain `"hello world"` and the checkbox should be checked. -==== - - -.Update the `text` Query Parameter -[%collapsible] -==== -Modify the constructor to update the query parameter dynamically: - -[source,java] ----- -public QueryParameterView() { - textField = new TextField(); -// tag::snippet[] - textField.setValueChangeMode(ValueChangeMode.LAZY); // <1> - textField.addValueChangeListener(event -> { - var queryParameters = UI.getCurrent().getActiveViewLocation() - .getQueryParameters().merging(QUERY_PARAMETER_TEXT, event.getValue()); - UI.getCurrent().navigate(QueryParameterView.class, queryParameters); - }); -// end::snippet[] - checkbox = new Checkbox(); - add(textField, checkbox); -} ----- -<1> The field updates 400 ms after the user stops typing. - -Try changing the text field value in the browser. The URL updates automatically. -==== - - -.Update the `check` Query Parameter -[%collapsible] -==== -Add a listener for the checkbox: - -[source,java] ----- -public QueryParameterView() { - textField = new TextField(); - textField.setValueChangeMode(ValueChangeMode.LAZY); // <1> - textField.addValueChangeListener(event -> { - var queryParameters = UI.getCurrent().getActiveViewLocation() - .getQueryParameters() - .merging(QUERY_PARAMETER_TEXT, event.getValue()); - UI.getCurrent().navigate(QueryParameterView.class, queryParameters); - }); - checkbox = new Checkbox(); -// tag::snippet[] - checkbox.addValueChangeListener(event -> { - var queryParameters = UI.getCurrent().getActiveViewLocation() - .getQueryParameters() - .merging(QUERY_PARAMETER_CHECK, event.getValue().toString()); - UI.getCurrent().navigate(QueryParameterView.class, queryParameters); - }); -// end::snippet[] - add(textField, checkbox); -} ----- - -Try toggling the checkbox. The URL updates automatically while preserving the `text` parameter. -==== - - -.Final Thoughts -[%collapsible] -==== -You've now successfully implemented query parameters in Flow. You learned how to: - -* Access query parameter values. -* Update query parameter values dynamically. - -Next, try modifying the code to: - -* Use `1` and `0` instead of `true` and `false` for the `check` parameter. -* Validate the `text` query parameter to allow only *letters, digits and whitespace*. - -You're now ready to use query parameters in real Vaadin applications! -==== diff --git a/articles/building-apps/views/pass-data/query-parameters/hilla.adoc b/articles/building-apps/views/pass-data/query-parameters/hilla.adoc deleted file mode 100644 index 05eb5249d0..0000000000 --- a/articles/building-apps/views/pass-data/query-parameters/hilla.adoc +++ /dev/null @@ -1,148 +0,0 @@ ---- -title: Hilla -page-title: How to use query parameters in Hilla | Vaadin -meta-description: Learn how to use query parameters in Hilla to manage view state, handle search and filtering, and enhance navigation in Hilla applications. -order: 10 ---- - - -= Query Parameters in Hilla -:toclevels: 2 - -In this guide, you'll learn how to access and set query parameters in a Hilla view using React. - - -== Using Query Parameters - -To work with query parameters in Hilla views, you can use the `useSearchParams` hook from `react-router`. This hook provides methods to read, update, and delete query parameters without causing a full page reload. - -The `useSearchParams` hook returns an array with two elements: - -- `searchParams` -- a `URLSearchParams` object for reading parameters. -- `setSearchParams` -- a function to update or clear query parameters. - -Here's an example of a view that manages a search term using query parameters: - -[source,tsx] ----- -// tag::snippet[] -import { useSearchParams } from 'react-router'; -// end::snippet[] -import { TextField } from '@vaadin/react-components/TextField.js'; - -export default function ProductView() { -// tag::snippet[] - const [searchParams, setSearchParams] = useSearchParams(); - const searchTerm = searchParams.get('category') || ''; -// end::snippet[] - return ( -
- { - const newValue = e.detail.value; - if (newValue) { -// tag::snippet[] - setSearchParams({ category: newValue }); -// end::snippet[] - } else { -// tag::snippet[] - setSearchParams({}); -// end::snippet[] - } - }} - /> -
Current search term: {searchTerm}
-
- ); -} ----- - -In this example, the `category` parameter is used to store the search term. When a user types a new value, `setSearchParams` updates the query parameter accordingly. If the value is empty, passing an empty object to `setSearchParams` clears all query parameters. - - -== Multiple Query Parameters - -You can work with multiple query parameters simultaneously. In the following example, a view manages both a search filter (`category`) and a sorting order (`sort`) while ensuring any other query parameters are preserved when updating values: - -[source,tsx] ----- -import { useSearchParams } from 'react-router'; -import { TextField } from '@vaadin/react-components/TextField.js'; -import { Select } from "@vaadin/react-components"; - -export default function ProductView() { - const [searchParams, setSearchParams] = useSearchParams(); - const category = searchParams.get('category') || ''; - const sortOrder = searchParams.get('sort') || 'asc'; - - // Function to update query parameters while preserving existing ones - const updateParams = (params: Record) => { - setSearchParams({ - ...Object.fromEntries(searchParams), - ...params - }); - }; - - // Ensure URL parameters reflect the current state - if (category !== searchParams.get('category') - || sortOrder !== searchParams.get('sort')) { - updateParams({ category: category, sort: sortOrder }); - } - - return ( -
- updateParams({ category: e.detail.value })} - /> -