18. Client Vue.js van de belastingberekeningsserver
18.1. Architecture
We gaan een client-server-toepassing implementeren met de volgende architectuur:

De belastingberekeningsserver is versie 14, ontwikkeld in het document |https://tahe.developpez.com/tutoriels-cours/php7|
18.2. De weergaven van de applicatie
De weergaven van de applicatie [vuejs-10] zijn die van versie 13 van het document |https://tahe.developpez.com/tutoriels-cours/php7| van de belastingberekeningsserver wanneer deze in de modus HTML wordt gebruikt. Maar in de huidige applicatie worden deze weergaven gegenereerd door de JavaScript-client en niet door de server PHP.
De eerste weergave is de authenticatiepagina:

De tweede weergave is die voor de belastingberekening:

De derde weergave toont de lijst met simulaties die de gebruiker heeft uitgevoerd:

Het bovenstaande scherm laat zien dat simulatie nr. 1 kan worden verwijderd. We krijgen dan het volgende scherm te zien:

Als we nu de laatste simulatie verwijderen, krijgen we het volgende nieuwe scherm te zien:

18.3. Projectelementen [vuejs-20]
De boomstructuur van project [vuejs-20] is als volgt:

De onderdelen van het project zijn als volgt:
- [assets/logo.jpg]: het logo van het project;
- [couches]: de lagen [métier] en [dao] van de applicatie;
- [plugins]: de plug-ins van de applicatie;
- [views]: de weergaven van de applicatie;
- [config.js]: configureert de applicatie;
- [router.js]: definieert de routing van de applicatie;
- [store.js]: de store van [Vuex];
- [main.js]: het hoofdscript van de applicatie;
18.3.1. De lagen [métier] en [dao]
18.3.1.1. De laag [dao]
De laag [dao] wordt geïmplementeerd door de klasse [Dao] uit paragraaf |vuejs-10|
18.3.1.2. De laag [métier]
De laag [métier] wordt geïmplementeerd door de klasse [Métier] uit het document |https://tahe.developpez.com/tutoriels-cours/php7|. Hieraan is de volgende methode [setTaxAdminData] toegevoegd:
// constructor
constructor(taxAdmindata) {
// this.taxAdminData: gegevens van de belastingdienst
this.taxAdminData = taxAdmindata;
}
// setter
setTaxAdminData(taxAdmindata) {
// this.taxAdminData: gegevens van de belastingdienst
this.taxAdminData = taxAdmindata;
}
De methode [setTaxAdminData] doet hetzelfde als de constructor. Hierdoor is de volgende reeks mogelijk:
- de klasse [Métier] instantiëren met een instructie [métier=new Métier()] wanneer men de klasse wil instantiëren maar de gegevens [taxAdminData] nog niet heeft;
- vervolgens de eigenschap [taxAdminData] later invullen via een bewerking [métier.setTaxAdminData(taxAdmindata)];
18.3.2. Het configuratiebestand [config]
Het bestand [config.js] ziet er als volgt uit:
// gebruik van de bibliotheek [axios]
const axios = require('axios');
// time-out van verzoeken HTTP
axios.defaults.timeout = 2000;
// de database van de belastingberekeningsserver URL
// het schema [https] veroorzaakt problemen in Firefox omdat de server
// een zelfondertekend certificaat verstuurt. Werkt prima met Chrome en Edge. Safari niet getest.
axios.defaults.baseURL = 'https://localhost/php7/scripts-web/impots/version-14';
// we gaan cookies gebruiken
axios.defaults.withCredentials = true;
// de configuratie exporteren
export default {
axios: axios
}
Deze configuratie is die van de bibliotheek [axios] die de laag [dao] gebruikt om zijn verzoeken HTTP uit te voeren. Let op regel 8: de server werkt op de beveiligde poort [https].
18.3.3. De plug-ins
De plug-ins [pluginDao, pluginMétier, pluginConfig] zijn bedoeld om drie nieuwe eigenschappen toe te voegen aan de functie/klasse [Vue]:
- [$dao]: krijgt als waarde een instantie van de klasse [Dao];
- [$métier]: krijgt als waarde een instantie van de klasse [Métier];
- [$config]: krijgt als waarde het object dat wordt geëxporteerd door het configuratiebestand [config];
[pluginDao]
export default {
install(Vue, dao) {
// voeg een eigenschap [$dao] toe aan de Vue-klasse
Object.defineProperty(Vue.prototype, '$dao', {
// wanneer naar Vue.$dao wordt verwezen, wordt de tweede parameter [dao] weergegeven
get: () => dao,
})
}
}
[pluginMétier]
export default {
install(Vue, métier) {
// voegt een eigenschap [$métier] toe aan de klasse Vue
Object.defineProperty(Vue.prototype, '$métier', {
// wanneer naar Vue.$métier wordt verwezen, wordt de tweede parameter [métier] weergegeven
get: () => métier,
})
}
}
[pluginConfig]
export default {
install(Vue, config) {
// voegt een eigenschap [$config] toe aan de Vue-klasse
Object.defineProperty(Vue.prototype, '$config', {
// wanneer er naar Vue.$config wordt verwezen, wordt de tweede parameter [config] gerenderd
get: () => config,
})
}
}
18.3.4. De store [Vuex]
De store van [Vuex] wordt geïmplementeerd door het volgende bestand [store]:
// Vuex-plugin
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
// Vuex-store
const store = new Vuex.Store({
state: {
// de tabel met simulaties
simulations: [],
// het nummer van de laatste simulatie
idSimulation: 0
},
mutations: {
// verwijdering van regel nr. index
deleteSimulation(state, index) {
// eslint-disable-next-line no-console
console.log("mutation deleteSimulation");
// regel nr. [index] wordt verwijderd
state.simulations.splice(index, 1);
// eslint-disable-next-line no-console
console.log("store simulations", state.simulations);
},
// een simulatie toevoegen
addSimulation(state, simulation) {
// eslint-disable-next-line no-console
console.log("mutation addSimulation");
// nummer van de simulatie
state.idSimulation++;
simulation.id = state.idSimulation;
// de simulatie wordt toegevoegd aan de tabel met simulaties
state.simulations.push(simulation);
},
// state opschonen
clear(state) {
state.simulations = [];
state.idSimulation = 1;
}
}
});
// het object exporteren [store]
export default store;
Opmerkingen
- regels 2-4: de plug-in [Vuex] is geïntegreerd in het framework [Vue];
- regels 8-13: we plaatsen de volgende elementen in de store van [Vuex]:
- [simulations]: de lijst met simulaties die door de gebruiker zijn uitgevoerd;
- [idSimulation]: het nummer van de laatste simulatie die door de gebruiker is uitgevoerd;
Ter herinnering: de store wordt gedeeld door de weergaven en de inhoud ervan is responsief: wanneer deze wordt gewijzigd, worden de weergaven die er gebruik van maken automatisch bijgewerkt. In onze applicatie hoeft alleen het element [simulations] responsief te zijn, niet het element [idSimulation]. We hebben dit element voor het gemak in de store gelaten;
- regels 14-40: de toegestane wijzigingen op het object [state] uit de regels 8-13. Ter herinnering: deze ontvangen altijd het object [state] uit de regels 8-13 als eerste parameter;
- regel 16: met de wijziging [deleteSimulation] kan een simulatie met nummer [index] worden verwijderd;
- regel 25: met de mutatie [addSimulation] kan een nieuwe simulatie aan de simulatietabel worden toegevoegd;
- regel 35: met de wijziging [clear] kan het object [state] van de regels 8-13 worden gereset;
18.3.5. Het routeringsbestand [router]
Het routeringsbestand is als volgt:
// importen
import Vue from 'vue'
import VueRouter from 'vue-router'
// de weergaven
import Authentification from './views/Authentification'
import CalculImpot from './views/CalculImpot'
import ListeSimulations from './views/ListeSimulations'
// routeringsplugin
Vue.use(VueRouter)
// de routes van de applicatie
const routes = [
// authenticatie
{
path: '/', name: 'authentification', component: Authentification
},
// belastingberekening
{
path: '/calcul-impot', name: 'calculImpot', component: CalculImpot
},
// lijst met simulaties
{
path: '/liste-des-simulations', name: 'listeSimulations', component: ListeSimulations
},
// sessie beëindigen
{
path: '/fin-session', name: 'finSession', component: Authentification
}
]
// de router
const router = new VueRouter({
// de routes
routes,
// de weergavemodus van de routes in de browser
mode: 'history',
})
// de router exporteren
export default router
Opmerkingen
- regel 16: bij het opstarten van de applicatie wordt de weergave [Authentification] weergegeven, omdat de URL de root is van [/];
- regel 20: de weergave [CalculImpot] wordt weergegeven wanneer de weergave URL ([/calcul-impot]) wordt opgevraagd;
- regel 24: de weergave [ListeSimulations] wordt weergegeven wanneer de weergave URL of [/liste-des-simualtions] wordt opgevraagd;
- regel 28: de weergave [Authentification] wordt weergegeven wanneer de weergave URL [/fin-session] wordt opgevraagd;
- regels 33-38: er wordt een object [router] aangemaakt met deze routes (regel 35) en de beheermodus [history] (regel 37) van URL;
- regel 41: deze router wordt geëxporteerd;
18.3.6. Het hoofdscript [main.js]
Het script [main.js] is als volgt:
// importen
import Vue from 'vue'
// hoofdscherm
import Main from './views/Main.vue'
// plug-in [bootstrap-vue]
import BootstrapVue from 'bootstrap-vue'
Vue.use(BootstrapVue);
// CSS bootstrap
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
// router
import router from './router'
// plugin [config]
import config from './config';
import pluginConfig from './plugins/pluginConfig'
Vue.use(pluginConfig, config)
// instantie van de laag [dao]
import Dao from './couches/Dao';
const dao = new Dao(config.axios);
// plug-in [dao]
import pluginDao from './plugins/pluginDao'
Vue.use(pluginDao, dao)
// instantie van de laag [métier]
import Métier from './couches/Métier';
const métier = new Métier();
// plugin [métier]
import pluginMétier from './plugins/pluginMétier'
Vue.use(pluginMétier, métier)
// Vuex-store
import store from './store'
// opstarten van de UI
new Vue({
el: '#app',
// de router
router: router,
// de Vuex-store
store: store,
// het hoofdscherm
render: h => h(Main),
})
Let op de volgende punten:
- regels 18-21: het object dat door het script [./config] wordt geëxporteerd, zal beschikbaar zijn in het attribuut [Vue.$config] en dus in alle weergaven van de applicatie. Dit was hier niet nodig, omdat het object [config] alleen wordt gebruikt door het script [main] (regel 25). Het komt echter vaak voor dat de configuratie voor meerdere weergaven nodig is. Daarom hebben we hier het principe willen handhaven om het beschikbaar te maken in een attribuut van de weergave;
- regels 24-25: instantiëren van de laag [dao]. De klasse [Dao] wordt geïmporteerd in regel 24 en vervolgens geïnstantieerd in regel 25. De constructor ervan accepteert als enige parameter het object [axios], een configuratie-eigenschap;
- regels 27-29: de laag [dao] wordt beschikbaar gemaakt in het attribuut [$dao] van alle weergaven;
- regels 31-37: dezelfde reeks wordt herhaald voor de laag [métier]. De constructor van de klasse [Métier] heeft als parameter [taxAdminData], die de gegevens van de belastingdienst vertegenwoordigt. We beschikken nog niet over deze gegevens. Het object [métier] uit regel 33 moet dus later worden aangevuld;
- regel 40: de store [Vuex] wordt geïmporteerd;
- regels 43-51: de hoofdweergave [Main] (regels 5 en 50) wordt geïnstantieerd, waarbij twee parameters worden doorgegeven:
- regel 46: de router [router], gedefinieerd in regel 16;
- regel 48: de store [Vuex] [store], gedefinieerd op regel 40;
- in beide gevallen staat de naam van de eigenschap links en de waarde rechts. De namen van de eigenschappen [router, store] worden bepaald door de frameworks [vue-router] en [vuex]. De bijbehorende waarden kunnen willekeurig zijn;
18.4. De weergaven van de applicatie
18.4.1. De hoofdweergave [Main]
De code van de hoofdweergave [Main] is als volgt:
<!-- definitie van het overzicht HTML -->
<template>
<div class="container">
<b-card>
<!-- jumbotron -->
<b-jumbotron>
<b-row>
<b-col cols="4">
<img src="../assets/logo.jpg" alt="Cerisier en fleurs" />
</b-col>
<b-col cols="8">
<h1>Calculez votre impôt</h1>
</b-col>
</b-row>
</b-jumbotron>
<!-- fout bij verzoek HTTP -->
<b-alert
show
variant="danger"
v-if="showError"
>L'erreur suivante s'est produite : {{error.message}}</b-alert>
<!-- huidige weergave -->
<router-view v-if="showView" @loading="mShowLoading" @error="mShowError" />
<!-- wordt geladen -->
<b-alert show v-if="showLoading" variant="light">
<strong>Requête au serveur de calcul d'impôt en cours...</strong>
<div class="spinner-border ml-auto" role="status" aria-hidden="true"></div>
</b-alert>
</b-card>
</div>
</template>
<script>
export default {
// naam
name: "app",
// interne status
data() {
return {
// controleert de wachtwaarschuwing
showLoading: false,
// regelt de foutmelding
showError: false,
// regelt de weergave van de huidige routeringsweergave
showView: true,
// een foutmelding
error: ""
};
},
// gebeurtenishandlers
methods: {
// fout bij asynchrone verzoek
mShowError(error) {
// eslint-disable-next-line
console.log("Main evt error");
// de foutmelding wordt weergegeven
this.error = error;
this.showError = true;
// de gerouteerde weergave wordt in de cache opgeslagen
this.showView = false;
// het wachtbericht wordt verborgen
this.showLoading = false;
},
// al dan niet een wachtpictogram weergeven
mShowLoading(value) {
// eslint-disable-next-line
console.log("Main evt showLoading");
// alarm bij wachten al dan niet weergeven
this.showLoading = value;
}
}
};
</script>
Opmerkingen
- De weergave [Main] zorgt voor de opmaak van de doorgestuurde en weergegeven weergave op regel 23:

