Table Of Contents
- Metodi principali
- Pattern avanzati
- 1. Navigazione condizionale
- 2. Navigazione con parametri complessi
- 3. Stack profondo con breadcrumb
- 4. Rimozione selettiva dallo stack
- 5. Navigazione modale con popup
- 6. Gestione stati back button
- 7. Preload pagine successive
- 8. Navigazione con transizioni personalizzate
- 9. Comunicazione bidirezionale tra pagine
- 10. Gestione errori navigazione
- Ciclo di vita pagina
Il page controller gestisce la navigazione e il ciclo di vita delle pagine nell’applicazione.
Metodi principali #
I metodi principali del page controller sono già stati descritti nel capitolo Il contenitore pagine (ShaNavController) nella sezione Navigazione: Proprietà e Metodi del Page Controller. Qui riassumiamo i concetti chiave e aggiungiamo pattern avanzati.
Riepilogo metodi #
| Metodo | Descrizione |
|---|---|
| push(viewclass, options) | Apri una nuova pagina |
| pop(count, info) | Torna indietro |
| remove(count, info) | Rimuovi pagine dallo stack |
| toggleMenu(show) | Apri/chiudi sidebar |
| setTitle(title) | Aggiorna titolo (solo Sticky) |
| setBreadcrumb() | Aggiorna breadcrumb |
| getActivePage() | Ottieni pagina attiva |
| postMessage(msg) | Invia messaggio alle pagine |
Pattern avanzati #
1. Navigazione condizionale #
function* openDetail() {
// Controlla permessi prima di navigare
let hasPermission = yield app.checkPermission("view_details");
if (!hasPermission) {
yield app.toast("Accesso negato", { variant: "destructive" });
return;
}
yield view.push(App.DetailView, {
title: "Dettaglio",
itemId: this.itemId
});
}
2. Navigazione con parametri complessi #
function* openEditView() {
yield view.push(App.EditView, {
title: "Modifica Ticket",
// Passa oggetto completo
ticket: {
id: this.ticket.id,
title: this.ticket.title,
status: this.ticket.status,
assignee: this.ticket.assignee
},
// Callback per aggiornamento
onSave: (updatedTicket) => {
this.updateLocalTicket(updatedTicket);
}
});
}
// In EditView - onLoad
function* onLoad() {
let ticket = this.options.ticket;
// Popola form
this.view.titleInput.value = ticket.title;
this.view.statusSelect.value = ticket.status;
}
// In EditView - save
function* saveButton_onClick() {
let updated = yield this.save();
// Chiama callback se fornito
if (this.options.onSave) {
this.options.onSave(updated);
}
yield view.pop(1, { saved: true });
}
3. Stack profondo con breadcrumb #
// Crea navigazione profonda mantenendo breadcrumb
// 1. Dashboard (root)
yield view.push(App.DashboardView, {
root: true,
title: "Dashboard"
});
// 2. Lista progetti
yield view.push(App.ProjectsView, {
title: "Progetti"
});
// 3. Dettaglio progetto
yield view.push(App.ProjectDetailView, {
title: "Progetto Alpha",
projectId: 123
});
// Breadcrumb: Dashboard > Progetti > Progetto Alpha
// 4. Lista task
yield view.push(App.TasksView, {
title: "Task",
projectId: 123
});
// Breadcrumb: Dashboard > Progetti > Progetto Alpha > Task
// 5. Dettaglio task
yield view.push(App.TaskDetailView, {
title: "Task #45",
taskId: 45
});
// Breadcrumb: Dashboard > ... > Progetto Alpha > Task > Task #45
4. Rimozione selettiva dallo stack #
// Scenario: Dashboard → Projects → ProjectDetail → TaskList → TaskDetail
// Vogliamo: Dashboard → ProjectDetail → TaskDetail
// (rimuoviamo Projects e TaskList)
function* saveTaskButton_onClick() {
yield this.saveTask();
// Rimuovi TaskList e Projects dallo stack
yield view.remove(App.TasksView);
yield view.remove(App.ProjectsView);
// Ora lo stack è: Dashboard → ProjectDetail → TaskDetail
yield app.toast("Task salvato", { variant: "success" });
}
5. Navigazione modale con popup #
// Apri form come popup invece che pagina normale
function* quickAddButton_onClick() {
yield view.push(App.QuickAddView, {
popup: true,
title: "Aggiungi rapido",
// Opzioni popup
popupPosition: "center",
popupWidth: "500px",
backdrop: true
});
}
// In QuickAddView - dopo salvataggio
function* saveButton_onClick() {
yield this.save();
// Chiudi popup
this.view.close();
// Notifica pagina sottostante
yield view.postMessage({
type: "item-added",
item: this.savedItem
});
}
// In pagina principale - ricevi notifica
function* onMessage(message) {
if (message.type === "item-added") {
// Ricarica lista
yield this.view.itemsDM.load();
}
}
6. Gestione stati back button #
// Gestisci back button browser/Android
function* canClose() {
// Chiamato quando utente preme back
let hasUnsavedChanges = this.checkUnsavedChanges();
if (hasUnsavedChanges) {
let confirm = yield app.popup(
"Modifiche non salvate",
"Vuoi salvare prima di uscire?",
["Annulla", "Esci senza salvare", "Salva ed esci"]
);
if (confirm === 0) {
return false; // Blocca chiusura
} else if (confirm === 2) {
yield this.save();
}
}
return true; // Permetti chiusura
}
7. Preload pagine successive #
// Precarica dati della pagina successiva per transizioni veloci
function* openDetailButton_onClick() {
let itemId = this.selectedItem.id;
// Avvia caricamento dati in background
let dataPromise = app.api.getItemDetail(itemId);
// Naviga immediatamente
yield view.push(App.DetailView, {
title: "Dettaglio",
itemId: itemId,
preloadedData: dataPromise // Passa promise
});
}
// In DetailView - onLoad
function* onLoad() {
if (this.options.preloadedData) {
// Usa dati precaricati
let data = yield this.options.preloadedData;
this.populateView(data);
} else {
// Carica normalmente
yield this.loadData();
}
}
8. Navigazione con transizioni personalizzate #
// Disabilita animazione per navigazione veloce
yield view.push(App.TargetView, {
title: "Target",
animate: false // Nessuna transizione
});
// Forza animazione anche se disabilitata globalmente
yield view.push(App.TargetView, {
title: "Target",
animate: true
});
9. Comunicazione bidirezionale tra pagine #
// Pagina A apre Pagina B e attende risultato
// In PageA
function* selectUserButton_onClick() {
yield view.push(App.UserPickerView, {
popup: true,
title: "Seleziona Utente",
currentUser: this.currentUser
});
}
function* onMessage(message) {
if (message.type === "user-selected") {
// Ricevi utente selezionato
this.view.userInput.value = message.user.name;
this.selectedUser = message.user;
}
}
// In UserPickerView
function* userRow_onClick() {
let user = this.sender.row;
// Invia risultato a pagina chiamante
yield view.postMessage({
type: "user-selected",
user: user
});
// Chiudi picker
this.view.close();
}
10. Gestione errori navigazione #
function* navigateToDetail() {
try {
let itemId = this.selectedItem.id;
// Verifica esistenza item
let exists = yield app.api.checkItemExists(itemId);
if (!exists) {
yield app.toast("Elemento non trovato", {
variant: "destructive"
});
return;
}
// Naviga
yield view.push(App.DetailView, {
title: "Dettaglio",
itemId: itemId
});
} catch (error) {
yield app.toast(`Errore: ${error.message}`, {
variant: "destructive"
});
console.error("Navigation error:", error);
}
}
Ciclo di vita pagina #
Eventi principali:
- Constructor: Creazione istanza
- onLoad(options): Caricamento iniziale
- onShow(): Pagina mostrata (anche dopo ritorno)
- onBack(closedView, info): Ricezione da pagina chiusa
- onMessage(message): Ricezione messaggi
- canClose(): Verifica prima di chiusura
- onClose(): Chiusura pagina
Esempio completo:
// In una view
function* onLoad() {
console.log("1. onLoad - caricamento iniziale");
// Ricevi parametri
this.itemId = this.options.itemId;
// Carica dati
yield this.loadData();
}
function* onShow() {
console.log("2. onShow - pagina mostrata");
// Aggiorna dati se necessario
if (this.needsRefresh) {
yield this.refresh();
this.needsRefresh = false;
}
}
function* onBack(closedView, info) {
console.log("3. onBack - ritorno da altra pagina");
// Ricevi dati da pagina chiusa
if (info.saved) {
yield this.refresh();
}
}
function* onMessage(message) {
console.log("4. onMessage - messaggio ricevuto");
if (message.type === "data-changed") {
this.needsRefresh = true;
}
}
function* canClose() {
console.log("5. canClose - verifica chiusura");
// Verifica modifiche non salvate
if (this.hasUnsavedChanges()) {
let confirm = yield app.popup(
"Conferma",
"Ci sono modifiche non salvate. Continuare?",
["Annulla", "Esci"]
);
return (confirm === 1);
}
return true;
}
function* onClose() {
console.log("6. onClose - chiusura pagina");
// Cleanup risorse
this.cleanup();
}