Skip to content

CRUD navigation flow

When you use AutoCrudOrchestrator, Mateu generates a complete navigation flow around your resource — not just a table.


For a CRUD published at /users:

@Service
@UI("/users")
public class UsersPage extends AutoCrudOrchestrator<User> {
final UserAdapter userAdapter;
public UsersPage(UserAdapter userAdapter) {
this.userAdapter = userAdapter;
}
@Override
public AutoCrudAdapter<User> simpleAdapter() {
return userAdapter;
}
}

Mateu generates these routes automatically:

RoutePurpose
/usersList
/users/newCreate form
/users/:idReadonly detail view
/users/:id/editEdit form

The default action in the listing is View (read-only), not Edit.

The standard flow is:

flowchart TD
L["/users — list"] -->|click row| V["/users/:id — readonly detail"]
L -->|click New| C["/users/new — create form"]
V -->|click Edit| E["/users/:id/edit — edit form"]
V -->|click Delete| D{Confirmation}
D -->|confirm| L
E -->|save| L
C -->|save| L

This is intentional: the read-only view serves as a safe preview before the user commits to editing.


You can replace the auto-generated edit form with a custom page using @Route and the uis parameter.

@Service
@Route(value = "/:id/edit", uis = {"/users"})
@Style(StyleConstants.CONTAINER)
@FormLayout(columns = 1)
public class UserEditorPage {
final UserRepository userRepository;
public UserEditorPage(UserRepository userRepository) {
this.userRepository = userRepository;
}
String id;
@NotEmpty
String name;
@NotEmpty
@Email
String email;
@Lookup(search = RoleOptionsSupplier.class, label = RoleLabelSupplier.class)
@Stereotype(FieldStereotype.checkbox)
List<String> roles;
@Button
Object save() {
userRepository.save(new User(id, name, email, roles));
return List.of(
new Message("User saved"),
new State(this)
);
}
}

Key points:

  • value = "/:id/edit" defines the sub-route pattern
  • uis = {"/users"} tells Mateu that this page is the edit form for the /users CRUD
  • The :id parameter is populated automatically from the URL
  • The page replaces the auto-generated edit form; everything else in the CRUD flow stays the same

Mateu reads URL parameters and maps them to fields with matching names.

If the route is /:id/edit and the page has a field String id, Mateu sets it automatically before the page is displayed.

This works for any parameter name:

@Route("/example/:name")
public class ExampleParametersViewModel {
String name; // populated from :name
int version;
@ReadOnly
String assessment;
@Button
void check() {
assessment = "name= " + name + ", version=" + version;
}
}

Without a custom editor, Mateu auto-generates the edit and create forms from the model:

  • all fields become form inputs
  • @NotEmpty / @NotNull generate validation
  • @HiddenInList fields appear in the form but not in the list
  • @EditableOnlyWhenCreating fields are editable in the create form but read-only in the edit form
  • @ReadOnly fields are always read-only

You can replace only the editor while keeping the auto-generated list, detail view, and create form. Or vice versa.

Use @Route(value = "...", uis = {"/route"}) to bind any custom page into the CRUD flow.


  • AutoCrudOrchestrator generates the full flow: list, view, edit, create
  • Default navigation goes: list → readonly detail → edit (not directly to edit)
  • Custom pages bind into the flow with @Route(uis = "/route")
  • URL parameters are mapped to same-named fields automatically
  • You can replace any part of the flow while keeping the rest auto-generated