- de regels 5-15 geven zone 1 weer;
- regel 23 geeft de gerouteerde weergave [2] weer;
- regels 16-19: een waarschuwing die alleen wordt weergegeven in geval van een communicatiefout met de server voor de belastingberekening;
- regels 25-28: een wachtmelding die wordt weergegeven bij elk verzoek HTTP aan de server;
- alle weergaven worden met deze opmaak weergegeven, aangezien elke gerouteerde weergave wordt weergegeven via de regels 20-24. De weergave [Main] dient om elementen te factoriseren die door de verschillende weergaven kunnen worden gedeeld;
- regel 23: elke doorgestuurde weergave kan drie gebeurtenissen genereren:
- [loading]: er is een verzoek HTTP gestart. Het bericht dat er op het antwoord wordt gewacht, moet worden weergegeven;
- [error]: het verzoek HTTP is met een fout beëindigd. Het foutbericht moet worden weergegeven en de gerouteerde weergave moet worden verborgen;
- regels 38-49: de status van de weergave:
- regel 41: [showLoading] regelt de weergave van het bericht dat aangeeft dat er wordt gewacht op de voltooiing van een verzoek HTTP (regel 25);
- regel 43: [showError] regelt de weergave van het foutbericht van een verzoek HTTP (regels 17-21);
- regel 45: [showView] regelt de weergave van de gerouteerde weergave (regel 23);
- regels 53-63: de methode [mShowError] verwerkt de gebeurtenis [error] die door de gerouteerde weergave wordt gegenereerd (regel 23);
- regels 65-70: de methode [mShowLoading] verwerkt de gebeurtenis [loading] die wordt gegenereerd door de gerouteerde weergave (regel 23);
- regel 23: let op de gebeurtenissen [error] en [loading]. Deze worden alleen onderschept als de gerouteerde weergave wordt weergegeven ([showView=true]). Daarom wordt de gerouteerde weergave in eerste instantie weergegeven (regel 45). Deze wordt alleen verborgen in geval van een fout (regel 60). Om dit probleem te voorkomen had men de richtlijn [v-show] kunnen gebruiken in plaats van [v-if]. Het verschil tussen deze twee richtlijnen is als volgt:
- [v-if=’false’] verbergt het gecontroleerde blok door het uit de globale HTML-code te verwijderen. De gebeurtenissen van de gerouteerde weergave kunnen dan niet meer worden onderschept;
- [v-show=’false’] verbergt het gecontroleerde blok door de bijbehorende CSS aan te passen, maar de code van het blok blijft aanwezig in de globale HTML en kan daardoor de gebeurtenissen van de gerouteerde weergave onderscheppen;
18.4.2. De lay-outweergave [Layout]
De code van de weergave [Layout] is als volgt:
<!-- definitie HTML van de lay-out van de gerouteerde weergave -->
<template>
<!-- regel -->
<div>
<b-row>
<!-- gebied met drie kolommen links -->
<b-col cols="3" v-if="left">
<slot name="left" />
</b-col>
<!-- gebied met negen kolommen rechts -->
<b-col cols="9" v-if="right">
<slot name="right" />
</b-col>
</b-row>
</div>
</template>
<script>
export default {
// instellingen van de weergave
props: {
// regelt de linkerkolom
left: {
type: Boolean
},
// regelt de rechterkolom
right: {
type: Boolean
}
}
};
</script>
Opmerkingen
- Met de weergave [Layout] kan de gerouteerde weergave in twee zones worden verdeeld:
- een zone met 3 Bootstrap-kolommen aan de linkerkant (regels 7-9). In deze zone wordt het navigatiemenu weergegeven, indien aanwezig;
- een zone met 9 kolommen aan de rechterkant (regels 11-13). In deze zone wordt de informatie weergegeven die door de gerouteerde weergave wordt aangeleverd;
18.4.3. De weergave [Authentification]
De authenticatieweergave ziet er als volgt uit:

