ionic-framework icon indicating copy to clipboard operation
ionic-framework copied to clipboard

Ionic router. OnInit working only once

Open traziusbiztest opened this issue 5 years ago • 50 comments

Bug Report

Ionic version: [x] 4.1.0

Current behavior: Ionic router run OnInit only first time

Expected behavior: Run OnInit each time

I have an app with Angular. It is a simple list of items, after clicking on an item opened item details page. Two routes:

  • IndexComponent. Items list.
  • ItemDetailsComponent. Edit item page
const routes: Routes = [
  {
    path: 'index',
    component: IndexComponent
  },
  {
    path: 'item/:id',
    component: ItemDetailsComponent
  }
];

I use ItemsService -> get/save items.

export class IndexComponent implements OnInit {
  public items: Array<Item>;

  constructor(private itemsService: ItemsService) {
  }

  ngOnInit() {
    this.itemsService.getItems()
    .subscribe(data => this.items = data)
  }
}
export class ItemDetailsComponent implements OnInit {
  public item: Item;

  constructor(private route: ActivatedRoute,
              private itemsService: ItemsService) {
  }

  ngOnInit() {
    const id = this.route.snapshot.params['id'];
    this.itemsService.getItem(id)
    .subscribe(data => this.item = data)
  }
}

In Angular application, OnInit lifecycle event working each time when I open these pages. And it is fine. I should edit an item on ItemDetailsComponent, and after clicking back to IndexComponent I see new information.

I start an Ionic project. Changed router-outlet to ion-router-outlet Same structure, same routing. And now:

  • IndexComponent OnInit working the only first time. When I back to this page from ItemDetailsComponent - OnInit not rerun. Ionic reuse IndexComponent, But I need a new instance.

  • ItemDetailsComponent OnInit working the only first time for each item in the items list. But if I edit an item, return back to IndexComponent, update items handly(click a button, because OnInit not work). And open ItemDetailsComponent again - I see old information because OnInit not firing - this is an old instance.

traziusbiztest avatar Mar 22 '19 09:03 traziusbiztest

I tried to delete

  {
    provide: RouteReuseStrategy,
    useClass: IonicRouteStrategy
  }

from app.module

But OnInit still not working

traziusbiztest avatar Mar 22 '19 09:03 traziusbiztest

https://gyazo.com/6afec1e7936e1254b41009ab942a2d3f

How I can disable this? I need only one component, not stack

traziusbiztest avatar Mar 22 '19 09:03 traziusbiztest

If I use custom RouteReuseStrategy I can't use route animations

traziusbiztest avatar Mar 22 '19 10:03 traziusbiztest

Just subscribe to the params...

 this.route.params
            .subscribe((params: Params) => {
                console.log(params['id']);
});

digaus avatar Mar 22 '19 12:03 digaus

@digaus, This is not working, because of an old component in stack,

traziusbiztest avatar Mar 25 '19 18:03 traziusbiztest

Hi @traziusbiztest,

Thanks for the issue! I can confirm this is a bug. By default, we cache the component when navigating (whereas a regular Angular component destroys it and re-creates it when going back). We have a fix in the works that should resolve issues where routing isn't working after the first time, params aren't being updated, etc.

Work on the fix is well underway, and I hope to have more info to share soon!

Thanks for using Ionic!

liamdebeasi avatar Mar 25 '19 19:03 liamdebeasi

Temporary solution? How to destroy an old component in stack?

traziusbiztest avatar Mar 26 '19 11:03 traziusbiztest

Hi there,

If you are trying to run code every time a particular page enters (or re-enters the view), you can use the ionViewDidEnter lifecycle hook. This hook is "fired when the component being routed to has animated in": https://ionicframework.com/docs/api/router-outlet

If you are trying to respond to changes in data, you could subscribe to the data via an Observable as @digaus suggested, or you can create an injectable that holds the data. With the injectable, you can write a method that serves up the data you need.

For help on implementing either of these ideas, feel free to post on our forums or our slack.

Thanks!

liamdebeasi avatar Mar 26 '19 12:03 liamdebeasi

i totally agree with @liamdebeasi

ngOnInit is only called, when the angular component gets initialised, which is not the case for ionic routing. the previous pages are cached and only disconnected from the change detection. The ionic component lifecycle hooks and the angular router events/observables should fulfill your needs.

It is like ionic navigation is working. Maybe it should be better documentated or clearified in the docs.

KillerCodeMonkey avatar Mar 27 '19 15:03 KillerCodeMonkey

