diff --git a/articles/building-apps/security/add-login/flow.adoc b/articles/building-apps/security/add-login/flow.adoc index 01cfb32c52..ee66c86025 100644 --- a/articles/building-apps/security/add-login/flow.adoc +++ b/articles/building-apps/security/add-login/flow.adoc @@ -263,18 +263,32 @@ public final class MainLayout extends AppLayout { } ---- -.TodoView.java +.MainView.java [source,java] ---- import jakarta.annotation.security.PermitAll; -@Route("") +@Route +// tag::snippet[] +@PermitAll +// end::snippet[] +public final class MainView extends Main { + ... +} +---- + +.TaskListView.java +[source,java] +---- +import jakarta.annotation.security.PermitAll; + +@Route("task-list") @PageTitle("Task List") @Menu(order = 0, icon = "vaadin:clipboard-check", title = "Task List") // tag::snippet[] @PermitAll // end::snippet[] -public class TodoView extends Main { +public class TaskListView extends Main { ... } ---- @@ -295,7 +309,7 @@ You should now see the login screen. Login with one of the following credentials * *User:* user / *Password:* password * *Admin:* admin / *Password:* admin -After logging in, you should be able to access the todo view. +After logging in, you should be able to access the task list view. ==== diff --git a/articles/building-apps/security/add-login/hilla.adoc b/articles/building-apps/security/add-login/hilla.adoc index cfb1facc41..3c81738c57 100644 --- a/articles/building-apps/security/add-login/hilla.adoc +++ b/articles/building-apps/security/add-login/hilla.adoc @@ -442,7 +442,7 @@ You should now see the login screen. Login with one of the following credentials * *User:* user / *Password:* password * *Admin:* admin / *Password:* admin -After logging in, you should be able to access the todo view. +After logging in, you should be able to access the task list view. ==== diff --git a/articles/building-apps/security/add-logout/hilla.adoc b/articles/building-apps/security/add-logout/hilla.adoc index b969dc61b3..255c9f51be 100644 --- a/articles/building-apps/security/add-logout/hilla.adoc +++ b/articles/building-apps/security/add-logout/hilla.adoc @@ -85,7 +85,7 @@ In this mini-tutorial, you'll add logout functionality to a real Vaadin applicat ==== Import `useAuth` into `src/main/frontend/views/@layout.tsx`: -.@layout.tsx +.frontend/views/@layout.tsx [source,tsx] ---- import {useAuth} from "Frontend/security/auth"; @@ -99,7 +99,7 @@ import {useAuth} from "Frontend/security/auth"; ==== The *user menu* in `@layout.tsx` already contains a *logout item*, but it does nothing. Modify it to call `logout()` when clicked: -.@layout.tsx +.frontend/views/@layout.tsx [source,tsx] ---- ... diff --git a/articles/building-apps/security/protect-services/flow.adoc b/articles/building-apps/security/protect-services/flow.adoc index d1fafb6527..d9014a2c51 100644 --- a/articles/building-apps/security/protect-services/flow.adoc +++ b/articles/building-apps/security/protect-services/flow.adoc @@ -117,14 +117,14 @@ class SecurityConfig extends VaadinWebSecurity { ==== -.Secure the Todo Service +.Secure the Task Service [%collapsible] ==== In an earlier tutorial, you made the task list read-only for users, allowing only admins to create tasks. -Open [classname]`TodoService` and add [annotationname]`@PreAuthorize` annotations like this: +Open [classname]`TaskService` and add [annotationname]`@PreAuthorize` annotations like this: -.TodoService.java +.TaskService.java [source,java] ---- @Service @@ -132,17 +132,17 @@ Open [classname]`TodoService` and add [annotationname]`@PreAuthorize` annotation @PreAuthorize("isAuthenticated()") // end::snippet[] @Transactional(propagation = Propagation.REQUIRES_NEW) -public class TodoService { +public class TaskService { ... // tag::snippet[] @PreAuthorize("hasRole('" + Roles.ADMIN + "')") // end::snippet[] - public void createTodo(String description, @Nullable LocalDate dueDate) { + public void createTask(String description, @Nullable LocalDate dueDate) { // ... } - public List list(Pageable pageable) { + public List list(Pageable pageable) { // ... } } @@ -154,21 +154,21 @@ Log in as `ADMIN` and create some tasks. Everything should work as before. ==== -.Break the Task List +.Break the Task List View [%collapsible] ==== -To see that the service is actually protected, you're going to break the task list. Open [classname]`TodoView` and comment out the lines that check whether the user is an admin or not: +To see that the service is actually protected, you're going to break the task list. Open [classname]`TaskListView` and comment out the lines that check whether the user is an admin or not: -.TodoView.java +.TaskListView.java [source,java] ---- -@Route("") +@Route("task-list") @PageTitle("Task List") @Menu(order = 0, icon = "vaadin:clipboard-check", title = "Task List") @PermitAll -public class TodoView extends Main { +public class TaskListView extends Main { - public TodoView(TodoService todoService, Clock clock, + public TaskListView(TaskService taskService, Clock clock, AuthenticationContext authenticationContext) { // The rest of the constructor omitted @@ -183,14 +183,14 @@ public class TodoView extends Main { // add(new ViewToolbar("Task List")); //} // end::snippet[] - add(todoGrid); + add(taskGrid); } ... } ---- Then go back to the browser, logout, and login as `USER`. If you now try to create a task, you should get an error message. -Now change `TodoView()` back again by removing the comments. +Now change `TaskListView()` back again by removing the comments. // TODO This should be replaced with an integration test that checks the security. ==== diff --git a/articles/building-apps/security/protect-services/hilla.adoc b/articles/building-apps/security/protect-services/hilla.adoc index b05d942d1e..374f0d6c83 100644 --- a/articles/building-apps/security/protect-services/hilla.adoc +++ b/articles/building-apps/security/protect-services/hilla.adoc @@ -194,14 +194,14 @@ class SecurityConfig extends VaadinWebSecurity { ==== -.Secure the Todo Service +.Secure the Task Service [%collapsible] ==== In an earlier tutorial, you made the task list read-only for users, allowing only admins to create tasks. -Open [classname]`TodoService` and replace [annotationname]`@AnonymousAllowed` with [annotationname]`@PermitAll`. Then, add [annotationname]`@RolesAllowed` to `createTodo()`: +Open [classname]`TaskService` and replace [annotationname]`@AnonymousAllowed` with [annotationname]`@PermitAll`. Then, add [annotationname]`@RolesAllowed` to `createTask()`: -.TodoService.java +.TaskService.java [source,java] ---- @BrowserCallable @@ -209,17 +209,17 @@ Open [classname]`TodoService` and replace [annotationname]`@AnonymousAllowed` wi @PermitAll // end::snippet[] @Transactional(propagation = Propagation.REQUIRES_NEW) -public class TodoService { +public class TaskService { ... // tag::snippet[] @RolesAllowed(Roles.ADMIN) // end::snippet[] - public void createTodo(String description, @Nullable LocalDate dueDate) { + public void createTask(String description, @Nullable LocalDate dueDate) { //... } - public List list(Pageable pageable) { + public List list(Pageable pageable) { // ... } } @@ -234,16 +234,16 @@ Log in as `ADMIN` and create some tasks. Everything should work as before. .Break the Task List [%collapsible] ==== -To see that the service is actually protected, you're going to break the task list. Open `src/main/frontend/views/@index.tsx` and change `TodoView()` so that `isAdmin` is always `true`: +To see that the service is actually protected, you're going to break the task list. Open `src/main/frontend/views/task-list.tsx` and change `TaskListView()` so that `isAdmin` is always `true`: -.frontend/views/@index.tsx +.frontend/views/task-list.tsx [source,tsx] ---- ... -export default function TodoView() { - const dataProvider = useDataProvider({ - list: (pageable) => TodoService.list(pageable), +export default function TaskListView() { + const dataProvider = useDataProvider({ + list: (pageable) => TaskService.list(pageable), }); const auth = useAuth(); // tag::snippet[] @@ -255,7 +255,7 @@ export default function TodoView() { Then go back to the browser, logout, and login as `USER`. If you now try to create a task, you should get an error message. -Now change `TodoView()` back again. +Now change `TaskListView()` back again. // TODO This should be replaced with an integration test that checks the security, if that is even possible to write at the moment. ==== diff --git a/articles/building-apps/security/protect-views/flow.adoc b/articles/building-apps/security/protect-views/flow.adoc index 8b0951d81f..c848c51814 100644 --- a/articles/building-apps/security/protect-views/flow.adoc +++ b/articles/building-apps/security/protect-views/flow.adoc @@ -320,7 +320,7 @@ class SecurityConfig extends VaadinWebSecurity { .Create Admin View [%collapsible] ==== -Create a new class [classname]`AdminView` in the [packagename]`[application package].todo.ui.view` package: +Create a new class [classname]`AdminView` in the [packagename]`[application package].taskmanagement.ui.view` package: .AdminView.java [source,java] @@ -359,18 +359,18 @@ Attempt to access http://localhost:8080/admin directly. You should see an access .Make the Task List Read-Only For Users [%collapsible] ==== -So far all authenticated users have been able to add tasks to [classname]`TodoView`. You'll now change it so that only users with the `ADMIN` role can add tasks. Open [classname]`TodoView` and change the constructor as follows: +So far all authenticated users have been able to add tasks to [classname]`TaskListView`. You'll now change it so that only users with the `ADMIN` role can add tasks. Open [classname]`TaskListView` and change the constructor as follows: -.TodoView.java +.TaskListView.java [source,java] ---- -@Route("") +@Route("task-list") @PageTitle("Task List") @Menu(order = 0, icon = "vaadin:clipboard-check", title = "Task List") @PermitAll -public class TodoView extends Main { +public class TaskListView extends Main { - public TodoView(TodoService todoService, Clock clock, + public TaskListView(TaskService taskService, Clock clock, // tag::snippet[] AuthenticationContext authenticationContext) { // end::snippet[] @@ -385,7 +385,7 @@ public class TodoView extends Main { add(new ViewToolbar("Task List")); // <2> } // end::snippet[] - add(todoGrid); + add(taskGrid); } ... } diff --git a/articles/building-apps/security/protect-views/hilla.adoc b/articles/building-apps/security/protect-views/hilla.adoc index 3471d89b58..faa4768f11 100644 --- a/articles/building-apps/security/protect-views/hilla.adoc +++ b/articles/building-apps/security/protect-views/hilla.adoc @@ -239,18 +239,18 @@ Attempt to access http://localhost:8080/admin directly. You should end up on the .Make the Task List Read-Only For Users [%collapsible] ==== -So far all authenticated users have been able to add tasks to the todo view. You'll now change it so that only users with the `ADMIN` role can add tasks. Open `src/main/frontend/views/index.tsx` and change it as follows: +So far all authenticated users have been able to add tasks to the task list view. You'll now change it so that only users with the `ADMIN` role can add tasks. Open `src/main/frontend/views/task-list.tsx` and change it as follows: -.frontend/views/index.tsx +.frontend/views/task-list.tsx [source,tsx] ---- // tag::snippet[] import { useAuth } from "Frontend/security/auth"; // end::snippet[] ... -export default function TodoView() { - const dataProvider = useDataProvider({ - list: (pageable) => TodoService.list(pageable), +export default function TaskListView() { + const dataProvider = useDataProvider({ + list: (pageable) => TaskService.list(pageable), }); // tag::snippet[] const auth = useAuth(); @@ -262,7 +262,7 @@ export default function TodoView() { {/* tag::snippet[] */} {isAdmin && {/* <1> */} - + } {/* end::snippet[] */} diff --git a/articles/building-apps/views/add-router-layout/hilla.adoc b/articles/building-apps/views/add-router-layout/hilla.adoc index 1b2706c971..2f5013a1c5 100644 --- a/articles/building-apps/views/add-router-layout/hilla.adoc +++ b/articles/building-apps/views/add-router-layout/hilla.adoc @@ -24,7 +24,7 @@ To create a router layout, create a file named `@layout.tsx` in any directory un Here's an example of a basic router layout created directly under the `views` directory that wraps all views in the application, as it is located in the root of `views` directory: [source,tsx] -./views/@layout.tsx +.frontend/views/@layout.tsx ---- // tag::snippet[] import { Outlet } from 'react-router'; @@ -56,7 +56,7 @@ In this example, the `MainLayout` component wraps all views in the application w Here's an example of a layout that wraps the views defined in the `customers` directory and any possible subdirectories: [source,tsx] -./views/customers/@layout.tsx +.frontend/views/customers/@layout.tsx ---- import { Outlet } from 'react-router'; @@ -100,7 +100,7 @@ views There are certain views and routes that should not be rendered inside any layouts. A `login` view is common example of such a view that should escape being rendered within the application layout. You can skip the layouts that are applied to views using the `ViewConfig` configuration object. Export this object from your view file to instruct the router not to wrap this view inside any layout available in the directory structure: [source,tsx] -./views/login.tsx +.frontend/views/login.tsx ---- // tag::snippet[] import { ViewConfig } from '@vaadin/hilla-file-router/types.js'; @@ -126,7 +126,7 @@ export const config: ViewConfig = { The Hilla router provides utilities to create navigation menus based on your route structure. Use the `createMenuItems()` utility to automatically generate menu items: [source,tsx] -./views/@layout.tsx +.frontend/views/@layout.tsx ---- // tag::snippet[] import { createMenuItems } from '@vaadin/hilla-file-router/runtime.js'; @@ -190,7 +190,7 @@ The skeleton already contains a main layout. Instead of implementing one from sc The main layout is based on <<{articles}/components/app-layout#,App Layout>>: -./views/@layout.tsx +.frontend/views/@layout.tsx [source,tsx] ---- // imports and interal components @@ -321,7 +321,7 @@ The <<{articles}/building-apps/security#,Security>> guides show you how to add r Create a new directory named as `customers` under `views`. Inside this directory, create a new file called [filename]`@layout.tsx`, like this: [source,tsx] -./views/customers/@layout.tsx +.frontend/views/customers/@layout.tsx ---- import { Outlet } from 'react-router'; @@ -353,7 +353,7 @@ You can't see what your new layout looks like yet, because you don't have any vi ==== You'll now create two views that both use the new nested layout automatically. Inside the [directoryname]`views` directory, create two new views; [filename]`new.tsx` and [filename]`@index.tsx`: -./views/customers/new.tsx +.frontend/views/customers/new.tsx [source,tsx] ---- export default function NewCustomerView() { @@ -368,7 +368,7 @@ export default function NewCustomerView() { ---- <1> A red background is added to the view to make it visually distinct from the main layout and the nested layout. -./views/customers/@index.tsx +.frontend/views/customers/@index.tsx [source,tsx] ---- export default function CustomerListView() { @@ -403,7 +403,7 @@ Navigate back and forth between them, and verify that the nested layout is appli ==== Add a [filename]`login.tsx` under the `views` directory: -.views/login.tsx +.frontend/views/login.tsx [source,tsx] ---- export default function LoginView() { @@ -428,7 +428,7 @@ Navigate to the login view using the menu or by navigating to http://localhost:8 ==== To skip the automatic layout for the login view, you need to export a `config` object from the view file. Add the following code to the `login.tsx` file: -./views/login.tsx +.frontend/views/login.tsx [source,tsx] ---- import { ViewConfig } from '@vaadin/hilla-file-router/types.js'; diff --git a/articles/building-apps/views/add-view/flow.adoc b/articles/building-apps/views/add-view/flow.adoc index ba53edeed2..b310c62594 100644 --- a/articles/building-apps/views/add-view/flow.adoc +++ b/articles/building-apps/views/add-view/flow.adoc @@ -16,7 +16,7 @@ In this guide, you'll learn how to create and name views in Java, assign multipl 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 root view like this: +For example, you can define the [classname]`HelloWorld` component as the main view like this: [source,java] ---- @@ -217,62 +217,45 @@ In this mini-tutorial, you'll explore both derived and explicit routes. You'll a .Set Up the Project [%collapsible] ==== -First, generate a <<{articles}/getting-started/start#,walking skeleton with a Flow UI>>, <<{articles}/getting-started/import#,open>> it in your IDE, and <<{articles}/getting-started/run#,run>> it with hotswap enabled. +To start, generate a <<{articles}/getting-started/start#,walking skeleton with a Flow UI>>, <<{articles}/getting-started/import#,open>> it in your IDE, and <<{articles}/getting-started/run#,run>> it with hotswap enabled. ==== -.Modify the Todo View +.Create a Dashboard View [%collapsible] ==== -You'll start by changing the path of the [classname]`TodoView` to `todo`. Open the class [classname]`TodoView` in the [packagename]`[application package].todo.ui.view` package. Find the `@Route` annotation on the class, and remove the default parameter value. The code should now look like this: +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: -.TodoView.java -[source,java] ----- -// tag::snippet[] -@Route // <1> -// end::snippet[] -@PageTitle("Task List") -@Menu(order = 0, icon = "vaadin:clipboard-check", title = "Task List") -public class TodoView extends Main { - ... -} ----- -<1> The `""` default parameter value has been removed. - -Because the path is now derived from the name of the class, you can access the view at: http://localhost:8080/todo -==== - - -.Create a Main View -[%collapsible] -==== -Next, you'll create a new main view. Create a new package [packagename]`[application package].tutorial.ui.view`, and inside it a new class called [classname]`MainView`, like this: - -.MainView.java +.DashboardView.java [source,java] ---- import com.vaadin.flow.component.html.Main; import com.vaadin.flow.router.Route; @Route -public class MainView extends Main { - public MainView() { - setText("Main View"); +public class DashboardView extends Main { + public DashboardView() { + setText("Dashboard View"); } } ---- -The path is again derived from the name of the class, which means you can access the view at: http://localhost:8080 +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] ==== -Now add a `@RouteAlias("home")` annotation to the [classname]`MainView`, like this: +Next, add a `@RouteAlias("")` annotation to [classname]`DashboardView`, like this: -.MainView.java +.DashboardView.java [source,java] ---- import com.vaadin.flow.component.html.Main; @@ -283,26 +266,26 @@ import com.vaadin.flow.router.RouteAlias; @Route // tag::snippet[] -@RouteAlias("home") +@RouteAlias("") // end::snippet[] -public class MainView extends Main { +public class DashboardView extends Main { public MainView() { - setText("Main View"); + setText("Dashboard View"); } } ---- -You can now access the main view also at: http://localhost:8080/home +You can now access the dashboard view also at: http://localhost:8080/ ==== .Try a Route with Multiple Segments [%collapsible] ==== -Now go back to [classname]`TodoView` and change the path to `manage/tasks/with/vaadin`, like this: +Now open [classname]`TaskListView` and change the path to `manage/tasks/with/vaadin`, like this: -.TodoView.java +.TaskListView.java [source,java] ---- // tag::snippet[] @@ -310,12 +293,12 @@ Now go back to [classname]`TodoView` and change the path to `manage/tasks/with/v // end::snippet[] @PageTitle("Task List") @Menu(order = 0, icon = "vaadin:clipboard-check", title = "Task List") -public class TodoView extends Main { +public class TaskListView extends Main { ... } ---- -You can now access the todo view at: http://localhost:8080/manage/tasks/with/vaadin +You can now access the task list view at: http://localhost:8080/manage/tasks/with/vaadin ==== @@ -325,7 +308,6 @@ You can now access the todo view at: http://localhost:8080/manage/tasks/with/vaa 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. -* Create a main view and apply best practices for naming and organizing views. * Define multiple routes for a single view, making navigation more flexible. * Work with multi-segment routes to create more readable and meaningful URLs. diff --git a/articles/building-apps/views/add-view/hilla.adoc b/articles/building-apps/views/add-view/hilla.adoc index f40a9c2d8e..8f4b60e745 100644 --- a/articles/building-apps/views/add-view/hilla.adoc +++ b/articles/building-apps/views/add-view/hilla.adoc @@ -173,75 +173,65 @@ Avoid using explicit routes unless absolutely necessary. The routing system is d [.collapsible-list] == Try It -In this mini-tutorial, you'll explore both automatically resolved routes and explicit routes. You'll also create a new, simple view and specify multiple routes for it. +In this mini-tutorial, you'll explore automatically resolved routes. You'll also create a new, simple view and specify multiple routes for it. .Set Up the Project [%collapsible] ==== -First, generate a <<{articles}/getting-started/start#,walking skeleton with a Hilla UI>>, <<{articles}/getting-started/import#,open>> it in your IDE, and <<{articles}/getting-started/run#,run>> it. +To start, generate a <<{articles}/getting-started/start#,walking skeleton with a Hilla UI>>, <<{articles}/getting-started/import#,open>> it in your IDE, and <<{articles}/getting-started/run#,run>> it. ==== -.Modify the Todo View +.Create a Dashboard View [%collapsible] ==== -You'll start by changing the path of the `TodoView` to `todo`. The `TodoView` is stored in the file [filename]`@index.tsx` that is located directly under the `views` directory. To change its route to `/todo`, you can pick either of the following approaches: +Next, you'll create a new dashboard view. In the `views` directory, create a file named [filename]`dashboard.tsx`: -* Option 1: Create a new directory named `todo` and move the file into it. In this case the directory structure would look like this: - -[source] +[source,tsx] +.frontend/views/dashboard.tsx ---- -views -├── todo -│ └── @index.tsx -├── @layout.tsx -└── _ErrorHandler.ts +export default function DashboardView() { + return