Deze weergave wordt verkregen door uit de weergave [Layout] de linkerkolom te verwijderen, zodat alleen de rechterkolom wordt weergegeven.
De code ervan is als volgt:
<!-- definitie HTML van de weergave -->
<template>
<Layout :left="false" :right="true">
<template slot="right">
<!-- formulier HTML – de waarden worden verzonden met de actie [authentifier-utilisateur] -->
<b-form @submit.prevent="login">
<!-- titel -->
<b-alert show variant="primary">
<h4>Bienvenue. Veuillez vous authentifier pour vous connecter</h4>
</b-alert>
<!-- 1e regel -->
<b-form-group label="Nom d'utilisateur" label-for="user" label-cols="3">
<!-- invoerveld gebruiker -->
<b-col cols="6">
<b-form-input type="text" id="user" placeholder="Nom d'utilisateur" v-model="user" />
</b-col>
</b-form-group>
<!-- 2e regel -->
<b-form-group label="Mot de passe" label-for="password" label-cols="3">
<!-- wachtwoordveld -->
<b-col cols="6">
<b-input type="password" id="password" placeholder="Mot de passe" v-model="password" />
</b-col>
</b-form-group>
<!-- derde regel -->
<b-alert
show
variant="danger"
v-if="showError"
class="mt-3"
>L'erreur suivante s'est produite : {{message}}</b-alert>
<!-- knop van het type [submit] op een derde regel -->
<b-row>
<b-col cols="2">
<b-button variant="primary" type="submit" :disabled="!valid">Valider</b-button>
</b-col>
</b-row>
</b-form>
</template>
</Layout>
</template>
<!-- weergavedynamiek -->
<script>
import Layout from "./Layout";
export default {
// status van de component
data() {
return {
// gebruiker
user: "",
// zijn wachtwoord
password: "",
// controleert de weergave van een foutmelding
showError: false,
// de foutmelding
message: "",
// sessie gestart
sessionStarted: false
};
},
// gebruikte componenten
components: {
Layout
},
// berekende eigenschappen
computed: {
// geldige invoer
valid() {
return this.user && this.password && this.sessionStarted;
}
},
// gebeurtenishandlers
methods: {
// ----------- authenticatie
async login() {
try {
// begin wachtrij
this.$emit("loading", true);
// blokkerende authenticatie bij de server
const response = await this.$dao.authentifierUtilisateur(
this.user,
this.password
);
// einde van het laden
this.$emit("loading", false);
// analyse van het antwoord
if (response.état != 200) {
// de fout wordt weergegeven
this.message = response.réponse;
this.showError = true;
return;
}
// geen fout
this.showError = false;
// --------- nu worden de gegevens van de belastingdienst opgevraagd
// wachten begint
this.$emit("loading", true);
// blokkerend verzoek aan de server
const response2 = await this.$dao.getAdminData();
// einde van het laden
this.$emit("loading", false);
// analyse van het antwoord
if (response2.état != 1000) {
// de fout wordt weergegeven
this.message = response2.réponse;
this.showError = true;
return;
}
// geen fout
this.showError = false;
// de ontvangen gegevens worden opgeslagen in de laag [métier]
this.$métier.setTaxAdminData(response2.réponse);
// er wordt overgeschakeld naar het scherm voor de belastingberekening
this.$router.push({ name: "calculImpot" });
} catch (error) {
// de fout wordt doorgegeven aan de hoofdcomponent
this.$emit("error", error);
}
}
},
// levenscyclus: de component is zojuist aangemaakt
created() {
// eslint-disable-next-line
console.log("authentification", "created");
// er wordt een sessie gestart met de server jSON
// het wachten begint
this.$emit("loading", true);
// de sessie met de server wordt geïnitialiseerd – asynchrone aanvraag
// de belofte wordt gebruikt die is geretourneerd door de methoden van de laag [dao]
this.$dao
// een sessie wordt geïnitialiseerd jSON
.initSession()
// het antwoord is ontvangen
.then(response => {
// wachten beëindigd
this.$emit("loading", false);
// analyse van het antwoord
if (response.état != 700) {
// de fout wordt weergegeven
this.message = response.réponse;
this.showError = true;
return;
}
// de sessie is gestart
this.sessionStarted = true;
})
// in geval van een fout
.catch(error => {
// de fout wordt doorgegeven aan de weergave [Main]
this.$emit("error", error);
});
}
};
</script>
Opmerkingen
- regel 3: de weergave [Authentification] gebruikt uitsluitend de rechterkolom van [Layout] (regels 3 en 4);
- regels 6-38: het Bootstrap-formulier dat gebied 1 van de bovenstaande schermafbeelding genereert;
- regel 6: de gebeurtenis [@submit] vindt plaats wanneer de gebruiker op de knop van het type [submit] in regel 35 klikt. De modifier [prevent] zorgt ervoor dat de pagina niet opnieuw wordt geladen tijdens de gebeurtenis [submit]. Men had ook het volgende kunnen schrijven:
- een <b-form>-tag zonder afhandeling van de gebeurtenis [submit];
- een <b-button>-tag met de gebeurtenis [@click=’login’] en zonder het attribuut [type=’submit’];
Dat werkt ook. Het voordeel van de gekozen oplossing is dat het verzenden niet alleen gebeurt door op de knop [Valider] te klikken, maar ook door op de validatietoets (toets [Entrée]) in de invoervelden te drukken. De oplossing [<b-form @submit.prevent="login">] is hier dus gekozen voor het gebruiksgemak;
- regels 33-37: een waarschuwing die verschijnt wanneer de server de door de gebruiker ingevoerde inloggegevens heeft afgewezen:

- regel 35: de knop [Valider] is niet altijd actief. De status ervan hangt af van het berekende attribuut [valid] in de regels 71-73. Het attribuut [valid] is waar als:
- er iets in de velden [user, password] van het formulier staat;
- de sessie jSON is gestart. In eerste instantie is deze sessie niet gestart (regel 59) en daarom is de knop [Valider] inactief.
- regels 49-60: de status van de weergave;
- [user] vertegenwoordigt de invoer van de gebruiker in het veld [user] (regels 12-17) van het formulier. De instructie [v-model] op regel 15 stelt een bidirectionele koppeling tot stand tussen de invoer van de gebruiker en het attribuut [user] van de weergave;
- [password] vertegenwoordigt de invoer van de gebruiker in het veld [password] (regels 19-24) van het formulier. De instructie [v-model] op regel 22 stelt een bidirectionele koppeling in tussen de invoer van de gebruiker en het attribuut [password] van de weergave;
- [showError] regelt (regel 29) de weergave van de waarschuwing in de regels 26-31;
- [message] is de foutmelding (regel 31) die in de waarschuwing van de regels 26-31 moet worden weergegeven;
- [sessionStarted] geeft aan of de sessie jSON met de server al dan niet is gestart. Aanvankelijk heeft dit attribuut de waarde [false] (regel 59). De sessie jSON met de server wordt geïnitialiseerd in de gebeurtenis [created] van de levenscyclus van de weergave, regels 126-156. Als de server positief reageert, wordt het attribuut [sessionStarted] gewijzigd in [true] (regel 149);
- regels 126-156: de functie [created] wordt uitgevoerd wanneer de weergave [Authentification] is aangemaakt (maar nog niet per se weergegeven). Op de achtergrond wordt dan een sessie jSON met de server geïnitialiseerd. We weten dat dit de eerste handeling is die moet worden uitgevoerd met de server voor de belastingberekening. Hiervoor wordt de laag [dao] van de applicatie gebruikt (regel 134). Alle methoden van deze laag zijn asynchroon. Hier wordt gebruikgemaakt van de Promise die wordt geretourneerd door de methode [$dao.initSession], die de sessie jSON met de server initialiseert.
- regels 138-150: de code die wordt uitgevoerd wanneer de server zijn antwoord zonder fouten heeft teruggestuurd;
- regel 142: de eigenschap [état] van het antwoord wordt gecontroleerd. Deze moet de waarde [700] hebben als de bewerking is geslaagd. Zo niet, dan is er een fout opgetreden waarvan de oorzaak wordt aangegeven in de eigenschap [response.réponse] (regel 144). Vervolgens wordt de foutmelding van de weergave weergegeven (regel 145);
- regel 149: we zien dat de sessie jSON is gestart;
- regels 152-155: de code die bij een fout wordt uitgevoerd. Deze fout wordt doorgegeven aan de bovenliggende weergave [Main], die
- de fout weergeeft;
- het wachtbericht zal verbergen;
- de doorgestuurde weergave, de weergave [Autentification], verbergt;
- regels 79-124: de methode [login] verwerkt de klik op de knop [Valider];
- regel 79: de methode is voorzien van het voorvoegsel [async] om het gebruik van het sleutelwoord [await] mogelijk te maken, regels 84 en 103;
- regels 84-87: blokkerende aanroep van de methode [$dao.authentifierUtilisateur(user, password)]. We hadden een belofte [Promise] kunnen gebruiken, zoals in de functie [created] is gedaan. We wilden wat variatie in de stijl aanbrengen. Er is geen risico dat de gebruiker wordt geblokkeerd, omdat we bij alle verzoeken HTTP een [timeout] van 2 seconden hebben ingesteld. Hij zal niet lang hoeven te wachten. Bovendien kan hij niets doen zolang de server geen antwoord heeft gegeven, omdat de knop [Valider] dan inactief blijft;
- regel 91: de belastingberekeningsserver verstuurt antwoorden met de structuur jSON, die allemaal de structuur [{‘action’:action, ‘état’:val, ‘réponse’:réponse}] hebben. De authenticatie is geslaagd als [état==200]. Als dat niet het geval is, wordt er een foutmelding weergegeven, regels 93-94;
- regel 98: een eventuele foutmelding van een eerdere bewerking wordt verborgen;
- regels 99-116: er worden nu gegevens van de belastingdienst opgevraagd bij de server waarmee de belasting kan worden berekend. In [this.$métier] hebben we een instantie van de klasse [Métier] die op dit moment niets kan doen omdat deze gegevens ontbreken;
- regel 103: de gegevens van de belastingdienst worden via een blokkerende bewerking bij de server opgevraagd;
- regels 107-112: het antwoord van de server wordt geanalyseerd. De statuswaarde moet gelijk zijn aan 1000; anders is er een fout opgetreden. In dat laatste geval wordt de foutmelding weergegeven (regels 109-110);
- regels 113-118: als de bewerking is geslaagd, wordt:
- verbergt men de foutmelding, regel 114;
- de gegevens van de belastingdienst doorgeven aan de laag [métier] (regel 116);
- wordt de weergave [CalculImpot] weergegeven, regel 118. We herinneren ons dat [this.$router] de router van de applicatie aanduidt. Met de methode [push] kan de volgende gerouteerde weergave worden vastgelegd. Hier wordt deze aangeduid met het attribuut [name]. We hadden deze ook kunnen aanduiden met het attribuut [path]. Deze informatie staat in het routeringsbestand:
// berekening van de belasting
{
path: '/calcul-impot', name: 'calculImpot', component: CalculImpot
},
- regels 119-122: de [catch] wordt geactiveerd wanneer een van de twee verzoeken HTTP is mislukt (server niet aanwezig, time-out overschreden, ...). De fout wordt vervolgens gemeld aan de bovenliggende weergave [Main], die de fout weergeeft, het wachtbericht verbergt en de weergave [Authentification] verbergt;
18.4.4. De weergave [CalculImpot]
De weergave [CalculImpot] ziet er als volgt uit:

- [1]: een navigatiemenu neemt de linkerkolom van de gerouteerde weergave in beslag;
- [2]: het formulier voor de belastingberekening beslaat de rechterkolom van de gerouteerde weergave;
De code van de weergave [CalculImpot] is als volgt:
<!-- definitie HTML van de weergave -->
<template>
<div>
<Layout :left="true" :right="true">
<!-- formulier voor de belastingberekening aan de rechterkant -->
<FormCalculImpot slot="right" @resultatObtenu="handleResultatObtenu" />
<!-- navigatiemenu aan de linkerkant -->
<Menu slot="left" :options="options" />
</Layout>
<!-- weergavegebied voor de resultaten van de belastingberekening onder het formulier -->
<b-row v-if="résultatObtenu" class="mt-3">
<!-- leeg gebied met drie kolommen -->
<b-col cols="3" />
<!-- gebied met negen kolommen -->
<b-col cols="9">
<b-alert show variant="success">
<span v-html="résultat"></span>
</b-alert>
</b-col>
</b-row>
</div>
</template>
<script>
// importen
import FormCalculImpot from "./FormCalculImpot";
import Menu from "./Menu";
import Layout from "./Layout";
export default {
// interne status
data() {
return {
// menuopties
options: [
{
text: "Liste des simulations",
path: "/liste-des-simulations"
},
{
text: "Fin de session",
path: "/fin-session"
}
],
// resultaat van de belastingberekening
résultat: "",
résultatObtenu: false
};
},
// gebruikte componenten
components: {
Layout,
FormCalculImpot,
Menu
},
// methoden voor gebeurtenisbeheer
methods: {
// resultaat van de belastingberekening
handleResultatObtenu(résultat) {
// het resultaat wordt samengesteld in een reeks HTML
const impôt = "Montant de l'impôt : " + résultat.impôt + " euro(s)";
const décôte = "Décôte : " + résultat.décôte + " euro(s)";
const réduction = "Réduction : " + résultat.réduction + " euro(s)";
const surcôte = "Surcôte : " + résultat.surcôte + " euro(s)";
const taux = "Taux d'imposition : " + résultat.taux;
this.résultat =
impôt +
"<br/>" +
décôte +
"<br/>" +
réduction +
"<br/>" +
surcôte +
"<br/>" +
taux;
// weergave van het resultaat
this.résultatObtenu = true;
// ---- update van de store [Vuex]
// een simulatie van +
this.$store.commit("addSimulation", résultat);
}
}
};
</script>
Opmerkingen
- regel 4: de twee kolommen van [Layout] zijn hier aanwezig;
- regel 6: het formulier voor de belastingberekening neemt de rechterkolom in beslag. Het genereert de gebeurtenis [resultatObtenu] zodra het resultaat van de belastingberekening is verkregen. Merk op dat de namen van gebeurtenissen en de namen van de methoden die deze beheren geen accenten mogen bevatten;
- regel 8: het navigatiemenu neemt de linkerkolom in beslag;
- regels 11-20: het resultaat van de belastingberekening wordt onder het formulier weergegeven:

- regel 11: het resultaat wordt alleen weergegeven als het attribuut [résultatObtenu] (regel 47) de waarde [true] heeft;
- regels 34-48: de status van de weergave:
- [options]: de lijst met opties van het navigatiemenu. Deze tabel wordt als parameter doorgegeven aan de component [Menu], regel 8;
- [résultat]: het resultaat van de belastingberekening. Dit resultaat is een tekenreeks HTML. Daarom is de richtlijn [v-html] op regel 17 gebruikt om het weer te geven;
- [résultatObtenu]: de booleaanse waarde die bepaalt of het resultaat wordt weergegeven, regel 11;
- regels 59-81: de methode [handleResultatObtenu] geeft het resultaat weer van de belastingberekening die door de onderliggende weergave [FormCalculImpot] is doorgegeven, regel 6. Dit resultaat is een object met de eigenschappen [impot, décôte, réduction, surcôte, taux, marié, enfants, salaire];
- regels 61-75: het object [impot, décôte, réduction, surcôte, taux] wordt in een tekst HTML geschreven, die wordt weergegeven door regel 17 van de sjabloon;
- regel 77: dit resultaat wordt weergegeven;
- regel 80: de mutatie [addSimulation] van de Vuex-store wordt aangeroepen, waardoor [résultat] wordt toegevoegd aan de simulaties die al in de store aanwezig zijn;
18.4.5. Het navigatiemenu [Menu]
Het navigatiemenu wordt weergegeven in de linkerkolom van de gerouteerde weergaven:

