Controller
An optional base class for your FXML controllers. Extend it and
FlowController starts wiring your controller
automatically: it injects the Stage the view is showing on, records
the viewName it was loaded from, and — the important part — calls
initialize() again every time a cached view is shown,
so screens can refresh themselves without any manual plumbing.
Una clase base opcional para tus controladores FXML. Extiéndela y
FlowController empieza a conectar tu controlador
automáticamente: inyecta el Stage en el que se muestra la vista, registra
el viewName desde el que se cargó y — lo importante — llama a
initialize() otra vez cada vez que se muestra una vista
cacheada, para que las pantallas puedan refrescarse solas sin ningún
cableado manual.
The problem it solvesEl problema que resuelve
FlowController caches one FXMLLoader per
view, which is what makes navigation fast and keeps controllers reachable. But
caching has a catch: JavaFX only calls a controller's initialize()
once, when the FXML is first loaded. Navigate away and come back, and the
screen still shows whatever it held before — stale totals, an old selection, a form
that was never cleared.
FlowController cachea un FXMLLoader por
vista, que es lo que hace la navegación rápida y mantiene los controladores
accesibles. Pero el caché tiene una trampa: JavaFX solo llama al
initialize() de un controlador una vez, cuando el FXML se carga
por primera vez. Navegas a otro lado y vuelves, y la pantalla sigue mostrando lo que
tenía antes — totales obsoletos, una selección vieja, un formulario que nunca se
limpió.
Extending Controller closes that gap. CoreFx detects
the base class with a pattern match and re-invokes initialize() on
every reuse, so "runs when the view is shown" becomes the contract — not "runs once,
ever".
Extender Controller cierra esa brecha. CoreFx detecta la
clase base con un pattern match y vuelve a invocar initialize() en cada
reutilización, así que "se ejecuta cuando la vista se muestra" pasa a ser el contrato
— no "se ejecuta una sola vez, para siempre".
Quick startInicio rápido
public class DashboardController extends Controller {
@FXML private Label totalLabel;
@FXML private TableView<Order> table;
// Called by JavaFX on first load, and by CoreFx on every
// navigation back to this cached view.
@Override
public void initialize() {
totalLabel.setText(orderService.currentTotal());
TableUtils.setItems(table, orderService.findAll());
}
@FXML
private void onClose() {
getStage().close(); // injected by FlowController
}
}
On the first load JavaFX itself calls the no-arg initialize(), as it does for any FXML controller. On every later navigation to that view, FlowController.getLoader(...) finds the cached loader and calls initialize() again. Same method, two callers — so write it to be safely re-runnable.En la primera carga es el propio JavaFX quien llama al initialize() sin argumentos, como hace con cualquier controlador FXML. En cada navegación posterior a esa vista, FlowController.getLoader(...) encuentra el loader cacheado y vuelve a llamar a initialize(). El mismo método, dos llamantes — así que escríbelo para que se pueda re-ejecutar sin problemas.
API referenceReferencia de API
The one method you must implement. Put here whatever the screen needs each time it becomes visible: reload data, reset filters, clear a form. Called once by JavaFX at load time and again by CoreFx on every reuse of the cached loader.El único método que debes implementar. Pon aquí lo que la pantalla necesite cada vez que se hace visible: recargar datos, resetear filtros, limpiar un formulario. Lo llama una vez JavaFX al cargar y otra vez CoreFx en cada reutilización del loader cacheado.
The window this view is currently showing on. Injected (and re-injected) by FlowController on every navigation call, so it always points at the right stage — the main stage, a secondary window, or a modal. Use it to close, resize or title the window from inside the controller.La ventana en la que se muestra esta vista actualmente. FlowController lo inyecta (y reinyecta) en cada llamada de navegación, así que siempre apunta al stage correcto — el principal, una ventana secundaria o un modal. Úsalo para cerrar, redimensionar o titular la ventana desde el propio controlador.
The name the view was loaded under (the FXML file name, without extension). Set by CoreFx the moment the loader is created, so a controller always knows its own identity — handy for logging or for asking FlowController about itself.El nombre con el que se cargó la vista (el nombre del archivo FXML, sin extensión). CoreFx lo asigna en el momento en que se crea el loader, así un controlador siempre conoce su propia identidad — útil para logs o para preguntarle a FlowController por sí mismo.
A free-form slot for the intent behind the navigation — "create" vs "edit", for instance — so one view can serve several modes. CoreFx never writes to it; it is yours to set before navigating and read inside initialize().Un espacio libre para la intención detrás de la navegación — "create" frente a "edit", por ejemplo — para que una misma vista sirva varios modos. CoreFx nunca lo escribe; es tuyo para asignarlo antes de navegar y leerlo dentro de initialize().
Consumes the given key event and fires a synthetic TAB at its source — the usual trick for making Enter advance focus like a data-entry form. The source must be a Control.Consume el evento de teclado dado y dispara un TAB sintético sobre su origen — el truco habitual para que Enter avance el foco como en un formulario de captura. El origen debe ser un Control.
toString(), equals and hashCode are overridden over the three fields (stage, action, viewName).toString(), equals y hashCode están sobrescritos sobre los tres campos (stage, action, viewName).
What FlowController injects, and whereQué inyecta FlowController, y dónde
Every one of these methods checks whether the loaded controller extends Controller and, if so, injects the stage it is navigating to. Controllers that don't extend it are simply left alone — the base class is entirely opt-in.Cada uno de estos métodos comprueba si el controlador cargado extiende Controller y, en tal caso, le inyecta el stage al que está navegando. Los controladores que no la extienden simplemente se dejan intactos — la clase base es totalmente opcional.
| HookPunto de enganche | What CoreFx setsQué asigna CoreFx |
|---|---|
createLoaderInstance(name) | viewName, once, as the loader is built.viewName, una vez, al construir el loader. |
getLoader(viewName) | Calls initialize() when a cached loader is reused.Llama a initialize() cuando se reutiliza un loader cacheado. |
goViewMain · changeViewInMain | stage ← the main stage.stage ← el stage principal. |
goViewInWindow | stage ← the new window.stage ← la ventana nueva. |
goViewInModal · goViewInModalAndWait | stage ← the modal stage.stage ← el stage del modal. |
changeViewInStage · changeViewInScene | stage ← the target stage.stage ← el stage de destino. |
changeViewInBorderPane | stage ← the stage owning the pane.stage ← el stage dueño del panel. |
Because it fires on every visit, treat it as "refresh this screen", not "build this screen once". Adding a listener or a menu item there will add it again on the next visit — do that kind of one-time setup in the controller's constructor, or guard it with a flag. Reloading data, resetting fields and re-binding text is exactly what belongs here.Como se dispara en cada visita, trátalo como "refresca esta pantalla", no como "construye esta pantalla una vez". Añadir ahí un listener o un ítem de menú lo añadirá otra vez en la siguiente visita — haz ese tipo de configuración única en el constructor del controlador, o protégela con un flag. Recargar datos, resetear campos y re-enlazar textos es exactamente lo que corresponde aquí.
RecommendationsRecomendaciones
Do extend Controller for any screen that shows data which can change while the user is elsewhere — lists, dashboards, anything with a total.Extiende Controller en cualquier pantalla que muestre datos que puedan cambiar mientras el usuario está en otro lado — listas, dashboards, cualquier cosa con un total.
Do use getStage() instead of digging a window out of a node (node.getScene().getWindow()) — it is already the right stage, and it stays right across modals.Usa getStage() en vez de sacar la ventana desde un nodo (node.getScene().getWindow()) — ya es el stage correcto, y sigue siéndolo a través de los modales.
Do pair setAction("edit") with FlowController's typed transfer value when one view serves both create and edit modes.Combina setAction("edit") con el valor de transferencia tipado de FlowController cuando una misma vista sirve para crear y editar.