Dashboard

; +} ---- -* Option 2: Rename the file to [filename]`todo.tsx`. In this case the directory structure would look like this: - -[source] ----- -views -├── @layout.tsx -├── _ErrorHandler.ts -└── todo.tsx ----- +The path for this view is automatically resolved to `/dashboard`, so you can access it at: http://localhost:8080/dashboard ==== -.Create a Main View +.Create an Admin View [%collapsible] ==== -Next, you'll create a new main view. In the `views` directory, create a new file called [filename]`@index.tsx`: +You'll now create an admin view. Since it may grow in complexity, you'll place it in its own directory. In the `views` directory, create a new directory called `admin`. Inside it, add a file named [filename]`@index.tsx`: [source,tsx] -.@index.tsx +.frontend/views/admin/@index.sx ---- -export default function MainView() { - return

Main View

; +export default function AdminView() { + return

Admin

} ---- -The path for this view is automatically resolved to `/`, and users can access it by navigating to `\https://example.com/`. +This view is automatically assigned the route `/admin`. Open http://localhost:8080/admin to see it in action. ==== .Add a Route Alias [%collapsible] ==== -Now, add another view file that exports a component that returns the `MainView` component. In the `views` directory, create a new file named [filename]`home.tsx`: +Now, you'll make the dashboard view the default landing page of the application. Open `frontend/views/@index.tsx` and update it as follows: [source,tsx] -.home.tsx +.frontend/views/@index.tsx ---- -export default function HomeView() { - return MainView(); +... +export default function MainView() { + return DashboardView(); } ---- -The path for this view is automatically resolved to `/home`, and users can access it by navigating to `\https://example.com/home`. This way, the same view is accessible via `/` and `/home`. +With this alias in place, the dashboard is now accessible via both http://localhost:8080 and http://localhost:8080/dashboard. ==== @@ -250,8 +240,7 @@ The path for this view is automatically resolved to `/home`, and users can acces ==== Now you've explored how to define and organize Hilla views in a Vaadin application. You've learned how to: -* Use both automatically resolved and explicit routes to structure your application's navigation. -* Create a main view and apply best practices for naming and organizing views. +* Use automatically resolved routes to structure your application's navigation. * Define multiple routes for a single view, making navigation more flexible. Next, refer to 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/index.adoc b/articles/building-apps/views/add-view/index.adoc index feab1826fd..8fc0ed009f 100644 --- a/articles/building-apps/views/add-view/index.adoc +++ b/articles/building-apps/views/add-view/index.adoc @@ -18,9 +18,12 @@ Each view is associated with its own URL path. When the user navigates from one [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 _root_ view. It is mapped to the `""` path: +In this screenshot, the visible view is the _main_ view. It is mapped to the `""` path: -image::images/root-view.png[Example of a root view] +image::images/root-view.png[Example of a main view] + +[NOTE] +The main view is sometimes also called the _root_ view. The terms are interchangeable. In this screenshot, the visible view is mapped to the `"customers"` path: diff --git a/articles/building-apps/views/navigate/flow.adoc b/articles/building-apps/views/navigate/flow.adoc index 81a5c44717..725c6af9f1 100644 --- a/articles/building-apps/views/navigate/flow.adoc +++ b/articles/building-apps/views/navigate/flow.adoc @@ -190,7 +190,7 @@ 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 root 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. +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: @@ -218,30 +218,10 @@ If you completed the mini-tutorial on <<../add-view/flow#try-it,adding views>>, ==== -.Modify the Todo View -[%collapsible] -==== -Open [classname]`TodoView` and update its route to `manage/tasks/with/vaadin`. If you followed the <<../add-view/flow#try-it,adding views>> tutorial, you've already done this. - -.TodoView.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 TodoView extends Main { - ... -} ----- -==== - - .Create the Links View [%collapsible] ==== -Now, you'll create a new view that provides multiple ways to navigate to the todo view. Create a new package [packagename]`[application package].tutorial.ui.view` package, and in it a class called `LinksView`: +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] @@ -261,7 +241,7 @@ public class LinksView extends Main { .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]`TodoView`: +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] @@ -277,7 +257,7 @@ public class LinksView extends Main { public LinksView() { // tag::snippet[] - add(new RouterLink("Todo", TodoView.class)); + add(new RouterLink("Task List", TaskListView.class)); // end::snippet[] } } @@ -285,14 +265,14 @@ public class LinksView extends Main { Now, open your browser and go to: http://localhost:8080/links -Hover on the "Todo" link to see that it points to `\http://localhost:8080/manage/tasks/with/vaadin`. Click the link to navigate to the todo view, then use the *browser's back button* to return. +Hover on the "Task List" link to see that it points to `\http://localhost:8080/task-list`. 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 todo view when clicked. Modify [classname]`LinksView` to include a [classname]`Button`: +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] @@ -309,37 +289,37 @@ import com.vaadin.flow.router.RouterLink; public class LinksView extends Main { public LinksView() { - add(new RouterLink("Todo", TodoView.class)); + add(new RouterLink("Task List", TaskListView.class)); // tag::snippet[] - add(new Button("Todo", - event -> UI.getCurrent().navigate(TodoView.class))); + add(new Button("Task List", + event -> UI.getCurrent().navigate(TaskListView.class))); // end::snippet[] } } ---- -Switch back to the browser. Thanks to *hotswap*, the new [guibutton]*Todo* button should appear automatically. Click it to navigate to the todo view. +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 todo view. +To make navigation more reusable and readable, you'll now create a dedicated method for navigating to the task list view. -Open [classname]`TodoView` and add this method: +Open [classname]`TaskListView` and add this method: -.TodoView.java +.TaskListView.java [source,java] ---- -@Route("manage/tasks/with/vaadin") +@Route("task-list") @PageTitle("Task List") @Menu(order = 0, icon = "vaadin:clipboard-check", title = "Task List") -public class TodoView extends Main { +public class TaskListView extends Main { ... // tag::snippet[] - public static void showTodos() { - UI.getCurrent().navigate(TodoView.class); + public static void showTasks() { + UI.getCurrent().navigate(TaskListView.class); } // end::snippet[] } @@ -354,9 +334,9 @@ Now, update [classname]`LinksView` to use this method instead of calling [method public class LinksView extends Main { public LinksView() { - add(new RouterLink("Todo", TodoView.class)); + add(new RouterLink("Task List", TaskListView.class)); // tag::snippet[] - add(new Button("Todo", event -> TodoView.showTodos())); + add(new Button("Task List", event -> TaskListView.showTasks())); // end::snippet[] } } diff --git a/articles/building-apps/views/navigate/hilla.adoc b/articles/building-apps/views/navigate/hilla.adoc index c9963cc3c0..dfe37a6257 100644 --- a/articles/building-apps/views/navigate/hilla.adoc +++ b/articles/building-apps/views/navigate/hilla.adoc @@ -78,23 +78,6 @@ First, generate a <<{articles}/getting-started/start#,walking skeleton with a Hi ==== -.Modify the Todo View -[%collapsible] -==== -Change the path of the `TodoView` to `todo`. The `TodoView` is stored in the file `@index.tsx` that is located directly under the `views` directory. To change its route to `/todo`, rename the file to `todo.tsx` so that the directory structure looks like this: - -[source] ----- -views -├── @layout.tsx -├── _ErrorHandler.ts -└── todo.tsx ----- - -This is a convenience step for having a simple and clear Main view for the next steps. -==== - - .Create the About View [%collapsible] ==== @@ -112,21 +95,6 @@ The path for this view is automatically resolved to `/about`, and users can acce ==== -.Create a Main View -[%collapsible] -==== -Next, create a new main view. This view is going to be the source of navigation in this mini-tutorial. In the `views` directory, create a new file called `@index.tsx`: - -[source,tsx] -.@index.tsx ----- -export default function MainView() { - return