De code van de weergave [Menu] is als volgt:
<!-- definitie HTML van de weergave -->
<template>
<!-- verticaal Bootstrap-menu -->
<b-nav vertical>
<!-- menuopties -->
<b-nav-item
v-for="(option,index) of options"
:key="index"
:to="option.path"
exact
exact-active-class="active"
>{{option.text}}</b-nav-item>
</b-nav>
</template>
<script>
export default {
// instellingen van de weergave
props: {
options: {
type: Array
}
}
};
</script>
Opmerkingen
- de menuopties worden geleverd door de parameter [options] (regels 7, 20-22);
- elk element van de tabel [options] heeft een eigenschap [text] (regel 12) die de tekst van de link is, en een eigenschap [path] (regel 9) die het pad naar de doelweergave van de link aangeeft;
18.4.6. De weergave [FormCalculImpot]
Deze weergave bevat het formulier voor de belastingberekening:

De code ervan is als volgt:
<!-- definitie HTML van de weergave -->
<template>
<!-- formulier HTML -->
<b-form @submit.prevent="calculerImpot" class="mb-3">
<!-- bericht over 12 kolommen op een blauwe achtergrond -->
<b-alert show variant="primary">
<h4>Remplissez le formulaire ci-dessous puis validez-le</h4>
</b-alert>
<!-- formulierelementen -->
<!-- eerste regel -->
<b-form-group label="Etes-vous marié(e) ou pacsé(e) ?" label-cols="4">
<!-- keuzerondjes in 5 kolommen-->
<b-col cols="5">
<b-form-radio v-model="marié" value="oui">Oui</b-form-radio>
<b-form-radio v-model="marié" value="non">Non</b-form-radio>
</b-col>
</b-form-group>
<!-- tweede regel -->
<b-form-group label="Nombre d'enfants à charge" label-cols="4" label-for="enfants">
<b-input
type="text"
id="enfants"
placeholder="Indiquez votre nombre d'enfants"
v-model="enfants"
:state="enfantsValide"
/>
<!-- eventuele foutmelding -->
<b-form-invalid-feedback :state="enfantsValide">Vous devez saisir un nombre positif ou nul</b-form-invalid-feedback>
</b-form-group>
<!-- derde regel -->
<b-form-group
label="Salaire annuel"
label-cols="4"
label-for="salaire"
description="Arrondissez à l'euro inférieur"
>
<b-input
type="text"
id="salaire"
placeholder="Salaire annuel"
v-model="salaire"
:state="salaireValide"
/>
<!-- eventuele foutmelding -->
<b-form-invalid-feedback :state="salaireValide">Vous devez saisir un nombre positif ou nul</b-form-invalid-feedback>
</b-form-group>
<!-- vierde regel, knop [submit] in 5 kolommen -->
<b-col cols="5">
<b-button type="submit" variant="primary" :disabled="formInvalide">Valider</b-button>
</b-col>
</b-form>
</template>
<!-- script -->
<script>
export default {
// interne status
data() {
return {
// getrouwd of niet
marié: "non",
// aantal kinderen
enfants: "",
// jaarsalaris
salaire: ""
};
},
// berekende interne status
computed: {
// validatie van het formulier
formInvalide() {
return (
// salaris ongeldig
!this.salaireValide ||
// of ongeldige kinderen
!this.enfantsValide ||
// of fiscale gegevens niet verkregen
!this.$métier.taxAdminData
);
},
// validatie van het salaris
salaireValide() {
// moet een getal zijn >=0
return Boolean(this.salaire.match(/^\s*\d+\s*$/));
},
// validatie van kinderen
enfantsValide() {
// moet een getal zijn >=0
return Boolean(this.enfants.match(/^\s*\d+\s*$/));
}
},
// gebeurtenisbeheerder
methods: {
calculerImpot() {
// de belasting wordt berekend met behulp van de laag [métier]
const résultat = this.$métier.calculerImpot(
this.marié,
this.enfants,
this.salaire
);
// eslint-disable-next-line
console.log("résultat=", résultat);
// het resultaat wordt aangevuld
résultat.marié = this.marié;
résultat.enfants = this.enfants;
résultat.salaire = this.salaire;
// het [resultatObtenu]-gebeurtenis wordt gegenereerd
this.$emit("resultatObtenu", résultat);
}
}
};
</script>
Opmerkingen
- regels 4-51: het Bootstrap-formulier;
- regels 11-17: een groep keuzerondjes met hun labels;
- regels 14-15: de tag <b-form-radio> zorgt ervoor dat er een keuzerondje wordt weergegeven:
- regel 14: de richtlijn [v-model] zorgt ervoor dat bij een klik op de knop het attribuut [marié] op regel 61 de waarde [oui] krijgt (attribuut [value="oui"]);
- regel 15: de instructie [v-model] zorgt ervoor dat bij een klik op de knop het attribuut [marié] van regel 61 de waarde [non] krijgt (attribuut [value="non"]);
- regels 19-29: het gedeelte voor het invoeren van het aantal kinderen:
- regel 24: de invoer van het aantal kinderen is gekoppeld aan het attribuut [enfants] van regel 63;
- regel 25: de geldigheid van de invoer wordt gecontroleerd door het berekende attribuut [enfantsValide] van de regels 87-89;
- regel 28: zorgt ervoor dat er een foutmelding wordt weergegeven als de invoer ongeldig is;
- regels 31-45: het invoergedeelte voor het jaarsalaris:
- regel 35: geeft een helpbericht weer direct onder het invoerveld;
- regel 41: de invoer van het salaris is gekoppeld aan het attribuut [salaire] van regel 65;
- regel 42: de geldigheid van de invoer wordt gecontroleerd door het berekende attribuut [salaireValide] van de regels 82-85;
- regel 45: zorgt ervoor dat er een foutmelding wordt weergegeven als de invoer ongeldig is;
- regels 48-50: een knop van het type [submit]. Wanneer op deze knop wordt geklikt of wanneer een invoer wordt bevestigd met de toets [Entrée], wordt de methode [calculerImpot] uitgevoerd (regel 94);
- regel 49: de status van de knop (actief/inactief) wordt bepaald door het berekende attribuut [formInvalide] in de regels 71-80;
- regels 71-80: het formulier is geldig als:
- het aantal kinderen geldig is;
- het salaris geldig is;
- de applicatie de gegevens van de belastingdienst van de server heeft ontvangen waarmee de belasting kan worden berekend. Ter herinnering: deze gegevens worden opgeslagen in de eigenschap [$métier.taxAdminData]. De weergave [FormCalculImpot] kan worden weergegeven voordat deze gegevens zijn opgehaald, omdat deze asynchroon worden opgevraagd op hetzelfde moment dat de weergave wordt getoond. Hier wordt ervoor gezorgd dat de gebruiker niet op de knop [Valider] kan klikken zolang de gegevens nog niet zijn opgehaald;
- regels 94-109: de methode voor het berekenen van de belasting:
- regels 96-100: de laag [métier] voert deze berekening uit. Dit is een synchrone berekening. Zodra de gegevens [taxAdminData] zijn opgehaald, hoeft de client [Vue] niet meer met de server te communiceren. Alles gebeurt lokaal. Er wordt een object [résultat] verkregen met de eigenschappen van [impôt, décôte, surcôte, réduction, taux];
- regels 104-106: de eigenschappen [marié, enfants, salaire] worden aan het resultaat toegevoegd;
- regel 108: het resultaat wordt via de gebeurtenis [resultatObtenu] doorgegeven aan de bovenliggende weergave [CalculImpot]. Deze weergave is verantwoordelijk voor het weergeven van het resultaat;
18.4.7. De weergave [ListeSimulations]
De weergave [ListeSimulations] toont de lijst met simulaties die door de gebruiker zijn uitgevoerd:

