Add a Service
In this guide, you’ll learn how to inject an application service into a Flow view and how to call it in various scenarios.
Injecting a Service
Since application services are Spring beans, you can inject them directly into your Flow views through constructor injection.
In the following example, CustomerOnboardingService
is injected into CustomerOnboardingView
:
@Route
public class CustomerOnboardingView extends Main {
private final CustomerOnboardingService service; 1
public CustomerOnboardingView(CustomerOnboardingService service) { 2
this.service = service;
// ...
}
...
}
-
Store the service in a
final
variable for future reference. -
Inject the service as a constructor parameter.
Constructor injection is recommended because it ensures that dependencies are provided at object creation, making the class easier to test and avoiding potential issues with uninitialized fields. Additionally, since the service is stored in a final variable, it cannot be reassigned accidentally, ensuring safer code.
Calling a Service
Since Flow views are regular Java objects, calling a service is as simple as invoking a method.
In the following example, the view calls CustomerOnboardingService
when the user clicks a button:
@Route
public class CustomerOnboardingView extends Main {
private final CustomerOnboardingService service;
private final Binder<CustomerOnboardingForm> binder;
public CustomerOnboardingView(CustomerOnboardingService service) {
this.service = service;
this.binder = new Binder<>(CustomerOnboardingForm.class);
// Fields omitted
var createCustomerBtn = new Button("Create");
createCustomerBtn.addClickListener(event -> createCustomer());
add(createCustomerBtn);
}
private void createCustomer() {
try {
var formData = binder.writeRecord(); 1
var customer = service.onboardCustomer(formData); 2
CustomerView.navigateTo(customer.customerId()); 3
} catch (ValidationException ex) {
// Handle the exception
}
}
}
-
Retrieves a
CustomerOnboardingForm
record from the binder. -
Calls the service to onboard the customer.
-
Navigates to the newly created customer’s view.
Calling a Service on View Creation
Sometimes, you may need to call a service immediately upon view creation—for example, to populate a combo box or grid with data. While it may be tempting to do this in the constructor, this is not recommended.
Vaadin may instantiate a view without actually displaying it. Because of this, you should keep constructors free of side effects.
Note
|
What is a side effect?
A side effect is any operation that modifies state outside the object’s scope or interacts with external systems like databases, files, or network services during object construction.
|
After Navigation
To call a service only after the user has navigated to a view, implement the AfterNavigationObserver
interface and call the service in the afterNavigation()
method:
@Route
public class MyView extends Main implements AfterNavigationObserver {
private final CountryService countryService;
private final ComboBox<Country> countries;
public MyView(CountryService countryService) {
this.countryService = countryService;
countries = new ComboBox<>();
add(countries);
}
@Override
public void afterNavigation(AfterNavigationEvent afterNavigationEvent) {
countries.setItems(countryService.getCountries());
}
}
This ensures that service calls happen only when the view is actually rendered.
Cleaning Up
If a service call requires cleanup afterward — such as unsubscribing from a stream — use Vaadin’s attach and detach events.
Every Flow component is notified when it is attached to or detached from the UI. You can handle these events in two ways:
-
Override the protected
onAttach()
andonDetach()
methods. -
Register attach and detach listeners dynamically.
A common approach is to override onAttach()
and register a detach listener.
In the following example, the view subscribes to a reactive stream when attached and unsubscribes when detached:
public class MyView extends Main {
private final SubscriptionService subscriptionService;
public MyView(SubscriptionService subscriptionService) {
this.subscriptionService = subscriptionService;
// ...
}
@Override
protected void onAttach(AttachEvent attachEvent) {
var subscription = subscriptionService.myStream().subscribe(message -> { 1
// Do something with the message
});
addDetachListener(detachEvent -> {
detachEvent.unregisterListener(); 2
subscription.dispose(); 3
});
}
}
-
Calls the service to subscribe to the stream when attached.
-
Removes the detach listener to prevent duplicate listeners.
-
Cancels the subscription to avoid memory leaks.
Important
|
Components Can Be Attached and Detached Multiple Times
When adding a detach listener inside onAttach() , always remove it when the component is detached. Otherwise, if the component is reattached later, multiple detach listeners will accumulate, leading to potential memory leaks.
|