Main View

; -} ----- -==== - - .Add a Link to the Main View [%collapsible] ==== @@ -136,13 +104,14 @@ Now, add a link that targets the `AboutView` from the `MainView`. In the `@index .@index.tsx ---- import { NavLink } from 'react-router'; +... export default function MainView() { return ( - <> +

Main View

Link to About - +
); } ---- @@ -154,12 +123,13 @@ This code creates a clickable link labeled "Link to About" that navigates to the .Add a Button for Programmatic Navigation [%collapsible] ==== -Now, add a button that navigates to the `AboutView` programmatically. In the `@index.tsx` file, change the codes to have the following code: +Now, add a button that navigates to the `AboutView` programmatically. Change the `@index.tsx` file as follows: [source,tsx] -.@index.tsx +.frontend/views/@index.tsx ---- import { NavLink, useNavigate } from 'react-router'; +... export default function MainView() { const navigate = useNavigate(); @@ -169,13 +139,13 @@ export default function MainView() { }; return ( - <> +

Main View

Link to About - +
); } ---- diff --git a/articles/building-apps/views/pass-data/query-parameters/hilla.adoc b/articles/building-apps/views/pass-data/query-parameters/hilla.adoc index e5d0b934c0..45bdb4eb5d 100644 --- a/articles/building-apps/views/pass-data/query-parameters/hilla.adoc +++ b/articles/building-apps/views/pass-data/query-parameters/hilla.adoc @@ -208,7 +208,7 @@ public class ProductService { ==== Create a view file called `products.tsx` under `src/main/frontend/views/`: -.products.tsx +.frontend/views/products.tsx [source,tsx] ---- import { VerticalLayout } from "@vaadin/react-components"; @@ -236,7 +236,7 @@ You should see two labels: ==== Import and use the `useSearchParams` from `react-router` to access the query parameters: -.products.tsx +.frontend/views/products.tsx [source,tsx] ---- import { VerticalLayout } from "@vaadin/react-components"; @@ -271,7 +271,7 @@ Try entering the following in the browser's URL, and see how the values for the ==== Use the query parameters to call the `ProductService` methods: -.products.tsx +.frontend/views/products.tsx [source,tsx] ---- import { VerticalLayout } from "@vaadin/react-components"; @@ -353,7 +353,7 @@ Try entering the following in the browser's URL, and verify the products are ren ==== Now, update the query parameters dynamically when the user changes the search term or sort order. For this, add a `TextField` and a `RadioGroup` to the view, and update the query parameters when the user interacts with them: -.products.tsx +.frontend/views/products.tsx [source,tsx] ---- // tag::snippet[] diff --git a/articles/getting-started/walk-through.adoc b/articles/getting-started/walk-through.adoc index b370f172a0..fac5624416 100644 --- a/articles/getting-started/walk-through.adoc +++ b/articles/getting-started/walk-through.adoc @@ -57,35 +57,35 @@ src │ │ ├── base <1> │ │ │ └── domain │ │ │ └── AbstractEntity.java -│ │ └── todo <2> +│ │ └── taskmanagement <2> │ │ ├── domain -│ │ │ ├── Todo.java -│ │ │ └── TodoRepository.java +│ │ │ ├── Task.java +│ │ │ └── TaskRepository.java │ │ └── service -│ │ └── TodoService.java +│ │ └── TaskService.java │ └── Application.java <3> │ └── test/java └── [application package] - ├── todo + ├── taskmanagement │ └── service - │ └── TodoServiceIT.java <4> + │ └── TaskServiceIT.java <4> └── ArchitectureTest.java <5> ---- <1> The `base` feature package. -<2> The `todo` feature package. +<2> The `taskmanagement` feature package. <3> Main entry point into the application. -<4> Example integration test for the `TodoService`. +<4> Example integration test for the `TaskService`. <5> ArchUnit architecture test for the entire application. If you generated a Flow view, the project contains more Java files. You'll learn about those later. The main entry point into the application is `Application.java`. This class contains the `main()` method that start up the Spring Boot application. -The skeleton follows a *feature-based package structure*, organizing code by *functional units* rather than traditional architectural layers. It includes two feature packages: `base` and `todo`. +The skeleton follows a *feature-based package structure*, organizing code by *functional units* rather than traditional architectural layers. It includes two feature packages: `base` and `taskmanagement`. * The `base` package contains classes meant for reuse across different features, either through composition or inheritance. -* The `todo` package is an example feature package that demonstrates the structure. It represents a *self-contained unit of functionality*, including UI components, business logic, and data access. Once you create your own features, you'll remove this package. +* The `taskmanagement` package is an example feature package that demonstrates the structure. It represents a *self-contained unit of functionality*, including UI components, business logic, and data access. Once you create your own features, you'll remove this package. This feature-driven approach keeps related code together, making it easier to maintain, extend, and understand. A feature package could represent anything from a *specific use case* (e.g., "User Registration"), a *UI view* (e.g., "Dashboard"), or even a *business subdomain* (e.g., "Billing"). By grouping everything needed for a feature into a single package structure, you avoid scattering logic across layers and reduce unnecessary coupling. @@ -94,15 +94,15 @@ You'll find `package-info.java` files in every package. These files add the `@Nu `ArchitectureTest.java` is an link:https://www.archunit.org[ArchUnit] test that guards against unintentional dependencies between classes. As your application grows, it helps keep your code base in shape. -=== The Todo Feature +=== The Task Management Feature -The `todo` feature consists of a JPA entity, a Spring Data JPA repository interface, and an application service. +The `taskmanagement` feature consists of a JPA entity, a Spring Data JPA repository interface, and an application service. The repository stores and fetches entities from a relational database. The skeleton uses an in-memory H2 database. This is useful for prototyping, but soon you'll want to replace it with something else. The application service acts as the API of the feature and is the boundary between the _presentation layer_ and the _application layer_. Its main purpose in the skeleton is to show how an application service interacts with the domain model in a Vaadin application. -The todo service has a sample integration test. It starts up the application and its embedded H2 database, and checks that the service works as expected. Its main purpose in the skeleton is to show how to write integration tests for application services. +The task service has a sample integration test. It starts up the application and its embedded H2 database, and checks that the service works as expected. Its main purpose in the skeleton is to show how to write integration tests for application services. === Java Views [badge-flow]#Flow# @@ -120,24 +120,25 @@ src │ │ └── ViewToolbar.java │ └── view │ ├── MainErrorHandler.java - │ └── MainLayout.java - └── todo + │ ├── MainLayout.java + │ └── MainView.java + └── taskmanagement └── ui └── view - └── TodoView.java + └── TaskListView.java ---- The `base` feature package contains one user interface package with two sub-packages: `component` and `view`. The `component` package contains custom UI components that can be reused throughout the entire application. The skeleton only contains one, but as your application grows, you'll add more components to this package. -The `view` package contains view-related classes that cut across multiple views in multiple features. The skeleton contains an error handler, and a main layout. +The `view` package contains view-related classes that cut across multiple views in multiple features. The skeleton contains an error handler, a main layout, and a simple main view. You'll want to replace the main view with your own as the application grows. The error handler receives all exceptions that reach the user interface, logs them, and shows an error notification to the user. You'll want to customize this as the application grows. Your application shows all the views inside the main layout by default. It contains the application's name, a navigation menu, and a mock user menu that doesn't do anything. You'll want to at least change the application name, and either remove or implement the user menu. -The `todo` feature package contains one UI-related package. It contains the view that allows users to create and list tasks to do. +The `taskmanagement` feature package contains one UI-related package. It contains the view that allows users to create and list tasks to do. == Frontend Files @@ -172,14 +173,15 @@ src └── views ├── @index.tsx ├── @layout.tsx - └── _ErrorHandler.ts + ├── _ErrorHandler.ts + └── task-list.tsx ---- The `components` directory contains custom UI components that can be reused throughout the entire application. The skeleton only contains one, but as your application grows, you'll add more components to this directory. -The `views` directory contains an example view, a main layout, and an error handler. The file names in this directory all have special meaning. You'll learn about it later. +The `views` directory contains a main view, a main layout, an error handler, and an example view. The file names in this directory all have special meaning. You'll learn about it later. -The example view - `@index.tsx` - allows users to add and list tasks to do. +The example view - `task-list.tsx` - allows users to add and list tasks to do. Your application shows all the views inside the main layout - `@layout.tsx` - by default. It contains the application's name, a navigation menu, and a mock user menu that doesn't do anything. You'll want to at least change the application name, and either remove or implement the user menu.