
Olá pessoal! Meu nome é Nikita Zhigamovsky, programadora da
KitApp, e quero falar sobre minha experiência na construção de navegação no Ionic 4: o problema que encontrei e sua solução.
Desenvolvo soluções multiplataforma para aplicativos móveis desde 2018. Eu trabalhava na terceira versão do Ionic, mas, com o passar do tempo, a funcionalidade está se desenvolvendo, decidi mudar para uma versão mais recente, e os momentos e bugs irritantes do modelo anterior no Ionic 4
parecem já
ter sido corrigidos.
Parece que o que poderia dar errado. Finalmente, temos a funcionalidade de um roteamento Angular normal, e não o antigo NavController com todas as suas deficiências. Mesmo no site oficial da Ionic, o guia de roteamento indica que vale a pena navegar pelas páginas de maneira programática usando os métodos
angular / roteador. Mas havia algo que me fez voltar ao antigo NavController.
A essência do problema
Um bug interessante foi notado. Suponha que você tenha um menu lateral, você o criou usando o painel de divisão de íons. Você também tem páginas separadas no menu e deseja ir delas para outras páginas que estão no menu. Navegue usando Router.navigateByUrl ('/ menu / ...'). Em seguida, chamamos a página de menu A e a página é separada do menu - B. Mas há uma
MAS!Suponha que, na página A, uma certa lógica seja acionada no evento ngOnInit. Você navega para a página B usando o roteador e percebe que a página do menu ainda está ativa - ela não foi excluída. Portanto, se você voltar à página A, o evento ngOnInit não funcionará, pois o evento ngOnDestroy desta página não funcionou. Parece que tudo é lógico. Nesses momentos, eles geralmente recorrem a um dos métodos do ciclo de vida, não o angular, mas o ionic - ionViewWillEnter. É acionado quando você acessa a página assim que se torna ativa. Tudo parece estar bem, se encaixa perfeitamente, mas há um certo número de convenções.
Nenhuma das opções para uma ação adequada na página A funcionará ao acessá-la, se essa transição não for das páginas que estão no menu. Você não poderá acompanhar a transição para esta página, porque, repito, ainda está aberta e funciona silenciosamente em outras páginas, por exemplo, na página B.
Alguns exemplos ilustrativos:
ionViewWillEnter funcionará se você tiver a seguinte estrutura de página:
1) Páginas separadas
- página1
- página2
- página3Neste exemplo, o ionViewWillEnter funcionará perfeitamente em todas as páginas. (página1 => página2, página2 => página3 etc.)
2) Menu / Guias
- menu
- menuPage1
- menuPage2
- menuPage3Neste exemplo, tudo ficará bem: o método ionViewWillEnter será acionado toda vez que você for a qualquer uma das páginas (menuPage1 => menuPage2, menuPage1 => menuPage3, etc.).
Mas no exemplo abaixo, tudo é mais complicado:
- menu
- menuPage1
- menuPage2
- menuPage3
- loginPage
- inscriçãoÉ aqui que começam os problemas do roteamento angular padrão. Ao navegar dentro das páginas do menu (menuPage1 => menuPage2 => menuPage3) - o método ionViewWillEnter funcionará como de costume, da mesma maneira ao navegar entre páginas individuais (loginPage => signupPage). Mas assim que começamos a mover-se entre páginas separadas e páginas de menu (loginPage => menu / menuPage1 ou menu / menuPage3 => signupPage), nem o método ngOnInit nem o ionViewWillEnter funcionam. O ngOnInit não funcionará porque a página não foi destruída, o que é lógico. Mas por que o ionViewWillEnter não funcionou?
Com base na documentação, o ionViewWillEnter trabalha em pilhas de roteamento separadas (a palavra-chave “individual”) ou entre páginas individuais ou em menus / guias. Mas não na estrutura mista de páginas e menus / guias individuais. Estranho, mas isso é considerado comportamento normal. Ao mesmo tempo, esse não é exatamente o comportamento que os usuários esperam, especialmente quando você considera o nome dos ganchos do ciclo de vida :).
Então, como resolver esse problema?
Tendo visitado muitos fóruns, mas não tendo visto uma solução normal, e tendo visto alguns hacks duvidosos que nem sempre funcionam, fica claro que algo é necessário. Algo que mudará a funcionalidade de transição entre páginas de qualquer tipo.
O que fazer neste caso? É claro, jogue o roteador no inferno e esqueça-o, porque ainda existe o nosso NavController anteriormente odiado e agora tão bom.
A principal diferença entre o método NavController.navigateRoot () é que, após mudar para outra página, a anterior é automaticamente destruída! E quando você alternar novamente, o método ngOnInit e o ionViewWillEnter funcionarão! De fato - esta é a solução perfeita - sem muletas e funções auto-escritas duvidosas.
O mais legal é que ele funciona com qualquer estrutura de página: mesmo entre normal, mesmo dentro do menu, e até misto, como no exemplo anterior.
Resuma os aspectos positivos:
- O NavController exclui a página anterior da pilha, respectivamente, quando você voltar a ela - ela é atualizada, os métodos ionViewWillEnter e ngOnInit funcionam e você pode chamar a lógica neles novamente e atualizar as informações nas páginas, por exemplo.
- Esqueça os métodos antigos push (), setRoot () e pop (), além de navegar pelos elementos da classe. Afinal, foi isso que criou muitos problemas. Agora, o navCtrl atualizou os métodos, que passam pelo mesmo caminho dos métodos do roteador.
Há uma ressalva, onde fazer sem "MAS" :)
Se adicionarmos um manipulador de eventos ao botão "voltar" do hardware no android e nesse manipulador tentarmos ir a algum lugar usando o roteador ou o navController, obteremos o seguinte erro no console: 'Navegação acionada fora da zona angular'.
Sim, a navegação funcionará, a página será aberta, mas nada funcionará nela - nem a inicialização das propriedades, nem os métodos do ciclo de vida. Infelizmente, a navegação pressionando o botão Voltar é acionada fora da zona Angular e, de fato, abre apenas o modelo: sem vincular variáveis ao modelo, sem funções, ganchos, métodos de ciclo de vida - sem nada.
A solução é muito simples, na verdade. Simplesmente forçamos explicitamente a navegação dentro da zona Angular.
Um exemplo:import { Component, NgZone } from '@angular/core'; import { NavController } from '@ionic/angular'; @Component({ selector: 'app-root', templateUrl: 'app.component.html' }) export class AppComponent { constructor(private navCtrl: NavController, private ngZone: NgZone){} this.ngZone.run(() => this.navCtrl.navigateForward('menu')).then(); this.ngZone.run(() => this.router.navigateByUrl('/menu/my-orders')).then(); }
E agora tudo funciona bem!
Existem muitos artigos interessantes sobre o ngZone, aconselho você a ler. Boa sorte
Um pouco sobre os métodos navController:
- this.navCtrl.setDirection ('root') - define a página raiz na pilha, excluindo todas as anteriores.
- this.navCtrl.navigateRoot ('homePage') - semelhante ao navCtrl.setDirection ('root') + router.navigateByUrl ('homePage'), mas com a remoção obrigatória da página anterior na pilha (é disso que precisamos).
- this.navCtrl.navigateForward ('examplePage') - semelhante ao router.navigateByUrl ('/ examplePage), mas com uma indicação explícita de onde ir + pode excluir a página anterior da pilha.
- this.navCtrl.back () - semelhante ao location.back (), mas com animação.
- this.navCtrl.navigateBack ('backPage') - semelhante ao navCtrl.setDirection ('back') + router.navigateByUrl ('backPage').
Suponha que agora estamos no menu / página1,

e temos uma pilha de menus separada e, depois de passar do menu / página1 para a página de login, precisamos excluir a página do menu / página1 para que, depois de mudar para ela novamente, tenhamos algum tipo de lógica trabalhando em ngOnInit ou ionViewWillEnter. Se usarmos router.navigateByUrl ('login) para a transição, depois estaremos na página de login, mas também teremos uma página de menu,

consequentemente, após alternar do login para o menu / página1, nem o ngOnInit nem o ionViewWillEnter funcionarão.
Se você usar o navCtrl.navigateRoot ('login') para navegar, depois de abrir a página de login, a página anterior será excluída. E os métodos ngOnInit e ionViewWillEnter funcionarão.

Essa é a beleza de usar o navController - o
comportamento esperado é totalmente consistente com o atual .