I also stumbled over this bug. I changed the lifecycle events to the ionic ones which works well on components that are accessed via the router (let's call them pages). But on components that are on this page the ionViewWillLeave doesn't seem to be called. So the component that is on a page is not notified if the parent component is going into the cache.

Because of this I cannot unsubscribe an observable that I passed to the sub-component.

Hope this makes sense, I'll try to notify the component via an input that is called on ionViewWillLeave of the parent component.

tobika avatar Mar 27 '19 16:03 tobika

I also detected the same behavior when I tried to follow the tutorial of Maximilian Schwarzmüller @ https://www.youtube.com/watch?v=r2ga-iXS5i4&t=2h57m53s For me, the tutorial doesn't work anymore when it's about trying to delete positions of the list like in https://www.youtube.com/watch?v=r2ga-iXS5i4&t=3h17m0s and change back to the view which lists all positions.

I tried ionViewDidEnter and it worked well for me. Is there any better idea?

TMInnovations avatar Jun 25 '19 19:06 TMInnovations

Same bug here.

Juanperezc avatar Jun 25 '19 23:06 Juanperezc

Hi, I tried to work around with this by using ionic events. I had the same problem when a route is initialized but requires re-authentication(when it tries to retrieve data(by ngOnInit) but instead get a 401 error in return because of expired token(verification happens on my backend)) so navigating to login route. After a successful login, navigate back to the previous URL to retrieve the data again. which result to the OnInit never gets called again after initialization.

I tried to use router NavigationEnd but I encountered problems, like it is firing multiple times. so i decided to use the ionic events.

I just specify the topic on publish(I used the router url as topics) and for every route subscription. so if an event.subscribe hits the right topic. It can execute a certain function(call ngOnInit again).

https://ionicframework.com/docs/v3/api/util/Events/

hope someone might find this helpful. sorry its my first time to post comment.

irealyze avatar Jun 26 '19 07:06 irealyze

Please work on this issue on priority. Many are facing this issue and will start creating workarounds and/or assume ngOnInit will be loaded once and write some logic. When this behavior changes, it will cause issues in the future.

There should be a way to not cache views at all!

thinkdj avatar Jul 01 '19 05:07 thinkdj

It seems that ngOnInit() will fire so long as some parameters change. Using ionDidViewEnter(), doesn't work very well because it fires after the components have been generated, causing errors.

So what I have used, is ugly, but it works. Every time I navigate to a page that I have previously visited, I append a random number (uuid to insure uniqueness) to the parameters. This causes ngOnInit() to fire every time a page is visited.

No perfect, not pretty, but practical.

ramzikorkor avatar Jul 08 '19 20:07 ramzikorkor

5 months still open?

tomriddle1234 avatar Jul 26 '19 16:07 tomriddle1234

Is this bug has fixed ?

Tauqeer1 avatar Aug 08 '19 17:08 Tauqeer1

@Tauqeer1 This has been fixed in the newer versions of Ionic.

thinkdj avatar Aug 08 '19 17:08 thinkdj

It's not fixed, still facing this issue

Zeehond99 avatar Aug 27 '19 15:08 Zeehond99

It's not fixed, still facing this issue

@Papabeer04 Which version ? Fresh install or an upgrade using package.json ?

thinkdj avatar Aug 27 '19 15:08 thinkdj

It seems that ngOnInit() will fire so long as some parameters change. Using ionDidViewEnter(), doesn't work very well because it fires after the components have been generated, causing errors.

So what I have used, is ugly, but it works. Every time I navigate to a page that I have previously visited, I append a random number (uuid to insure uniqueness) to the parameters. This causes ngOnInit() to fire every time a page is visited.

No perfect, not pretty, but practical.

I'm getting: ExpressionChangedAfterItHasBeenCheckedError

Can you show an example of how you did it?

Zeehond99 avatar Aug 27 '19 15:08 Zeehond99

It's not fixed, still facing this issue

@Papabeer04 Which version ? Fresh install or an upgrade using package.json ?

5.2.4

Zeehond99 avatar Aug 27 '19 15:08 Zeehond99

My problem remains with a modal, when I open the modal and close it and go to another page. Then go back to the page and open the modal again ionViewWillEneter and ionViewDidEnter don't work.

ionViewWillEnter fixed my normal pages at the moment.

Zeehond99 avatar Aug 27 '19 17:08 Zeehond99

I used ionViewWillEnter() instead of ngOnInit(). It works,

kamvir avatar Sep 15 '19 04:09 kamvir

I did some investigations. Hope it helps Ionic version @ionic/angular 4.11.1

Actual result I have 2 pages: Welcome, Login and I just navigate forward, don't have any back navigation. Scenario 1

  1. Start app from Welcome page.
  2. Navigate to Login page
  3. Continue navigation to Welcome page, after that navigate to Login page.
  4. From log I could see Login page OnInit event fire 2 times. Work like a champ, I guess because only Welcome is in stack.

Scenario 2

  1. Start app from Login page.
  2. Navigate to Welcome page.
  3. Continue navigation to Login page, after that navigate to Welcome page.
  4. From log I only see Login page OnInit event fire 1 time. Guess right now it is in stack now. can not re-initiate.

Expected Result From log I should see 2 lines of log for Login page OnInit event.

Thanks for improving this framework!


Guys. Please don't say I use ionViewWillEnter and it works. What does temporary solution mean for you? What @liamdebeasi suggest is using ionViewWillEnter when "run code every time a particular page enters" and it is acceptable solution for this case only. How about the other cases? You will get temporary solution and use another temporary solution because this temporary solution make. You might ask why. I will show you how Ionic ionViewWillEnter and Angular ngOnInit different in log. Ionic-Angular already different when it fires component events.

Angular LoginPageComponent constructor ngOnInit ngDoCheck ngAfterContentInit ngAfterContentChecked ngAfterViewInit ngAfterViewChecked

Ionic-Angular LoginPageComponent constructor ngOnInit ngDoCheck ngAfterContentInit ngAfterContentChecked ngAfterViewInit ngAfterViewChecked ngDoCheck ngAfterContentChecked ngAfterViewChecked ngDoCheck ngAfterContentChecked ngAfterViewChecked ionViewWillEnter ngDoCheck ngAfterContentChecked ngAfterViewChecked ionViewDidEnter ngDoCheck ngAfterContentChecked ngAfterViewChecked ngDoCheck ngAfterContentChecked ngAfterViewChecked

lhtrang avatar Oct 17 '19 09:10 lhtrang

My problem remains with a modal, when I open the modal and close it and go to another page. Then go back to the page and open the modal again ionViewWillEneter and ionViewDidEnter don't work.

ionViewWillEnter fixed my normal pages at the moment.

@Papabeer04 , I'm facing with the same issue just as you wrote. Did you able to fix the issue?

When will ionic team fix this issue? As far as I see it's not a new one...

m4szl4g avatar Nov 27 '19 17:11 m4szl4g

Dear All, @traziusbiztest @liamdebeasi @Papabeer04 @kamvir @thinkdj

I have tried all the above suggestions but no luck for me. Please let me know what I need to do as I am also facing same problem.

Why ngOnInit() works only once when I come back to home page the data not displaying. :(

Please help me. Thanks in advance.

Mozib Khan

mozib avatar Dec 04 '19 08:12 mozib

For me the problem was actually in my service. I followed the course of Simon Grimm but he makes a mistake.

(Using firebase)

service.ts :

 constructor(private afs: AngularFirestore) {
    this.ideaCollection = this.afs.collection<Idea>(ideas);
    this.ideas = this.ideaCollection.snapshotChanges().pipe(
      map(actions => {
        return actions.map(a => {
          const data = a.payload.doc.data();
          const id = a.payload.doc.id;
          return { id, ...data };
        });
      })
    );
  }
 
  getIdeas(): Observable<Idea[]> {
    return this.ideas;
  }

Should be:

 constructor(private afs: AngularFirestore) {
    this.ideaCollection = this.afs.collection<Idea>('ideas');

  }
 
  getIdeas(): Observable<Idea[]> {
    return this.ideas = this.ideaCollection.snapshotChanges().pipe(
      map(actions => {
        return actions.map(a => {
          const data = a.payload.doc.data();
          const id = a.payload.doc.id;
          return { id, ...data };
        });
      })
    );
  }

Zeehond99 avatar Dec 04 '19 10:12 Zeehond99

For me the problem was actually in my service. I followed the course of Simon Grimm but he makes a mistake.

(Using firebase)

service.ts :

 constructor(private afs: AngularFirestore) {
    this.ideaCollection = this.afs.collection<Idea>(ideas);
    this.ideas = this.ideaCollection.snapshotChanges().pipe(
      map(actions => {
        return actions.map(a => {
          const data = a.payload.doc.data();
          const id = a.payload.doc.id;
          return { id, ...data };
        });
      })
    );
  }
 
  getIdeas(): Observable<Idea[]> {
    return this.ideas;
  }

Should be:

 constructor(private afs: AngularFirestore) {
    this.ideaCollection = this.afs.collection<Idea>('ideas');

  }
 
  getIdeas(): Observable<Idea[]> {
    return this.ideas = this.ideaCollection.snapshotChanges().pipe(
      map(actions => {
        return actions.map(a => {
          const data = a.payload.doc.data();
          const id = a.payload.doc.id;
          return { id, ...data };
        });
      })
    );
  }

Wow! you are great it works for me.

Thank you so much @Papabeer04

Please get in touch via my skype (chaton_skype)

Thanks,

Mozib

mozib avatar Dec 04 '19 13:12 mozib

Try this:

constructor(
    private zone: NgZone
  ) {}

this.zone.run(() => {
      this.router.navigateByUrl('/login', { skipLocationChange: true });
 });

thebest2019 avatar Mar 13 '20 09:03 thebest2019