De code van de weergave is als volgt:
<!-- definitie HTML van de weergave -->
<template>
<div>
<!-- pagina-indeling -->
<Layout :left="true" :right="true">
<!-- simulaties in de rechterkolom -->
<template slot="right">
<template v-if="simulations.length==0">
<!-- geen simulaties -->
<b-alert show variant="primary">
<h4>Votre liste de simulations est vide</h4>
</b-alert>
</template>
<template v-if="simulations.length!=0">
<!-- er zijn simulaties -->
<b-alert show variant="primary">
<h4>Liste de vos simulations</h4>
</b-alert>
<!-- tabel met simulaties -->
<b-table striped hover responsive :items="simulations" :fields="fields">
<template v-slot:cell(action)="data">
<b-button variant="link" @click="supprimerSimulation(data.index)">Supprimer</b-button>
</template>
</b-table>
</template>
</template>
<!-- navigatiemenu in de linkerkolom -->
<Menu slot="left" :options="options" />
</Layout>
</div>
</template>
<script>
// importen
import Layout from "./Layout";
import Menu from "./Menu";
export default {
// componenten
components: {
Layout,
Menu
},
// interne status
data() {
return {
// opties van het navigatiemenu
options: [
{
text: "Calcul de l'impôt",
path: "/calcul-impot"
},
{
text: "Fin de session",
path: "/fin-session"
}
],
// tabelparameters HTML
fields: [
{ label: "#", key: "id" },
{ label: "Marié", key: "marié" },
{ label: "Nombre d'enfants", key: "enfants" },
{ label: "Salaire", key: "salaire" },
{ label: "Impôt", key: "impôt" },
{ label: "Décôte", key: "décôte" },
{ label: "Réduction", key: "réduction" },
{ label: "Surcôte", key: "surcôte" },
{ label: "", key: "action" }
]
};
},
// berekende interne status
computed: {
// lijst met simulaties uit de Vuex-store
simulations() {
return this.$store.state.simulations;
}
},
// methoden
methods: {
supprimerSimulation(index) {
// eslint-disable-next-line
console.log("supprimerSimulation", index);
// verwijdering van simulatie nr. [index]
this.$store.commit("deleteSimulation", index);
}
}
};
</script>
Opmerkingen
- regel 5: de weergave beslaat beide kolommen van de lay-out [Layout] van de gerouteerde weergaven;
- regels 7-26: de simulaties worden in de rechterkolom weergegeven;
- regel 28: het navigatiemenu wordt in de linkerkolom weergegeven;
- regels 8, 14, 20, 75: de simulaties zijn afkomstig uit de opslag [Vuex] [$this.store];
- regels 8-13: er wordt een waarschuwing weergegeven wanneer de lijst met simulaties leeg is;
- regels 14-25: de tabel HTML wordt weergegeven wanneer de lijst met simulaties niet leeg is;
- regels 20-24: de tabel HTML wordt gegenereerd door een <b-table>-tag;
- regel 20: de simulatietabel wordt geleverd door het berekende attribuut [simulations] van de regels 74-76;
- regel 20: de configuratie van de tabel HTML wordt bepaald door het berekende attribuut [fields] van de regels 58-69. Regel 67: de sleutelkolom [action] is de laatste kolom van de tabel HTML;
- regels 21-23: sjabloon van de laatste kolom van de tabel HTML;
- regel 22: hier plaatsen we een knop van het type link. Wanneer hierop wordt geklikt, wordt de methode [supprimerSimulation(data.index)] aangeroepen, waarbij [data] de huidige regel vertegenwoordigt (regel 21). [data.index] vertegenwoordigt het nummer van deze regel in de lijst met weergegeven regels;
- regel 28: het navigatiemenu wordt gegenereerd. De opties hiervan worden geleverd door het attribuut [options] van de regels 47-56;
- regels 80-85: de methode die reageert op een klik op een link [Supprimer] op de pagina HTML;
- regel 84: de mutatie [deleteSimulation] van de store [Vuex] wordt aangeroepen (zie paragraaf |vuejs-15|);
18.5. Uitvoering van het project

