Reputation: 485
I have a route like the following: page/:id
, where it loads a different content for each id
. However, every time I go to a different page, it reloads the entire content. I'd like to reload only the data coming from the API.
Navigation:
<ul>
<li [routerLink]="['/page', 1]">Page 1</li>
<li [routerLink]="['/page', 2]">Page 2</li>
<li [routerLink]="['/page', 3]">Page 3</li>
</ul>
Template:
<h1 *ngIf="data">{{data.name}}</h1>
Component:
export class DataComponent implements OnInit {
data: any;
error: any;
constructor(
private route: ActivatedRoute,
private service: DataService
) {}
ngOnInit() {
this.getData();
}
getData() {
this.service.getData()
.subscribe(
res => this.data = res,
error => this.error = error
);
}
}
If you go to this Plunker, you'll see the black rectangle blinks every time the route changes. Ideally, the black part would remain intact, only the data inside would change.
I also noticed this flickering behavior happens only if I'm using an HTTP call. If I'd have other things in that component, this wouldn't happen when changing routes.
Any ideas on how to solve this?
EDIT: Here's a Plunkr showing the expected behavior. However, I was using former ROUTER_DIRECTIVES
, which are deprecated now.
Upvotes: 4
Views: 5941
Reputation: 485
Extending @Rob's answer, I did manage to solve this issue using the Resolve
guard. However, I was making a few mistakes in its implementation.
I've created a new data-resolve
where I resolve the HTTP
call before initializing the route:
@Injectable()
export class DataResolve implements Resolve {
constructor(private service: DataService, private router: Router) {}
resolve(route: ActivatedRouteSnapshot):
Observable<any> | Promise<any> | any {
return this.service.getData().then(data => {
if (data) {
return data;
} else {
this.router.navigate(['/page/1']);
return false;
}
});
}
}
Then, I needed to add that service to my module's providers:
providers: [DataService, DataResolve]
And also tell the routing to solve that before loading the route:
resolve: { pages: DataResolve }
After that, I'll have an array I can use in my component to get the data:
this.route.data.forEach((data: {pages}) => {
this.data = data.pages;
});
I've also updated my Plunker with a working example.
Upvotes: 0
Reputation: 40886
Your blinking occurs because when you navigate, DataComponent
is destroyed and re-built, but it cannot be shown until new data arrives from the server because you block its display with *ngIf="data"
. On this plunker, I've added a 1 second delay to the server response to make the problem obvious. Note that only the DataComponent
blinks. The rest of PagesComponent
does not.
On this plunker, there is no visible blinking because the data is hard-coded so there is no wait for the server's response.
To alleviate the blinking, don't use *ngIf
since that removes the element from the dom entirely. On this plunker, no blinking occurs even while we wait for data to arrive from the server.
Basically, as long as you choose to hide the element until data arrives, whenever the component is re-initialized you will see blinking.
Upvotes: 1
Reputation: 12872
You are already only reloading the content coming from the API. The black box is flickering because you are styling the h1
and the h1
is changing. If you want the box there for every page then move it up to the AppComponent
s template.
<div class="box">
<router-outlet></router-outlet>
</div>
Then move the styles to the .box
selector rather than the h1
.
Another issue will be the height of the box will collapse and expand while the content is removed and refreshed. You can fix this by using a resolve guard on your routes.
https://angular.io/docs/ts/latest/guide/router.html#!#resolve-guard
Upvotes: 0