Observers and Route Parameters
De app van Routing en Composities kan alleen nieuwe klanten aan de database toevoegen. Met behulp van de volgende concepten geef je gebruikers ook de mogelijkheid om de gegevens van bestaande klanten te bewerken:
- Routepatronen
- Het doorgeven van parameterwaarden 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 bovenste directory die het
pom.xml-bestand bevat, 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.
Het gebruik van de klant id
Om FormView te gebruiken om bestaande klanten te bewerken, heb je een manier nodig om aan te geven welke klant je wilt bewerken. Dit kun je doen door een initiële parameter aan FormView te geven die de klant-ID vertegenwoordigt. In Werken met Gegevens heb je een Customer-entiteit gemaakt die een numerieke Long-waarde toekent als een unieke id aan klanten wanneer ze aan de database worden toegevoegd.
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
In deze stap breng je wijzigingen aan in FormView, zodat het een id als initiële parameter gebruikt voordat er iets wordt geladen. Vervolgens laat je FormView de id evalueren om te bepalen of het formulier bedoeld is voor het toevoegen van een nieuwe klant of het bijwerken van een bestaande. Ten slotte pas je MainView aan, zodat het een id-waarde verzendt wanneer het naar FormView navigeert.
Een routepatroon aan FormView toevoegen
In de vorige stap werd de route in FormView ingesteld op @Route(customer), wat de klasse lokaal mapte naar http://localhost:8080/customer. Een routepatroon toevoegen stelt je in staat om een id als een initiële parameter aan FormView toe te voegen.
Een Routepatroon stelt je in staat om een parameter in de URL toe te voegen, deze optioneel te maken en beperkingen op geldige patronen in te stellen. Met behulp van de annotatie @Route, zorgt het volgende ervoor dat id een optionele routeparameter voor FormView is:
-
/:idgeeft de route een genummerde parameterid, zodat het laden vanhttp://localhost:8080/customer/6FormViewlaadt met eenid-parameter van6. -
?maakt de parameteridoptioneel. Standaard zijn parameters vereist, maar door deidoptioneel te maken, kun jeFormViewgebruiken voor het toevoegen van nieuwe klanten die nog geenidhebben. -
<[0-9]+>beperktidtot een positief nummer. In hoekige haken,<>, kun je een beperking als een reguliere expressie aan de parameter toevoegen. Als deidniet aan de beperking voldoet, bijvoorbeeldhttp://localhost:8080/customer/john-smith, stuurt het de gebruiker naar een 404-pagina.
Om de optionele routeparameter aan FormView toe te voegen, verander 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 nummer is.
Echter, FormView kan nog steeds laden wanneer een gebruiker handmatig een URL voor een niet-bestaande klant invoert, zoals http://localhost:8080/customer/5000. Een levenscycluswaarnemer toevoegen voordat je naar FormView gaat, stelt je app in staat om te bepalen hoe om te gaan met de binnenkomende id-waarde.
Voorwaardelijk routeren
Levenscycluswaarnemers stellen componenten in staat om te reageren op levenscyclusgebeurtenissen op specifieke momenten. Het artikel Levenscycluswaarnemers noemt beschikbare waarnemers, maar deze stap maakt alleen gebruik van de WillEnterObserver.
De timing van de WillEnterObserver vindt plaats voordat de routering van de component is voltooid. Het gebruik van deze waarnemer stelt je in staat om de binnenkomende id te evalueren. Als de id niet overeenkomt met een bestaande klant, kun je de gebruiker terugsturen naar MainView om een geldige klant te vinden om te bewerken.
Voordat we de code voor de WillEnterObserver bespreken, toont de volgende flowchart welke mogelijke uitkomsten er moeten zijn bij het routeren naar FormView:
De WillEnterObserver gebruiken
Met behulp van de levenscycluswaarnemer die wordt geactiveerd voordat de component volledig wordt geladen, WillEnterObserver, kun je voorwaarden toevoegen om te bepalen of de app moet doorgaan naar FormView, of dat deze de gebruikers moet omleiden naar MainView.
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 heeft de methode onWillEnter() die webforJ aanroept voordat er naar de component wordt gerouteerd. Deze methode heeft twee parameters: het WillEnterEvent en de ParametersBag.
Het WillEnterEvent bepaalt of de routering naar de component moet doorgaan met de methode accept(), of de routering moet stoppen met de methode reject(). Na het afwijzen van de huidige route, moet je de gebruiker ergens anders omleiden.
De ParametersBag bevat de routerparameters uit 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 routeren naar FormView toe om door te gaan
event.accept();
} else {
// Stop met routeren naar FormView
event.reject();
// Stuur de gebruiker naar MainView
navigateToMain();
}
}
De ParametersBag gebruiken
Zoals kort vermeld in de vorige sectie, bevat de ParametersBag de routerparameter uit 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 krijgen.
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 methode ifPresentOrElse() 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 naar FormView blijven gaan 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 routering wanneer er geen id aanwezig is. De waarnemer moet nog een verificatie uitvoeren alvorens door te gaan naar FormView: verifiëren dat de id overeenkomt met een bestaande klant.
Nu kan FormView de CustomerService gebruiken om de aanwezigheid van een klant te bevestigen met behulp van de methode doesCustomerExist(). Als er geen match is, kan de app de huidige routering afwijzen en de gebruiker omleiden naar MainView met navigateToMain().
Bij een geldige id kan de app accept() gebruiken om door te gaan met routeren naar FormView. Maak een fillForm()-methode aan om de customer-variabele toe te wijzen aan de klant met de bijbehorende id in de database en stel de waarden van de velden in:
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 direct de repository te bewerken.
Voltooide onWillEnter()
De laatste twee secties gingen in detail in op hoe je elke uitkomst voor de routering naar FormView moet afhandelen met behulp van de ParametersBag en de CustomerService.
Hier is de voltooide onWillEnter() voor FormView die de ParametersBag gebruikt om ofwel de binnenkomende route te weigeren of te accepteren, en andere methoden aanroept om ofwel 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, 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 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 bijwerken.
private Long customerId = 0L;
//...
private void submitCustomer() {
if (customerService.doesCustomerExist(customerId)) {
customerService.updateCustomer(customer);
} else {
customerService.createCustomer(customer);
}
navigateToMain();
}
Voltooide FormView
Hier is hoe FormView eruit zou moeten zien, nu het klanten bewerken kan:
Navigeren van MainView naar FormView om klanten te bewerken
Eerder in deze stap gebruikte je een bestaande ParametersBag om de waarde van een id te bepalen. Het creëren van een nieuwe ParametersBag laat je toe om direct tussen klassen te navigeren met de parameters van jouw keuze. Het gebruik van de gegevens in de Table is een haalbare optie om gebruikers naar FormView te sturen met een klant id.
Net als bij de knop, laat het koppelen van de navigatie aan een door de gebruiker gekozen actie hen beslissen wanneer ze naar FormView gaan. Het toevoegen van een gebeurtenisluis aan de Table stelt je in staat om de gebruiker naar FormView te 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 Table-items wordt standaard automatisch gegenereerd. Je kunt expliciet elke sleutel maken die samenhangt met een klant id door de methode setKeyProvider() te gebruiken:
table.setKeyProvider(Customer::getId);
In MainView, voeg je de methoden addItemClickListener() en setKeyProvider() toe aan buildTable(), en voeg daarna de methode die de gebruiker naar FormView stuurt met een waarde voor de id in de ParametersBag op basis van waar in de tabel de gebruiker klikte:
@Route("/")
@FrameTitle("Klanten Tabel")
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 rechtstreeks kunnen bewerken, moet je app de wijzigingen valideren voordat deze in de repository worden opgeslagen. In Valideren en Koppelen van Gegevens ga je validatieregels creëren en direct het gegevensmodel met de UI associëren, zodat de componenten foutmeldingen kunnen weergeven wanneer de gegevens ongeldig zijn.