De server [Laragon] moet ook worden gestart (zie document |https://tahe.developpez.com/tutoriels-cours/php7|) om de belastingberekeningsserver online te zetten.
18.6. De applicatie op een lokale server implementeren
Momenteel is onze client [Vue] geïmplementeerd op een testserver met de namen URL en [http://localhost:8080]. We gaan deze implementeren op de server [Laragon] op URL [http://localhost:80]. Er zijn verschillende stappen die moeten worden uitgevoerd om dit te bereiken.
Stap 1
Allereerst zorgen we ervoor dat de client [Vue] wordt geïmplementeerd op de testserver op de URL en [http://localhost:8080/client-vuejs-impot/].
We maken een bestand [vue.config.js] aan in de hoofdmap van ons huidige project [VSCode]:

Het bestand [vue.config.js] [1] zal de volgende inhoud hebben:
// vue.config.js
module.exports = {
// de URL van de klantdienst [vuejs] van de belastingberekeningsserver
publicPath: '/client-vuejs-impot/'
}
We moeten ook het routeringsbestand [router.js] [2] aanpassen:
// imports
import Vue from 'vue'
import VueRouter from 'vue-router'
// de weergaven
import Authentification from './views/Authentification'
import CalculImpot from './views/CalculImpot'
import ListeSimulations from './views/ListeSimulations'
// routeringsplugin
Vue.use(VueRouter)
// applicatieroutes
const routes = [
// authenticatie
{
path: '/', name: 'authentification', component: Authentification
},
// belastingberekening
{
path: '/calcul-impot', name: 'calculImpot', component: CalculImpot
},
// lijst met simulaties
{
path: '/liste-des-simulations', name: 'listeSimulations', component: ListeSimulations
},
// sessie beëindigen
{
path: '/fin-session', name: 'finSession', component: Authentification
}
]
// de router
const router = new VueRouter({
// de routes
routes,
// de weergavemodus van de routes in de browser
mode: 'history',
// de basis-URL van de applicatie
base: '/client-vuejs-impot/'
})
// de router exporteren
export default router
- regel 39: we geven de router aan dat de paden van de routes die in de regels 13-30 zijn gedefinieerd, relatief zijn ten opzichte van het pad dat in regel 39 is gedefinieerd. Het pad van regel 20, [/calcul-impot], wordt bijvoorbeeld [/client-vuejs-impot/calcul-impot];
Vervolgens kan het project [vuejs-20] opnieuw worden getest om te controleren of de paden van de applicatie zijn gewijzigd:

stap 2
We bouwen nu de productieversie van het project [vuejs-20]:

- in [1-2] configureren we de taak [build] [2] in het bestand [package.json] [1];
- in [3-5] voeren we deze taak uit. Deze taak bouwt de productieversie van het project [vuejs-20];
De taak [build] wordt uitgevoerd in een terminal van [VSCode]:


- in [3-6] geven waarschuwingen aan dat de gegenereerde code te groot is en dat deze moet worden opgesplitst in [8]. Dit heeft te maken met de optimalisatie van de code-architectuur, wat we hier niet zullen bespreken;
- in [7] wordt aangegeven dat de map [dist] de gegenereerde productieversie bevat:

- in [3] is het bestand [index.html] het bestand dat zal worden gebruikt wanneer URL of [https://localhost:80/client-vue-js-impot/] wordt opgevraagd;
We hebben hier een statische website die op elke willekeurige server kan worden geïmplementeerd. We gaan deze implementeren op de lokale Laragon-server (zie document |https://tahe.developpez.com/tutoriels-cours/php7|). De map [dist] [2] wordt gekopieerd naar de map [<laragon>/www] [4], waarbij <laragon> de installatiemap van de Laragon-server is. We hernoemen deze map naar [client-vuejs-impot] [5], aangezien we de productieversie hebben geconfigureerd om te draaien op URL [/client-vuejs-impot/].
stap 3
We voegen in de zojuist aangemaakte map [client-vuejs-impot] het volgende bestand [.htaccess] toe:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /client-vuejs-impot/
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /client-vuejs-impot/index.html [L]
</IfModule>

Dit is een configuratiebestand voor de Apache-webserver. Als we dit bestand niet toevoegen en direct URL [https://localhost/client-vuejs-impot/calcul-impot] opvragen, zonder eerst via URL [https://localhost/client-vuejs-impot/] te gaan, krijgen we een 404-foutmelding. Met dit bestand krijgen we inderdaad de pagina [CalculImpot] te zien.
Nu we dit hebben gedaan, starten we de Laragon-server als dat nog niet is gebeurd en vragen we de pagina URL [https://localhost/client-vuejs-impot/] op:

De lezer wordt uitgenodigd om de productieversie van onze applicatie te testen.
We kunnen de belastingberekeningsserver op één punt aanpassen: de headers die hij systematisch naar zijn clients verstuurt. Dit was nodig voor de clientversie die werd uitgevoerd vanuit het domein [localhost:8080]. Nu zowel de client als de server in het domein [localhost:80] draaien, zijn de headers CORS overbodig geworden.
We passen het bestand [config.json] van versie 14 van de server aan:

- in [4], en geven aan dat verzoeken met CORS voortaan worden geweigerd;
We slaan deze wijziging op en vragen opnieuw de URL [https://localhost/client-vuejs-impot/] aan. Dit zou nu weer moeten werken.
18.7. Beheer van handmatige URL-verzoeken
In plaats van netjes de links in het navigatiemenu te gebruiken, kan de gebruiker de URL-verzoeken van de applicatie handmatig in de adresbalk van de browser willen invoeren. Laten we bijvoorbeeld de URL [https://client-vuejs-impot/calcul-impot] opvragen zonder via de authenticatiepagina te gaan. Een hacker zou dit zeker proberen. We krijgen dan het volgende scherm te zien:

We krijgen inderdaad het scherm met de belastingberekening te zien. Laten we nu eens proberen de invoervelden in te vullen en te valideren:

We ontdekken dan dat de knop [1] [Valider] altijd uitgeschakeld blijft, zelfs als de invoer correct is. Laten we de code van de weergave [FormCalculImpot] eens bekijken:
<b-col cols="5">
<b-button type="submit" variant="primary" :disabled="formInvalide">Valider</b-button>
</b-col>
Op regel 2 zien we dat de actieve/inactieve status afhankelijk is van de eigenschap [formInvalide]. Dit is de volgende berekende eigenschap:
formInvalide() {
return (
// ongeldig salaris
!this.salaireValide ||
// of ongeldige kinderen
!this.enfantsValide ||
// of fiscale gegevens niet verkregen
!this.$métier.taxAdminData
);
},
Op regel 8 zien we dat het formulier alleen geldig is als de fiscale gegevens zijn verkregen. Deze worden echter verkregen tijdens de validatie van de weergave [Authentification], die de gebruiker heeft ‘overgeslagen’. Hij zal het formulier dus niet kunnen valideren. Als hij dat wel had kunnen doen, zou hij een foutmelding van de server hebben ontvangen waarin stond dat hij niet was geauthenticeerd. Controles moeten altijd aan de serverzijde worden uitgevoerd. Controles aan de browserzijde kunnen altijd worden omzeild. Het volstaat om een client van het type [Postman] te gebruiken die onbewerkte verzoeken naar de server stuurt.
Laten we nu URL en [https://localhost/client-vuejs-impot/liste-des-simulations] opvragen. We krijgen dan het volgende scherm te zien:

Nu de URL en [https://localhost/client-vuejs-impot/fin-session]. We krijgen het volgende beeld te zien:

Nu een weergave die niet bestaat: [https://localhost/client-vuejs-impot/abcd]:

Onze applicatie is redelijk goed bestand tegen handmatig ingevoerde URL-codes. Wanneer deze worden aangeroepen, weet de router van de applicatie dit. Het is dus mogelijk om in te grijpen voordat de weergave uiteindelijk wordt getoond. We gaan dit punt bekijken in het project [vuejs-21].
Een ander punt om te bekijken is het volgende. Stel dat de gebruiker een aantal simulaties volgens de regels heeft uitgevoerd:

Laten we nu de pagina vernieuwen met een F5:

We hebben iets gedaan wat niet wordt aangeraden: de URL handmatig invoeren (F5 invoeren komt hierop neer). Daardoor zijn onze simulaties verloren gegaan.
Het volgende project, [vuejs-21], heeft tot doel twee verbeteringen aan te brengen:
- de door de gebruiker ingevoerde URL controleren;
- de applicatiegegevens bewaren, zelfs als de gebruiker een URL invoert. Hierboven zien we dat we de lijst met simulaties kwijt zijn geraakt;