I'm still working on a project using Angular2.
If you want more details about why I need to do what I'm going to explain, please refer this issue.
I have an AppComponent which is bootstraped via bootstrap. It's a very simple component :
#Component({
selector: 'app-view',
directives: [ Devtools, MainComponent ],
template: `
<ngrx-devtools></ngrx-devtools>
<main-cmp></main-cmp>
`
})
export class AppComponent { }
This component includes another one : MainComponent (via the main-cmp selector). For some reasons, I want to set up my routing in MainComponent.
Here is the code :
#Component({
selector: 'main-cmp',
directives: [ ROUTER_DIRECTIVES, NavComponent ],
template: `
<h1>App</h1>
<nav-cmp></nav-cmp>
<router-outlet></router-outlet>
`
})
#RouteConfig([
{ path: '/home', name: 'Home', component: HomeComponent, useAsDefault: true },
{ path: '/medias', name: 'Medias', component: MediasComponent }
])
export class MainComponent {
constructor (private router:Router, private store:Store<AppStore>) {
router.subscribe(url => store.dispatch(changeUrl(url)));
}
}
Finally, MainComponent includes NavComponent which is a very basic nav.
The thing is, with this setup, I encounter this issue :
EXCEPTION: Component "AppComponent" has no route config. in [['Home'] in NavComponent#2:15].
Of course, if I move my router's logic to AppComponent, everything works well.
So my question is : is there a way to do routing stuff into another component than the one which is bootstraped ?
Thanks :).
It appears that it's not possible because the generate method of the RouteRegistry class explictly relies (hardcoded) on the root component. See this line in the source code:
https://github.com/angular/angular/blob/master/modules/angular2/src/router/route_registry.ts#L345
Here is the code that trhows the error:
RouteRegistry.prototype._generate = function(linkParams,
ancestorInstructions, prevInstruction, _aux, _originalLink) {
(...)
var parentComponentType = this._rootComponent; // <----
(...)
var rules = this._rules.get(parentComponentType);
if (lang_1.isBlank(rules)) { // <----
throw new exceptions_1.BaseException("Component \"" +
lang_1.getTypeNameForDebugging(parentComponentType) +
"\" has no route config.");
}
(...)
};
This method is indirectly used from the _updateLink method of the RouterLink directive.
Here is the corresponding stack trace:
RouteRegistry._generate (router.js:2702)
RouteRegistry.generate (router.js:2669)
Router.generate (router.js:3174)
RouterLink._updateLink (router.js:1205)
See the plunkr I used to debug your problem: https://plnkr.co/edit/8JojtgZmc8kA9ib6zvKS?p=preview.
How about a workaround - use root as a child route?
#Component({
selector: 'app',
directives: [ ROUTER_DIRECTIVES ],
template: `
<router-outlet></router-outlet>
`
})
#RouteConfig([
{path: '/...', as: 'Main', component: MainComponent, useAsDefault: true }
])
export class App { }
#Component({
selector: 'main-cmp',
directives: [ ROUTER_DIRECTIVES ],
template: `
<h1>App</h1>
<router-outlet></router-outlet>
`
})
#RouteConfig([
{ path: '/home', name: 'Home', component: HomeComponent, useAsDefault: true },
])
export class MainComponent { }
Related
#angular/router
Is there a way to have different routes with different levels all populating int he same router-outlet of that parent?
example:
/a/b/c/d
/a/b/c
/a/b
all populating in the router-outlet of component a ?
Thanks,
you can do it , you just have to maintain the routes in correct order,
Like below see a/b/c comes before a/b, means fully qualified path should come first.
export const routes: RouterConfig = [
{ path: 'a/b/c', component: ABC },
{ path: 'a/b', component: AB }
];
#Component({
selector: 'my-app',
template: `
<h1 class="title">Component Router</h1>
<nav>
<a routerLink="/a/b" routerLinkActive="active">a/b</a>
<a routerLink="/a/b/c" routerLinkActive="active">a/b/c</a>
</nav>
<router-outlet></router-outlet>
`,
directives: [ROUTER_DIRECTIVES]
})
export class AppComponent {
}
#Component({
selector: 'my-abc',
template: `
<h1>a-b-c</h1>
`
})
export class ABC {
}
#Component({
selector: 'my-ab',
template: `
<h1>a-b</h1>
`
})
export class AB {
}
Here is the Plunker!
I am a beginner in angular2.
I try to use routes in a CRUD app. My problem are the nested routes. Chart example :
AppComponent
/ \
MealListComponent DishListComponent
\
DishEditComponent <--- Must have DishList template
The link / and \ respresent routes.
Problem : I want my DishEditComponent template is not include on DishListComponent template.
You can test app on http://plnkr.co/edit/g7NaoVd5BkGtSmr8ZkFW?p=preview go to Liste Dish link, then to Add dish link.
You'll see both Dish List title and Dish Edit orr Add title because DishEditComponent template is included in DishListComponent template by router-outlet tag, but I want that only Dish Edit or Add title displayed.
Do you know a way to avoid nested routes ?
You can try using asyncRoute.
Here is explanation for it.
import {Component, View, bootstrap} from 'angular2/angular2';
import {RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS} from 'angular2/router';
import {Home} from './components/home/home';
import {About} from './components/about/about';
#Component({
selector: 'app'
})
#RouteConfig([
{ path: '/', component: Home, name: 'home' },
{ path: '/about', component: About, name: 'about' }
])
#View({
templateUrl: './app.html',
styleUrls: ['./app.css'],
directives: [ROUTER_DIRECTIVES]
})
class App {}
bootstrap(App, [ROUTER_PROVIDERS]);
Here’s the implementation of the About component:
import {Component, View, CORE_DIRECTIVES} from 'angular2/angular2';
import {NameList} from '../../services/NameList';
#Component({
selector: 'about',
providers: [NameList],
templateUrl: './components/about/about.html',
directives: [CORE_DIRECTIVES]
})
export class About {
constructor(public list: NameList) {}
addName(newname):boolean {
this.list.add(newname.value);
newname.value = '';
return false;
}
}
The class, which implements RouteDefinition and allows asynchronous loading of the component associated with given route. This allows on demand loading of the component’s dependencies as well. Here’s now our definition will look like with AsyncRoute:
#RouteConfig([
{ path: '/', component: Home, name: 'home' },
new AsyncRoute({
path: '/about',
loader: () => System.import('./components/about/about').then(m => m.About),
name: 'about'
})
])
Basically we register two routes: - A regular route - Async route. The async route accepts as argument a loader. The loader is a function that must return a promise, which needs to be resolved with the component that needs to be rendered.
I found the solution.
1. I must remove this line in DishListComponent template :
<router-outlet></router-outlet>
2. Replace line :
<a [routerLink]="['DishEdit']">Add dish</a>
by this line :
<button (click)="addDish()">Add dish</button>
3. Add import :
import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, Router } from '#angular/router-deprecated';
4. Update DishListComponent constructor :
constructor(private router: Router) {
}
5. Add this method in DishListComponent :
addDish() {
let link = ['DishEdit', {}];
this.router.navigate(link);
}
6. Remove PROVIDERS in DishListComponent
Final code
Final DishListComponent
#Component({
selector: 'dish-list',
directives: [ROUTER_DIRECTIVES],
template:`
<h1>Dish List</h1>
<button (click)="addDish()">Add dish</button>
<main>
</main>
`
})
export class DishListComponent {
constructor(private router: Router) {
}
addDish() {
let link = ['DishEdit', {}];
this.router.navigate(link);
}
}
The final RouteConfig
#RouteConfig([
{
path: '/dish-list',
name: 'DishList',
component: DishListComponent
//useAsDefault: true
},
{
path: '/dish-edit',
name: 'DishEdit',
component: DishEditComponent
},
{
path: '/meal-list',
name: 'MealList',
component: MealListComponent
}
])
The plunker link : http://plnkr.co/edit/LsLdc0efJtPaEbASWPek?p=preview
I hope it will help !
I have a main parent component that contains a built-in top nav, a separate sidebar nav component and a main content div that houses the <router-outlet> for the main component. I'm trying to get my head around how to have the links inside child nav component to change the router-outlet in the parent main component.
```
Main Component
#Component({
selector: 'app',
templateUrl: 'mainComponent.html',
encapsulation: ViewEncapsulation.None,
directives: [ROUTER_DIRECTIVES,ChildSidenav],
})
#RouteConfig([
{ path: '/', component: HomeCmp, as: 'Home' }, //for top nav
{ path: '/about', component: AboutCmp, as: 'About' }, //for top nav
{ path: '/user', component: UserCmp, as: 'User'}, //for top nav
{ path: '/data/...', component: SidenavBasicUsage, as: 'Data'}, //link in child component
{ path: '/dates', component: SelectDatesComponent, as: 'Dates'}, //link in child component
{ path: '/edit/paths', component: EditPathsComponent, as: 'EditPaths'}, //link in child component
{ path: '/edit/filters', component: EditFiltersComponent, as: 'EditFilters'}, //link in child component
])
export class AppCmp {}
Child Component
#Component({
selector: 'left-control',
templateUrl: 'sidebar.component.html',
moduleId: module.id,
directives: [ROUTER_DIRECTIVES],
})
#RouteConfig([
{path: '/',name: 'Data',component: SelectDataComponent,useAsDefault: true},
{path: '/dates',name: 'Dates',component: SelectDatesComponent},
{path: '/edit/paths',name: 'EditPaths',component: EditPathsComponent},
{path: '/edit/filters',name: 'EditFilters',component: EditFiltersComponent}
])
export class SideNavComponent{}
```
I haven't tried all these methods but I think they should work:
import {Router} from 'angular2/router'
...
constructor(private router:Router) {}
someMethod() {
this.router.parent.navigate(['About']);
this.router.navigate(['/About']);
this.router.navigate(['../About']);
}
With [routerLink]="..." it works the same (except the first example)
My Routing isn't working in Angular2, to demonstrate the point, I have put the same component as the destination for both the root of my site and /login. The component works at http://localhost:3000, but at http://localhost:3000/login, I just get a notice "Cannot GET /login".
app.component.ts:
import { Component } from 'angular2/core';
import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from 'angular2/router';
import {TodoService} from './todo/services/todo.service';
import { TodoCmp } from './todo/components/todo.component';
import { LoginComponent } from './user/components/login.component';
import { UserService } from './user/services/user.service';
#Component({
selector: 'my-app',
template: `
<h1>{{title}}</h1>
<router-outlet></router-outlet>
`,
styleUrls: ['client/dev/todo/styles/todo.css'],
directives: [ROUTER_DIRECTIVES],
providers: [
ROUTER_PROVIDERS,
TodoService
]
})
#RouteConfig([
{
path: '/',
name: 'TodoCmp',
component: TodoCmp,
useAsDefault: true
},
{
path: '/login',
name: 'TodoCmp',
component: TodoCmp
}
])
export class AppComponent {
title = 'ng2do';
}
Here is a link to my index file.
What have I done wrong?
Two routes in one #RouteConfig(...) can't have the same name:
#RouteConfig([
{
path: '/',
name: 'TodoCmp',
component: TodoCmp,
useAsDefault: true
},
{
path: '/login',
name: 'TodoCmp', <!-- <<<== should be 'Login' instead of 'TodoCmp'
component: TodoCmp
}
])
You should move ROUTER_PROVIDERS to bootstrap() (like HTTP_PROVIDERS)
Angular 2 - Is it possible to store my routes in another file and import them into the app.ts file (because over time the routes will build up)
Here is an example of my current app.ts that works. I basically want to move the route config routes to another file to make it cleaner:
import {Todo} from './components/todo/todo';
import {About} from './components/about/about';
import {AuthService} from './authService';
import {Component, View, bootstrap, bind, provide} from 'angular2/angular2';
import {Router, ROUTER_BINDINGS, RouterOutlet, RouteConfig, RouterLink, ROUTER_PROVIDERS, APP_BASE_HREF} from 'angular2/router';
import {Location, LocationStrategy, HashLocationStrategy} from 'angular2/router';
#Component({
selector: 'app'
})
#View({
template: `
<div class="container">
<nav>
<ul>
<li><a [router-link]="['/Home']">Todo</a></li>
<li><a [router-link]="['/About']">About</a></li>
</ul>
</nav>
<router-outlet></router-outlet>
</div>
`,
directives: [RouterOutlet, RouterLink]
})
#RouteConfig([
{ path: '/', redirectTo: '/home' },
{ path: '/home', component: Todo, as: 'Home' },
{ path: '/about', component: About, as: 'About' }
])
export class AppComponent {
constructor(router: Router, _authService: AuthService, _location: Location){
//Subscribe - watches routes pop state events.
router.subscribe((val) => {
_authService.isUserLoggedIn().then((success) => {
router.parent.navigate(['/About']);
});
})
}
}
bootstrap(AppComponent, [ROUTER_PROVIDERS, provide(APP_BASE_HREF, {useValue: '/'}), AuthService]);
i personally have created an route.interface.ts and a route.ts files.
Routes file
import {Route} from './route.interface'
import {AuthComponent} from './auth/auth.component'
export const Routes: Route[] = [
{
path: '/auth',
name: 'Authenticate',
component: AuthComponent
},
];
Route Interface
export interface Route {
path: string,
name: string,
component: any,
}
Usage in main component.
import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from 'angular2/router'
import {Routes} from './routes'
#Component({
selector: 'app',
templateUrl: './angular2/app/layout.component.html',
directives: [
ROUTER_DIRECTIVES
],
providers: [
HTTP_PROVIDERS,
ROUTER_PROVIDERS
],
})
#RouteConfig(Routes)
Hope that helps. you can even create a route service and inject it you main component. Enjoy coding!
You can add your RouteConfig per component
Lets say you have home and about as in your example, then you would define the routing from that specific component in the component itself.
So in your about component you can add
// './components/about/about'
#RouteConfig([
{ path: '/about', component: About, as: 'About' }
])
And in your home component you can do the same
// './components/home/home'
#RouteConfig([
{ path: '/home', component: Todo, as: 'Home' }
])