I am trying to put together a demo app of Angular 2 + Rx.JS 5/Next.
I noticed that my components are re-instantiated each time I switch the route.
Here is the code for the root app:
import {bootstrap} from 'angular2/platform/browser';
import {HTTP_PROVIDERS} from 'angular2/http';
import {ROUTER_PROVIDERS} from 'angular2/router';
import {AppComponent} from './app.component.ts';
bootstrap(AppComponent, [HTTP_PROVIDERS, ROUTER_PROVIDERS]);
Here is the code for the root component:
import {Component} from 'angular2/core';
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
import {FirstComponent} from './app.first-component.ts';
import {SecondComponent} from './app.second-component.ts';
import {AppService} from "./app.services.ts";
@Component({
selector: 'my-app',
providers: [AppService, FirstComponent, SecondComponent],
directives: [FirstComponent, SecondComponent, ROUTER_DIRECTIVES],
template: `<h1>An Angular 2 App</h1>
<a [routerLink]="['First']">first-default</a>
<a [routerLink]="['Second']">second</a>
<router-outlet></router-outlet>`
})
@RouteConfig([
{path: '/', name: 'First', component: FirstComponent, useAsDefault: true},
{path: '/second', name: 'Second', component: SecondComponent}
])
export class AppComponent {
}
Then the code for the first component (mapped to /
):
import {Component, OnInit, NgZone} from "angular2/core";
import {AppService} from "./app.services.ts";
import 'rxjs/Rx';
@Component({
selector: 'my-first',
template: `
<div>
<ul>
<li *ngFor="#s of someStrings">
a string: {{ s }}
</li>
</ul>
</div>`
})
export class FirstComponent implements OnInit {
zone:NgZone;
constructor(private appService:AppService) {
console.log('constructor', 'first');
this.zone = new NgZone({enableLongStackTrace: false});
}
someStrings:string[] = [];
ngOnInit() {
console.log('ngOnInit', 'first');
this.appService.refCounted.subscribe(
theStrings=> {
this.zone.run(() =>this.someStrings.push(...theStrings));
},
error=>console.log(error)
);
}
}
And the second component (mapped to /second
):
import {Component, OnInit, NgZone} from "angular2/core";
import {AppService} from "./app.services.ts";
@Component({
selector: 'my-second',
template: `
<div>
<ul>
<li *ngFor="#s of someStrings">
a string: {{ s }}
</li>
</ul>
</div>`
})
export class SecondComponent implements OnInit {
zone:NgZone;
constructor(private appService:AppService) {
console.log('constructor', 'second');
this.zone = new NgZone({enableLongStackTrace: false});
}
someStrings:string[] = [];
ngOnInit() {
console.log('ngOnInit', 'second');
this.appService.refCounted.subscribe(
theStrings=> {
this.zone.run(() =>this.someStrings.push(...theStrings));
},
error=>console.log(error)
);
}
}
And finally the app service (slightly less relevant to this question):
import {Injectable} from "angular2/core";
import {Observable} from "rxjs/Observable";
import {Subject} from "rxjs/Subject";
import 'rxjs/Rx';
@Injectable()
export class AppService {
constructor(){
console.log('constructor', 'appService');
}
someObservable$:Observable<string[]> = Observable.create(observer => {
const eventSource = new EventSource('/interval-sse-observable');
eventSource.onmessage = x => observer.next(JSON.parse(x.data));
eventSource.onerror = x => observer.error(console.log('EventSource failed'));
return () => {
eventSource.close();
};
});
subject$ = new Subject();
refCounted = this.someObservable$.multicast(this.subject$).refCount();
someMethod_() {
let someObservable$:Observable<string[]> = Observable.create(observer => {
const eventSource = new EventSource('/interval-sse-observable');
eventSource.onmessage = x => observer.next(JSON.parse(x.data));
eventSource.onerror = x => observer.error(console.log('EventSource failed'));
return () => {
eventSource.close();
};
});
return someObservable$;
}
}
So in order to debug the instantiation of the First
and Second
components, I have added a console.log in the constructors/ngOnInit:
and I noticed that each time I change the route by clicking on the links, I get:
constructor first
ngOnInit first
constructor second
ngOnInit second
...
Can someone please advise whether this is expected behavior? If so how can I get Angular2 to instantiate my components only once?
Note that I have explicitly required that the First
and Second
components be instantiated at the root-level component by adding a providers
array there.
P.S. Here is the github repository for this project: https://github.com/balteo/demo-angular2-rxjs/tree/WITH-ROUTER
edit:
I am still trying to find a solution to this problem. I am getting something wrong with the router or my components. I have pushed the app to github here hoping someone can provide advice.
ngOnDestroy doesn't get called because some components do not get destroyed when navigating to a different route.
First, add links to the two components. Assign the anchor tag that you want to add the route to the routerLink attribute. Set the value of the attribute to the component to show when a user clicks on each link. Next, update your component template to include <router-outlet> .
Angular routing is the method to direct the users to the relevant page that they want to perform their actions. In other words, There may be multiple components in a single angular app. Routing is the way to implement the connection between those components. If you say yes Angular will add app-routing.
>= 2.3.0-rc.0
A custom RouteReuseStrategy
can be implemented to control when routed components are destroyed and recreated or reused.
>= 2.0.0
CanReuse
doesn't exist anymore in the new router. When only route parameters change while staying on the same route, the component is not recreated.
If the route is changed and navigated back to the same component the component is recreated.
<=RC.x
Note that I have explicitly required that the First and Second components be instantiated at the root-level component by adding a providers array there.
Adding components to providers: []
is mostly meaningless especially in regard to this issue.
You can implement CanReuse
by adding
routerCanReuse(next: ComponentInstruction, prev: ComponentInstruction) { return true; }
to your component, but it's quite limited for which kind of navigation the same instance is reused (if you stay on the same route and only change parameters).
If CanReuse
doesn't fix your problem then move the data to a service, where instances are kept and bind the view of the component to the data of this service to make the component show current data.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With