Navigation navigateur
Vue d'ensemble
Les projets construits avec le framework OIP naviguent entre les vues via des transitions pilotées par le serveur. En parallÚle, l'interface du navigateur WEB expose ses boutons Précédent et Suivant, que l'utilisateur peut activer à tout moment durant sa progression. Le framework OIP offre une navigation cohérente et configurable pour réconcilier ces deux sources de navigation et garantir la cohérence de l'état applicatif.
Le composant Router expose la prop browserNavigationMode qui contrÎle comment les événements popstate (Précédent / Suivant du navigateur) sont traités. Trois modes sont disponibles :
- standard (défaut) : la navigation navigateur déclenche une transition en ciblant directement la vue encodée dans l'entrée d'historique correspondante. La pile d'historique est réutilisée telle quelle, sans ajout d'entrées supplémentaires ;
- oip : la navigation navigateur transite par le moteur de transitions OIP (onPrev ou onNext selon la direction détectée). à chaque navigation, une nouvelle entrée est ajoutée en fin de pile d'historique ;
- disabled : toute tentative de navigation navigateur est annulée silencieusement, sans appel serveur ni changement de vue.
Le hook useBlockBrowserNav permet de bloquer ponctuellement la navigation navigateur pour une vue spécifique, indépendamment du mode configuré sur le Router.
Prérequis
- Un projet utilisant le framework OIP ;
- Version 4.1.0 ou supérieure de la bibliothÚque @fasstech/oip-starter-utils ;
- L'utilisation du composant Router exposé par la bibliothÚque @fasstech/oip-starter-utils.
Configuration
La navigation navigateur se configure via la prop browserNavigationMode du composant <Router>. En l'absence de cette prop, le mode standard est utilisé par défaut.
// Mode standard (défaut)
<Router views={views} browserNavigationMode="standard" />
// Mode OIP
<Router views={views} browserNavigationMode="oip" />
// Navigation navigateur désactivée
<Router views={views} browserNavigationMode="disabled" />Le mode disabled désactive complÚtement la navigation navigateur de maniÚre silencieuse : à chaque clic sur Précédent ou Suivant, le navigateur est immédiatement remis à sa position courante via history.go(), sans déclencher d'appel serveur ni de changement de vue. L'état applicatif reste intact.
Exemple de configuration
Le composant App est passé en paramÚtre à la fonction start dans le fichier index.js et implémente le composant Router :
import { start as startApp } from '@fasstech/oip-starter-utils/app';
import { App } from './App';
(async () => {
startApp({
app: App,
// ...
});
})();Mode standard
En mode standard, la navigation navigateur est traitée comme une transition directe vers la vue cible encodée dans l'entrée d'historique. C'est le mode recommandé pour les OAV qui souhaitent respecter le comportement de navigation standard WEB.
à noter que dans ce mode, lors d'une navigation navigateur, le moteur de transition OIP et le workflow des vues défini dans le dossier transitions sont ignorés : le navigateur se déplace sur l'entrée relative de la pile d'historique et charge la vue correspondante.
Fonctionnement détaillé
Lorsque l'utilisateur appuie sur Précédent ou Suivant :
- L'événement popstate se déclenche. Le Router lit __oipIdx (compteur monotone permettant de détecter la direction) et __oipViewId (identifiant de la vue cible) depuis l'entrée d'historique de destination ;
- Bounce-back immĂ©diat : history.go(-delta) est appelĂ© pour ramener le navigateur Ă sa position courante pendant le traitement serveur. L'affichage ne change pas â le rendu est pilotĂ© par context.viewId, jamais par l'URL ;
- goTo(destViewId, viewData) (handler interne) est envoyĂ© au serveur. Si l'entrĂ©e d'historique possĂšde un __oipViewDataKey, les donnĂ©es de vue associĂ©es Ă cette position (stockĂ©es en sessionStorage lors de la navigation initiale vers cette entrĂ©e) sont transmises Ă goTo. Chaque entrĂ©e d'historique possĂšde sa propre clĂ© UUID, ce qui permet Ă une mĂȘme vue prĂ©sente Ă plusieurs positions dans la stack de restaurer des donnĂ©es indĂ©pendantes ;
- Lorsque le serveur répond, context.viewId est mis à jour, ce qui déclenche l'effet viewId ;
- L'effet viewId détecte que destIdxRef est renseigné et appelle history.go(delta) pour déplacer le navigateur vers l'entrée d'historique existante, sans créer de nouvelle entrée. oipIdxRef est mis à jour en conséquence.
La pile d'historique n'est jamais étendue lors d'une navigation navigateur en mode standard : le navigateur se déplace simplement vers une entrée déjà présente dans la stack.
Exemple de navigation
ScĂ©nario : l'utilisateur a naviguĂ© dans l'ordre Vue A â Vue B â Vue C via les transitions OIP. La pile d'historique contient trois entrĂ©es.
Etat initial
------------------------------------------------------------
[ Vue A (idx=0) | Vue B (idx=1) | Vue C (idx=2) ]
^
position actuelle (oipIdx=2)
Etape 1 â L'utilisateur appuie sur PrĂ©cĂ©dent
------------------------------------------------------------
Le navigateur se déplace sur l'entrée Vue B (idx=1).
popstate déclenché : destIdx=1, delta=-1.
[ Vue A (idx=0) | Vue B (idx=1) | Vue C (idx=2) ]
^
position navigateur (temporaire)
Etape 2 â Bounce-back : history.go(1)
------------------------------------------------------------
Le navigateur revient sur Vue C (idx=2).
L'affichage reste sur Vue C pendant le traitement serveur.
[ Vue A (idx=0) | Vue B (idx=1) | Vue C (idx=2) ]
^
position restaurée temporairement
Etape 3 â goTo("Vue B") envoyĂ© au serveur
Serveur répond : context.viewId = "Vue B"
viewId effect déclenché.
------------------------------------------------------------
destIdxRef=1 â history.go(-1), navigateur se dĂ©place sur Vue B.
oipIdx mis Ă jour Ă 1.
[ Vue A (idx=0) | Vue B (idx=1) | Vue C (idx=2) ]
^
position finale (oipIdx=1)
La pile est inchangée. L'entrée Vue C reste accessible via Suivant.Mode OIP
En mode oip, la navigation navigateur est traitée comme une transition OIP classique : Précédent appelle onPrev, Suivant appelle onNext. Le serveur évalue et détermine la vue de destination selon le workflow de vues défini. C'est le mode adapté aux OAV qui souhaitent respecter le moteur de transition OIP lors d'une navigation navigateur.
Fonctionnement détaillé
Lorsque l'utilisateur appuie sur Précédent ou Suivant :
- L'événement popstate se déclenche. Le Router lit __oipIdx depuis l'entrée d'historique de destination pour calculer delta = destIdx - oipIdxRef. Un delta négatif indique un appui sur Précédent, un delta positif indique un appui sur Suivant ;
- Bounce-back immédiat : history.go(-delta) est appelé pour ramener le navigateur à sa position courante. L'affichage ne change pas ;
- onPrev() ou onNext() est appelĂ© selon la direction. Les arguments sont toujours un objet vide {} â transitionData et resolvedViewData ne sont pas transmis lors d'une navigation navigateur ;
- Lorsque le serveur répond, context.viewId est mis à jour, ce qui déclenche l'effet viewId ;
- L'effet viewId appelle history.pushState, ce qui ajoute une nouvelle entrée en fin de pile d'historique. oipIdxRef est incrémenté.
Contrairement au mode standard, la pile d'historique grossit à chaque navigation navigateur. Le bouton Suivant est inaccessible aprÚs chaque navigation navigateur car le pushState écrase l'historique en avant.
Exemple de navigation
ScĂ©nario : l'utilisateur a naviguĂ© dans l'ordre Vue A â Vue B â Vue C via les transitions OIP.
Etat initial
------------------------------------------------------------
[ Vue A (idx=0) | Vue B (idx=1) | Vue C (idx=2) ]
^
position actuelle (oipIdx=2)
Etape 1 â L'utilisateur appuie sur PrĂ©cĂ©dent
------------------------------------------------------------
Le navigateur se déplace sur l'entrée Vue B (idx=1).
popstate déclenché : destIdx=1, delta=-1.
[ Vue A (idx=0) | Vue B (idx=1) | Vue C (idx=2) ]
^
position navigateur (temporaire)
Etape 2 â Bounce-back : history.go(1)
------------------------------------------------------------
Le navigateur revient sur Vue C (idx=2).
L'affichage reste sur Vue C pendant le traitement serveur.
[ Vue A (idx=0) | Vue B (idx=1) | Vue C (idx=2) ]
^
position restaurée temporairement
Etape 3 â onPrev({}) envoyĂ© au serveur
Serveur répond : context.viewId = "Vue B"
viewId effect dĂ©clenchĂ© â pushState (oipIdx=3)
------------------------------------------------------------
Une nouvelle entrée Vue B est ajoutée en fin de pile.
oipIdx mis Ă jour Ă 3.
[ Vue A (idx=0) | Vue B (idx=1) | Vue C (idx=2) | Vue B (idx=3) ]
^
position finale (oipIdx=3)
La pile s'est étendue. L'entrée Vue C (idx=2) n'est plus accessible via Suivant.
Etape 4 â L'utilisateur appuie Ă nouveau sur PrĂ©cĂ©dent
------------------------------------------------------------
popstate déclenché : destIdx=2, delta=-1.
Bounce-back, puis onPrev({}) envoyé au serveur.
Serveur répond : context.viewId = "Vue A"
pushState (oipIdx=4)
[ Vue A (idx=0) | Vue B (idx=1) | Vue C (idx=2) | Vue B (idx=3) | Vue A (idx=4) ]
^
position finale (oipIdx=4)Bloquer la navigation navigateur pour une vue spécifique
Le hook useBlockBrowserNav permet de bloquer la navigation navigateur (Précédent / Suivant) pour la durée de vie d'un composant monté. Lorsque le bloc est actif, toute tentative de navigation navigateur est annulée silencieusement via un bounce-back, sans appel serveur ni changement de vue.
Ce hook est efficace en mode oip et standard. En mode disabled, la navigation est déjà bloquée globalement et useBlockBrowserNav n'a aucun effet supplémentaire.
Exemple d'implémentation dans une vue
import { useState } from "react";
import { useProcess, useBlockBrowserNav } from "@fasstech/oip-starter-utils/app";
const MyView = () => {
const { context, processHandlers } = useProcess();
const [isDirty, setIsDirty] = useState(false);
// Block always
useBlockBrowserNav();
// OR Block conditionally (e.g. unsaved changes)
useBlockBrowserNav(isDirty);
return (
<div>
<h1>MY FORM</h1>
...
</div>
);
};Dans cet exemple :
- useBlockBrowserNav() sans argument bloque en permanence la navigation navigateur tant que MyView est monté. C'est utile pour les vues qui nécessitent de bloquer la navigation navigateur de maniÚre permanente et inconditionnelle ;
- useBlockBrowserNav(isDirty) conditionne le blocage à la valeur de isDirty. Tant que l'utilisateur n'a pas sauvegardé ses modifications (isDirty === true), la navigation navigateur est bloquée. DÚs que isDirty repasse à false, le bloc est automatiquement levé sans démontage du composant.
Dans les deux cas, le blocage est levé automatiquement au démontage du composant via le cleanup de l'effet.
Résumé
- Le mode de navigation navigateur se configure via la prop browserNavigationMode de <Router> ; le mode standard est actif par défaut ;
- En mode standard, la navigation navigateur dĂ©clenche directement une transition vers la vue cible et le viewData extraits de l'entrĂ©e d'historique. La pile d'historique n'est jamais Ă©tendue lors d'une navigation navigateur â le navigateur se dĂ©place vers une entrĂ©e existante ;
- En mode oip, la navigation navigateur appelle onPrev ou onNext selon la direction. Une nouvelle entrée est ajoutée à la pile à chaque navigation navigateur. transitionData et resolvedViewData ne sont pas transmis ;
- En mode disabled, toute navigation navigateur est annulée silencieusement sans appel serveur ni changement de vue ;
- Le hook useBlockBrowserNav bloque la navigation navigateur pour une vue spécifique, tant que le composant est monté. Il accepte un paramÚtre booléen optionnel (true par défaut) pour un blocage conditionnel ;
- useBlockBrowserNav ne doit ĂȘtre appelĂ© qu'une seule fois par arbre de vue : plusieurs appels simultanĂ©s produisent un comportement imprĂ©visible au dĂ©montage ;
- Le rendu est toujours piloté par context.viewId (état serveur), jamais par l'URL du navigateur. L'URL est maintenue en synchronisation via pushState et replaceState.