Observers and Route Parameters
De app van Routing and Composites kan alleen nieuwe klanten aan de database toevoegen. Met de volgende concepten geef je gebruikers de mogelijkheid om ook de gegevens van bestaande klanten te bewerken:
- Routeringspatronen
- Parameterwaarden doorgeven via een URL
- Levenscycluswaarnemers
Het voltooien van deze stap creëert een versie van 4-observers-and-route-parameters.
De app uitvoeren
Terwijl je je app ontwikkelt, kun je 4-observers-and-route-parameters als vergelijking gebruiken. Om de app in actie te zien:
-
Navigeer naar de bovenliggende directory met het
pom.xml-bestand, dit is4-observers-and-route-parametersals je de versie op GitHub volgt. -
Gebruik de volgende Maven-opdracht om de Spring Boot-app lokaal uit te voeren:
mvn
De app opent automatisch een nieuwe browser op http://localhost:8080.
Gebruik van de id van de klant
Om FormView te gebruiken om bestaande klanten te bewerken, heb je een manier nodig om aan te geven welke klant moet worden bewerkt. Dit kun je doen door een initiële parameter aan FormView door te geven die de klant-ID vertegenwoordigt. In Werken met Gegevens heb je een Customer-entiteit gemaakt die een numerieke Long-waarde toewijst als unieke id aan klanten wanneer ze aan de database worden toegevoegd.
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
In deze stap zul je wijzigingen aanbrengen in FormView, zodat het een id gebruikt als een initiële parameter voordat er iets wordt geladen. Vervolgens laat je FormView de id evalueren om te bepalen of het formulier bedoeld is om een nieuwe klant toe te voegen of een bestaande te updaten. Ten slotte pas je MainView aan, zodat het een id-waarde verzendt bij het navigeren naar FormView.
Een routeringspatroon toevoegen aan FormView
In de vorige stap werd de route in FormView ingesteld op @Route(customer), wat de klasse lokaal mapte naar http://localhost:8080/customer. Een routeringspatroon toevoegen laat je een id als een initiële parameter aan FormView toevoegen.
Een Routepatroon laat je een parameter aan de URL toevoegen, deze optioneel maken en beperkingen instellen op geldige patronen. Met behulp van de annotatie @Route is hier wat id een optionele routeparameter maakt voor FormView:
-
/:idgeeft de route een benoemde parameter vanid, zodat naarhttp://localhost:8080/customer/6gaanFormViewlaadt met eenid-parameter van6. -
?maakt de parameteridoptioneel. Standaard zijn parameters vereist, maar dooridoptioneel te maken kun jeFormViewgebruiken om nieuwe klanten toe te voegen die nog geenidhebben. -
<[0-9]+>beperktidtot een positief getal. In hoekige haken,<>, kun je een beperking als een reguliere expressie aan de parameter toevoegen. Als deidniet aan de beperking voldoet, bijv.http://localhost:8080/customer/john-smith, wordt de gebruiker naar een 404-pagina gestuurd.
Om de optionele routeparameter aan FormView toe te voegen, wijzig je de annotatie @Route naar dit:
@Route("customer/:id?<[0-9]+>")
Routeren naar FormView
FormView accepteert nu een optionele id-parameter en laadt alleen als de id een geheel positief getal is.
Echter, FormView kan nog steeds laden wanneer een gebruiker handmatig een URL invoert voor een niet-bestaande klant, zoals http://localhost:8080/customer/5000. Het toevoegen van een levenscycluswaarnemer voordat je FormView binnenkomt, laat je app bepalen hoe om te gaan met de inkomende id-waarde.
Voorwaardelijke routering
Levenscycluswaarnemers stellen componenten in staat om te reageren op levenscyclusgebeurtenissen op specifieke momenten. Het artikel Levenscycluswaarnemers somt de beschikbare waarnemers op, maar deze stap gebruikt alleen de WillEnterObserver.
De timing van de WillEnterObserver vindt plaats voordat de routing van de component is voltooid. Het gebruik van deze waarnemer stelt je in staat om de inkomende id te evalueren. Als de id niet overeenkomt met een bestaande klant, kun je de gebruiker omleiden naar MainView om een geldige klant te vinden om te bewerken.
Voordat we de code voor de WillEnterObserver bespreken, legt het volgende flowchart uit wat de mogelijke uitkomsten zouden moeten zijn bij het routeren naar FormView:
Het gebruik van de WillEnterObserver
Door de levenscycluswaarnemer die wordt getriggerd voordat de component volledig laadt, WillEnterObserver, kun je voorwaarden toevoegen om te bepalen of de app verder moet gaan naar FormView, of dat gebruikers naar MainView moeten worden omgeleid.
Elke levenscycluswaarnemer is een interface, dus implementeer WillEnterObserver als onderdeel van de declaratie voor FormView:
public class FormView extends Composite<Div> implements WillEnterObserver {
De WillEnterObserver-waarnemer heeft de onWillEnter()-methode die webforJ aanroept voordat naar de component wordt gerouteerd. Deze methode heeft twee parameters: het WillEnterEvent en de ParametersBag.
Het WillEnterEvent bepaalt of de component-routing moet doorgaan met de accept()-methode, of de routing moet stoppen met de reject()-methode. Na het afwijzen van de huidige route, moet je de gebruiker ergens anders omleiden.
De ParametersBag bevat de routerparameters van de URL. Je zult de ParametersBag in de volgende sectie gebruiken om de voorwaardelijke logica voor onWillEnter() te creëren met behulp van de id-parameter.
De volgende onWillEnter() is een voorbeeld met slechts twee uitkomsten:
@Override
public void onWillEnter(WillEnterEvent event, ParametersBag parameters) {
// Voeg voorwaardelijke logica toe
if (<condition>) {
// Sta de routing naar FormView toe om door te gaan
event.accept();
} else {
// Stop de routing naar FormView
event.reject();
// Stuur de gebruiker naar MainView
navigateToMain();
}
}
Het gebruik van de ParametersBag
Zoals eerder in de vorige sectie kort genoemd, bevat de ParametersBag de routerparameter van de URL. Elke levenscycluswaarnemer heeft toegang tot dit object, en het gebruik ervan in je app stelt je in staat om de id-waarde te verkrijgen.
Het ParametersBag-object biedt verschillende querymethoden om een parameter als een specifiek objecttype op te halen. Bijvoorbeeld, getInt() kan je een parameter als een Integer geven.
Echter, omdat sommige parameters optioneel zijn, wat getInt() daadwerkelijk retourneert is Optional<Integer>. Het gebruik van de ifPresentOrElse()-methode op de Optional<Integer> stelt je in staat om een variabele in te stellen met behulp van de Integer.
Wanneer er geen id aanwezig is, kan de gebruiker doorgaan naar FormView om een nieuwe klant toe te voegen.
@Override
public void onWillEnter(WillEnterEvent event, ParametersBag parameters) {
// Bepaal welke parameter je moet ophalen en controleer of deze aanwezig is of niet
parameters.getInt("id").ifPresentOrElse(id -> {
// Gebruik de id als een variabele
customerId = Long.valueOf(id);
// Wanneer er geen id aanwezig is, ga verder naar FormView voor een nieuwe klant
}, () -> event.accept());
}
Is de id geldig?
Op dit moment accepteert de WillEnterObserver uit de vorige sectie alleen de routing wanneer er geen id aanwezig is. De waarnemer moet nog een verificatie uitvoeren voordat deze doorgaat naar FormView: bevestigen dat de id overeenkomt met een bestaande klant.
Nu kan FormView de CustomerService gebruiken om het bestaan van een klant te bevestigen met behulp van de methode doesCustomerExist(). Als er geen overeenstemming is, kan de app de huidige routing afwijzen en de gebruiker omleiden naar MainView met navigateToMain().
Bij het geven van een geldige id kan de app accept() gebruiken om door te gaan met de routing naar FormView. Maak een methode fillForm() om de customer-variabele toe te wijzen aan de klant met de overeenkomstige id in de database en de waarden van de velden in te stellen:
public void fillForm(Long customerId) {
customer = customerService.getCustomerByKey(customerId);
firstName.setValue(customer.getFirstName());
lastName.setValue(customer.getLastName());
company.setValue(customer.getCompany());
country.selectKey(customer.getCountry());
}
Net als bij het toevoegen van een nieuwe klant, stelt het gebruik van de werkende kopie gebruikers in staat om klantgegevens in de UI te bewerken zonder rechtstreeks de repository te bewerken.
Voltooid onWillEnter()
De laatste twee secties behandelt hoe je elke uitkomst voor routing naar FormView kunt verwerken met behulp van de ParametersBag en de CustomerService.
Hier is de voltooide onWillEnter() voor FormView die de ParametersBag gebruikt om ofwel de inkomende route af te wijzen of te accepteren, en andere methoden aanroept om het formulier in te vullen of de gebruiker naar MainView te sturen:
@Override
public void onWillEnter(WillEnterEvent event, ParametersBag parameters) {
// Bepaal welke parameter je moet ophalen en controleer of deze aanwezig is of niet
parameters.getInt("id").ifPresentOrElse(id -> {
customerId = Long.valueOf(id);
if (customerService.doesCustomerExist(customerId)) {
// Deze klant bestaat al, dus ga verder naar FormView en initialiseer de velden met behulp van de id
event.accept();
fillForm(customerId);
} else {
// Deze klant bestaat niet, dus omleiden naar MainView
event.reject();
navigateToMain();
}
// Er was geen id aanwezig, dus ga verder naar FormView voor een nieuwe klant
}, () -> event.accept());
}
Een klant toevoegen of bewerken
De vorige versie van deze app voegde alleen nieuwe klanten toe wanneer de gebruiker het formulier indiende. Nu gebruikers bestaande klanten kunnen bewerken, moet de methode submitCustomer() verifiëren of de klant al bestaat voordat de database wordt bijgewerkt.
Aanvankelijk was het niet nodig om een variabele voor de klant id in FormView toe te wijzen, omdat nieuwe klanten een unieke id toegewezen krijgen wanneer ze in de database worden ingediend. Echter, als je customerId declareert als een initiële variabele in FormView met een id-waarde die niet in gebruik is, blijft deze onaangeroerd voor nieuwe klanten en wordt deze overschreven in onWillEnter() voor bestaande.
Dit stelt je in staat om doesCustomerExist() te gebruiken om te verifiëren of je een nieuwe klant moet toevoegen of een bestaande moet updaten.
private Long customerId = 0L;
//...
private void submitCustomer() {
if (customerService.doesCustomerExist(customerId)) {
customerService.updateCustomer(customer);
} else {
customerService.createCustomer(customer);
}
navigateToMain();
}
Voltooid FormView
Hier is hoe FormView eruit moet zien, nu dat het het bewerken van bestaande klanten kan afhandelen:
Navigeren van MainView naar FormView om klanten te bewerken
Eerder in deze stap heb je een bestaande ParametersBag gebruikt om de waarde van een id te bepalen. Het maken van een nieuwe ParametersBag stelt je in staat om rechtstreeks tussen klassen te navigeren met de parameters van jouw keuze. Het gebruik van de gegevens in de Tabel is een haalbare optie om gebruikers naar FormView te sturen met een klant id.
Net als bij de knop geeft het koppelen van de navigatie aan een door de gebruiker gekozen actie hen de mogelijkheid te beslissen wanneer ze naar FormView willen gaan. Het toevoegen van een gebeurtenislistener aan de Tabel laat je de gebruiker naar FormView sturen met een ParametersBag:
table.addItemClickListener(this::editCustomer);
private void editCustomer(TableItemClickEvent<Customer> e) {
Router.getCurrent().navigate(FormView.class,
ParametersBag.of("id=" + e.getItemKey()));
}
Echter, de sleutel van de Tabel-items is standaard automatisch gegenereerd. Je kunt expliciet elke sleutel laten correleren met de id van een klant door de setKeyProvider()-methode te gebruiken:
table.setKeyProvider(Customer::getId);
In MainView, voeg je de methoden addItemClickListener() en setKeyProvider() toe aan buildTable(), en voeg je de methode toe die de gebruiker naar FormView stuurt met een waarde voor de id in de ParametersBag, gebaseerd op waar in de tabel de gebruiker heeft geklikt:
@Route("/")
@FrameTitle("Klantentabel")
public class MainView extends Composite<Div> {
private final CustomerService customerService;
private Div self = getBoundComponent();
private Table<Customer> table = new Table<>();
private Button addCustomer = new Button("Voeg Klant Toe", ButtonTheme.PRIMARY,
e -> Router.getCurrent().navigate(FormView.class));
public MainView(CustomerService customerService) {
this.customerService = customerService;
addCustomer.setWidth(200);
buildTable();
self.setWidth("fit-content")
.addClassName("card")
.add(table, addCustomer);
}
private void buildTable() {
table.setSize("1000px", "294px");
table.setMaxWidth("90vw");
table.addColumn("firstName", Customer::getFirstName).setLabel("Voornaam");
table.addColumn("lastName", Customer::getLastName).setLabel("Achternaam");
table.addColumn("company", Customer::getCompany).setLabel("Bedrijf");
table.addColumn("country", Customer::getCountry).setLabel("Land");
table.setColumnsToAutoFit();
table.setColumnsToResizable(false);
table.getColumns().forEach(column -> column.setSortable(true));
table.setRepository(customerService.getRepositoryAdapter());
table.setKeyProvider(Customer::getId);
table.addItemClickListener(this::editCustomer);
}
private void editCustomer(TableItemClickEvent<Customer> e) {
Router.getCurrent().navigate(FormView.class,
ParametersBag.of("id=" + e.getItemKey()));
}
}
Volgende stap
Nu gebruikers klantgegevens direct kunnen bewerken, moet je app wijzigingen valideren voordat ze in de repository worden vastgelegd. In Valideren en Binden van Gegevens zul je validatieregels maken en de gegevensmodel direct koppelen aan de UI, zodat de componenten foutmeldingen kunnen weergeven wanneer de gegevens ongeldig zijn.