#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!
Related
I'm trying to limit the search of my dependencies to only its host by using #Host.
But it's not working without ng-content or transclusion.
The below code is not working(without ng-content)
// Root Component
#Component({
selector: 'my-app',
template: '<parent></parent>'
})
export class RootComponent { }
#Component({
selector: 'parent',
template: '<child></child>',
providers:[{provide:TestService,useClass:TestService}]
})
export class ParentComponent {
name: string = '';
constructor(abc:TestService){
abc.name='SomeName';
this.name=abc.name
}
}
#Component({
selector: 'child',
template: '<h1>{{parent}}</h1>'
})
export class ChildComponent {
parent: string = ""
constructor(#Host() testService: TestService) {
this.parent= 'My parent is :'+testService.name;
}
}
With ng-content
Now just by changing the templates in RootComponent & the ParentComponent the above code starts working
#Component({
selector: 'my-app',
template: '<parent><child></child></parent>'
})
export class RootComponent { }
#Component({
selector: 'parent',
template: '<ng-content></ng-content>',
providers:[{provide:TestService,useClass:TestService}]
})
export class ParentComponent {
name: string = '';
constructor(abc:TestService){
abc.name='SomeName';
this.name=abc.name
}
}
#Component({
selector: 'child',
template: '<h1>{{parent}}</h1>'
})
export class ChildComponent {
parent: string = ""
constructor(#Host() testService: TestService) {
this.parent= 'My parent is :'+testService.name;
}
}
Questions:
Why the first code not working(without ng-content)..?
#Host can only works with ng-content..?
What difference ng-content makes as the structure of the compiled HTML is same in both the cases.
Here are the Plunker for reference:
Not working
It looks like you need to use viewProviders instead of providers to work with #Host as also shown in #Host
Plunker example
My routerLink work outside of my the router-outlet in the navigation component but when I have a page that is within the router-outlet that has a routerlink to a different page I get an error.
browser_adapter.ts:82 TypeError: Cannot read property 'startsWith' of undefined
at UrlParser.parseRootSegment (url_tree.ts:301)
at DefaultUrlSerializer.parse (url_tree.ts:196)
at Router.navigateByUrl (router.ts:242)
at RouterLinkWithHref.onClick (router_link.ts:90)
at DebugAppView._View_ProfileFeedComponent0._handle_click_12_0 (ProfileFeedComponent.template.js:381)
at eval (view.ts:406)
at eval (dom_renderer.ts:274)
at eval (dom_events.ts:20)
at ZoneDelegate.invoke (zone.js:323)
at Object.onInvoke (ng_zone_impl.ts:72)
My router is a basic router.
app.routes.ts
import { provideRouter, RouterConfig } from '#angular/router';
import { HomeComponent } from "./home/home.component";
import {ProfileFeedComponent} from "./profileFeed/profileFeed.component";
import {QuestionComponent} from "./questions/question.component";
import {QuestionAskComponent} from "./questionAsk/questionAsk.component";
export const routes: RouterConfig = [
{path: '', component: HomeComponent, pathMatch: 'full'},
{path: 'profile-feed', component: ProfileFeedComponent},
{path: 'question', component: QuestionComponent},
{path: 'question/ask', component: QuestionAskComponent},
];
export const appRouterProviders = [
provideRouter(routes)
];
export const appRouterProviders = [
provideRouter(routes)
];
app.component.html
<navigation></navigation>
<div class="wrapper">
<router-outlet></router-outlet>
</div>
app.component.ts
#Component({
moduleId: module.id,
selector: "my-app",
templateUrl: "app.component.html",
directives: [ ROUTER_DIRECTIVES, CORE_DIRECTIVES, FORM_DIRECTIVES, NavigationComponent],
})
export class AppComponent {
public viewContainerRef : ViewContainerRef;
public constructor(viewContainerRef:ViewContainerRef) {
// You need this small hack in order to catch application root view container ref
this.viewContainerRef = viewContainerRef;
}
}
//ALL IMPORTS ARE PROPERLY INCLUDED IN THIS FILE
The problem is in this component I believe I just want to have the option to go to a different page within this page, seems straightforward not sure why it is not working. The below page would be inserted into the router-outlet from the menubar navigation. The link('/questions/ask') within this file is not available in the menu bar.
profileFeed.component.ts
import {Component} from "#angular/core";
import {ROUTER_DIRECTIVES, RouterLink, Router} from "#angular/router";
import {ProfileInfoComponent} from "../profileInfo/profileInfo.component";
#Component({
moduleId: module.id,
selector: 'profile-feed',
templateUrl: 'profileFeed.component.html',
styleUrls: ['profileFeed.component.css'],
directives: [
ProfileInfoComponent,
RouterLink,
ROUTER_DIRECTIVES
]
})
export class ProfileFeedComponent {
}
profileFeed.component.html
<div class="profile-feed-container container">
<div class="profile-detail-summary container-fluid">
<profile-info></profile-info>
<hr>
<div class="container-fluid">
<div class="container-fluid">
<a class="btn btn-default" routerLink='/question/ask'>Ask a Question</a>
</div>
</div>
</div>
</div>
The workaround I found to work is use a click method on the link and set a method inside the component's ts file to go to the route needed by using the Router class' navigateByUrl() method. Example:
Component.ts file
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
#Component({
selector: 'app-register',
templateUrl: './register.component.html',
styleUrls: ['./register.component.css']
})
export class RegisterComponent implements OnInit {
constructor(private router: Router) { }
ngOnInit() {
}
goToLoginPage() {
this.router.navigateByUrl("");
}
}
Component.html link
<p>Have an account? <a (click)="goToLoginPage()" class="signup">Sign in</a></p>
The page doesn't fully refresh it acts as if it were a regular <router-outlet> link. I also looked at the network tab and didn't see any new items appear.
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'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 { }
I have an application in ts which have this template :
<main>
<test></test>
<router-outlet></router-outlet>
</main>
With this RouteConfig
#RouteConfig([
{ path: '/myApp/game', name: 'GamePage', component: GameComponent, useAsDefault: true}
])
Here are the game component :
import {Component} from 'angular2/core';
#Component({
selector: 'test',
template: '<div>{{title}}</div>'
})
export class GameComponent {
title = "myTest";
constructor(){
console.log("I am constructed")
}
ngOnInit(){
console.log("initGameComponent");
}
}
So this component is imported twice (once by the directive 'test', a second time by the router outlet), which is what I want in order to show the problem.
My problem is that the first time (when I don't use the router) everything's works fine, but the second time, the '{{title}}' is not rendered and the console don't log 'initGameComponent' but DO log 'I am constructed'.
The question is why ?! (Sorry if it's something stupid because I'm starting with angular2, but I really can't figure it out)
Working Plunker -with No issue as you have described
boot.ts
import {Component,bind} from 'angular2/core';
import {Router,ROUTER_PROVIDERS,RouteConfig, ROUTER_DIRECTIVES,APP_BASE_HREF,LocationStrategy,RouteParams,ROUTER_BINDINGS} from 'angular2/router';
import {bootstrap} from 'angular2/platform/browser';
import{GameComponent} from 'src/GameComponent';
#Component({
selector: 'my-app',
template: `
<h1>Component Router</h1>
<hr>
<test></test>
<hr>
<router-outlet></router-outlet>
`,
directives: [ROUTER_DIRECTIVES,GameComponent]
})
#RouteConfig([
//{path:'/ComponentOne', name: 'ComponentOne', component: ComponentOne, useAsDefault:true},
{ path: '/myApp/game', name: 'GamePage', component: GameComponent, useAsDefault: true}
])
export class AppComponent {
}
bootstrap(AppComponent, [
ROUTER_PROVIDERS,bind(APP_BASE_HREF).toValue(location.pathname)
]);
GameComponent.ts
import {Component,View} from 'angular2/core';
#Component({
selector:'test',
template: `
<div>{{title}}</div>
`
})
export class GameComponent {
title = "myTest";
constructor(){
console.log("I am constructed")
}
ngOnInit(){
console.log("initGameComponent");
}
}
I think you must replace
<main>
<test></test>
<router-outlet></router-outlet>
</main>
to
<main>
<router-outlet></router-outlet>
</main>
Because router creates own tag. And after routing, DOM will looks like
<main>
<router-outlet></router-outlet>
<test _ngcontent-jll-9=""></test>
</main>