I've really just started digging into Angular 2 in the last week and (like many others apparently) have had some serious headaches around routing. I've started working with router 3.0.0-alpha.7. What I really want to do is share some retrieved data in my base component with these child components. Here's my route config.
[{
path: 'base/:id',
component: BaseComp,
children: [{
path: '',
component: OverviewComp
}, {
path: 'docs',
component: DocsComp
}]
}]
When the base route is hit, I'm retrieving some data through a service based on the: id parameter. Once that data is received, I'd like it to cascade to the children. I was hoping it would be as simple as putting @Inputs on OverviewComp and DocsComp, but I quickly realized there doesn't seem to be any way of doing this in the template considering the <router-outlet>
is the actual template component. derp.
Does anyone have any thoughts on what the best way to do this sort of thing is?
Should I work with an earlier router version (router-deprecated)?
Should the id
parameter be moved to the child components?
Should child components just hit the service to get the same data (cached)?
If you want to use multiple <router-outlet></router-outlet> you can use it. There is no limit in angular but there are some best practices of using multiple place one in app. component.
The Router-Outlet is a directive that's available from the router library where the Router inserts the component that gets matched based on the current browser's URL. You can add multiple outlets in your Angular application which enables you to implement advanced routing scenarios.
Should I work with an earlier router version (router-deprecated)?
It's a challenge to develop as fast as Angular2 overthrows their routing concept :). Though, make sure you don't go for the deprecated one. It was still in beta when they chose to flag it 'deprecated', so you probably don't want use a deprecated non-stable routing.
Should the id parameter be moved to the child components?
I'd say it depends on your structure. Let's assume you have many routes like this
/user/:id/....
It absolutely makes sense to resolve the user in a component that listens to /user/:id, and do that once and not in each of the child components. There is a drawback, though, if you want to make the user available in your child routes. Using a service is fine for such a purpose, but you need to cope with the asynchronous nature of backend http requests. When the child route is activated and their components get rendered, the backend call might have not finished yet. So you can't access in your service directly from your child component, you need to get an observable, too. This adds a little complexity to your service.
The other way is to prefer overhead over complexity, and resolve all of the data required in the child component. That means, for a route like this
/user/:id/process/:id/edit
you need to resolve both user and process, and probably the second call for the 'process' depends on the 'user' that has to be resolved beforehand. If you have a lot of child routes with lots of IDs to resolve this might get a little tedious, though.
Be aware, that if you route the segment /user/:id
to a component that needs to display some data about the user, and you need that user data in the child components, too, you should pick the first option. It makes no sense to make multiple calls to the same data.
Should child components just hit the service to get the same data (cached)?
Yes, for the same route, absolutely. That saves bandwidth and server resources. Moreover, it could be dangerous since the server might send different data in each subsequent request (at least, if you don't specify a version of requested document). Of course, whenever the user triggers a new route, choose for yourself if you present cached data or ask the server for an update.
I think I had a similar dilemma, and here is how I'm dealing with this kind of situations.
1. Target & Input parameters
When I want to do the split into a child component from the main component I will try to see if that's a different kind of system object or not (basically if I need a complete routing for it or I can solve my data link using just a property as target/input) Don't forget :
Angular insists that we declare a target property to be an input property. If we don't, Angular rejects the binding and throws an error.
Review part3 from ng2 tutorial : https://angular.io/docs/ts/latest/tutorial/toh-pt3.html
1. Routing
For different components you can define a new route and then get data into that using two methods:
1.1. From route params and a service (you should use a service method for any data loading , in this way you can refactor the data access and keep the component lean and focused on supporting the view). Should use the route params just for filters or small jobs tasks and not for data transfers. Here is a possible component :
Object1Routes
import { RouterConfig } from '@angular/router';
import { Object1Dashboard } from './object1.dashboard';
import { Object1Edit } from './object1.edit';
export const Object1Routes: RouterConfig = [
{
path: 'object1',
component: Object1Dashboard,
'children': [
<...>
,{ path: 'edit/:id', component: Object1Edit }
]
}
];
Object1Edit
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { Object1Service } from './services/object1/object1.service';
import { Object1Model } from './models/object1/object1.model';
@Component({
selector: 'object1-edit',
templateUrl: './object1/object1.edit.html',
directives: []
})
export class Object1Edit implements OnInit, OnDestroy {
model = new Object1Model;
sub:any;
editId:number;
constructor(
private route: ActivatedRoute,
private router: Router,
private serviceData: Object1Service
) { }
onSubmit(d:Object1Model) {
this.model = d;
this.router.navigate(['/object1']);
}
ngOnInit() {
this.sub = this.route.params.subscribe(params => {
this.editId = +params['id']; // (+) converts string 'id' to a number
this.serviceData.getObject1ById(this.editId).then(data => {
this.model = data;
});
});
});
}
ngOnDestroy() {
this.sub.unsubscribe();
}
}
1.2. Just from a service
Object1Routes
import { RouterConfig } from '@angular/router';
import { Object1Dashboard } from './object1.dashboard';
import { Object1List } from './object1.list';
export const Object1Routes: RouterConfig = [
{
path: 'object1',
component: Object1Dashboard,
'children': [
{ path: '', component: Object1List }
<...>
]
}
];
Object1List
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { Object1Service } from './services/object1/object1.service';
import { Object1Model } from './models/object1/object1.model';
@Component({
selector: 'object1-list',
templateUrl: './object1/object1.list.html'
})
export class Object1List implements OnInit, OnDestroy {
constructor(
private route: ActivatedRoute,
private router: Router,
private serviceData:Object1Service
) { }
modelArray:Object1Model[];
selectedId:number;
private sub: any;
onSelect(model:Object1Model) {
console.log('Select ' + model.code);
let link = ['/object1/edit', model.id];
this.router.navigate(link);
}
onDelete(model:Object1Model) {
console.log('Delete : ' + model.code);
this.serviceData.delObject1ById(model.id);
}
ngOnInit() {
this.sub = this.route .params.subscribe(params => {
this.selectedId = +params['id'];
this.serviceData.getAllObject1().then(data => this.modelArray = data);
});
}
ngOnDestroy() {
if (this.sub) {
this.sub.unsubscribe();
}
}
}
I hope this will help. Let me know your thoughts, if you have a different opinion or if I've missed out anything.
Here is a comprehensive example which you can use: http://plnkr.co/edit/Zd0qmavTzedtimImAgfQ?p=preview
The code provided is based on Angular 2.0.0-rc.2 and @angular/router 3.0.0-alpha.7.
Check also this article about the Single Responsibility Principle : https://blog.8thlight.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html
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