Inizia gratis

Documentazione · ShadowUI

Il page controller

Documentazione storica, non aggiornata.

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

MetodoDescrizione
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:

  1. Constructor: Creazione istanza
  2. onLoad(options): Caricamento iniziale
  3. onShow(): Pagina mostrata (anche dopo ritorno)
  4. onBack(closedView, info): Ricezione da pagina chiusa
  5. onMessage(message): Ricezione messaggi
  6. canClose(): Verifica prima di chiusura
  7. 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();
}