refactor: move angular source to /packages rather than modules/@angular
This commit is contained in:
248
packages/router/src/directives/router_link.ts
Normal file
248
packages/router/src/directives/router_link.ts
Normal file
@ -0,0 +1,248 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {LocationStrategy} from '@angular/common';
|
||||
import {Attribute, Directive, ElementRef, HostBinding, HostListener, Input, OnChanges, OnDestroy, Renderer, isDevMode} from '@angular/core';
|
||||
import {Subscription} from 'rxjs/Subscription';
|
||||
|
||||
import {QueryParamsHandling} from '../config';
|
||||
import {NavigationEnd} from '../events';
|
||||
import {Router} from '../router';
|
||||
import {ActivatedRoute} from '../router_state';
|
||||
import {UrlTree} from '../url_tree';
|
||||
|
||||
/**
|
||||
* @whatItDoes Lets you link to specific parts of your app.
|
||||
*
|
||||
* @howToUse
|
||||
*
|
||||
* Consider the following route configuration:
|
||||
* `[{ path: 'user/:name', component: UserCmp }]`
|
||||
*
|
||||
* When linking to this `user/:name` route, you can write:
|
||||
* `<a routerLink='/user/bob'>link to user component</a>`
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* The RouterLink directives let you link to specific parts of your app.
|
||||
*
|
||||
* When the link is static, you can use the directive as follows:
|
||||
* `<a routerLink="/user/bob">link to user component</a>`
|
||||
*
|
||||
* If you use dynamic values to generate the link, you can pass an array of path
|
||||
* segments, followed by the params for each segment.
|
||||
*
|
||||
* For instance `['/team', teamId, 'user', userName, {details: true}]`
|
||||
* means that we want to generate a link to `/team/11/user/bob;details=true`.
|
||||
*
|
||||
* Multiple static segments can be merged into one
|
||||
* (e.g., `['/team/11/user', userName, {details: true}]`).
|
||||
*
|
||||
* The first segment name can be prepended with `/`, `./`, or `../`:
|
||||
* * If the first segment begins with `/`, the router will look up the route from the root of the
|
||||
* app.
|
||||
* * If the first segment begins with `./`, or doesn't begin with a slash, the router will
|
||||
* instead look in the children of the current activated route.
|
||||
* * And if the first segment begins with `../`, the router will go up one level.
|
||||
*
|
||||
* You can set query params and fragment as follows:
|
||||
*
|
||||
* ```
|
||||
* <a [routerLink]="['/user/bob']" [queryParams]="{debug: true}" fragment="education">
|
||||
* link to user component
|
||||
* </a>
|
||||
* ```
|
||||
* RouterLink will use these to generate this link: `/user/bob#education?debug=true`.
|
||||
*
|
||||
* (Deprecated in v4.0.0 use `queryParamsHandling` instead) You can also tell the
|
||||
* directive to preserve the current query params and fragment:
|
||||
*
|
||||
* ```
|
||||
* <a [routerLink]="['/user/bob']" preserveQueryParams preserveFragment>
|
||||
* link to user component
|
||||
* </a>
|
||||
* ```
|
||||
*
|
||||
* You can tell the directive to how to handle queryParams, available options are:
|
||||
* - 'merge' merge the queryParams into the current queryParams
|
||||
* - 'preserve' prserve the current queryParams
|
||||
* - default / '' use the queryParams only
|
||||
* same options for {@link NavigationExtras.queryParamsHandling}
|
||||
*
|
||||
* ```
|
||||
* <a [routerLink]="['/user/bob']" [queryParams]="{debug: true}" queryParamsHandling="merge">
|
||||
* link to user component
|
||||
* </a>
|
||||
* ```
|
||||
*
|
||||
* The router link directive always treats the provided input as a delta to the current url.
|
||||
*
|
||||
* For instance, if the current url is `/user/(box//aux:team)`.
|
||||
*
|
||||
* Then the following link `<a [routerLink]="['/user/jim']">Jim</a>` will generate the link
|
||||
* `/user/(jim//aux:team)`.
|
||||
*
|
||||
* @ngModule RouterModule
|
||||
*
|
||||
* See {@link Router.createUrlTree} for more information.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: ':not(a)[routerLink]'})
|
||||
export class RouterLink {
|
||||
@Input() queryParams: {[k: string]: any};
|
||||
@Input() fragment: string;
|
||||
@Input() queryParamsHandling: QueryParamsHandling;
|
||||
@Input() preserveFragment: boolean;
|
||||
@Input() skipLocationChange: boolean;
|
||||
@Input() replaceUrl: boolean;
|
||||
private commands: any[] = [];
|
||||
private preserve: boolean;
|
||||
|
||||
constructor(
|
||||
private router: Router, private route: ActivatedRoute,
|
||||
@Attribute('tabindex') tabIndex: string, renderer: Renderer, el: ElementRef) {
|
||||
if (tabIndex == null) {
|
||||
renderer.setElementAttribute(el.nativeElement, 'tabindex', '0');
|
||||
}
|
||||
}
|
||||
|
||||
@Input()
|
||||
set routerLink(commands: any[]|string) {
|
||||
if (commands != null) {
|
||||
this.commands = Array.isArray(commands) ? commands : [commands];
|
||||
} else {
|
||||
this.commands = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 4.0.0 use `queryParamsHandling` instead.
|
||||
*/
|
||||
@Input()
|
||||
set preserveQueryParams(value: boolean) {
|
||||
if (isDevMode() && <any>console && <any>console.warn) {
|
||||
console.warn('preserveQueryParams is deprecated!, use queryParamsHandling instead.');
|
||||
}
|
||||
this.preserve = value;
|
||||
}
|
||||
|
||||
@HostListener('click')
|
||||
onClick(): boolean {
|
||||
const extras = {
|
||||
skipLocationChange: attrBoolValue(this.skipLocationChange),
|
||||
replaceUrl: attrBoolValue(this.replaceUrl),
|
||||
};
|
||||
this.router.navigateByUrl(this.urlTree, extras);
|
||||
return true;
|
||||
}
|
||||
|
||||
get urlTree(): UrlTree {
|
||||
return this.router.createUrlTree(this.commands, {
|
||||
relativeTo: this.route,
|
||||
queryParams: this.queryParams,
|
||||
fragment: this.fragment,
|
||||
preserveQueryParams: attrBoolValue(this.preserve),
|
||||
queryParamsHandling: this.queryParamsHandling,
|
||||
preserveFragment: attrBoolValue(this.preserveFragment),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @whatItDoes Lets you link to specific parts of your app.
|
||||
*
|
||||
* See {@link RouterLink} for more information.
|
||||
*
|
||||
* @ngModule RouterModule
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: 'a[routerLink]'})
|
||||
export class RouterLinkWithHref implements OnChanges, OnDestroy {
|
||||
@HostBinding('attr.target') @Input() target: string;
|
||||
@Input() queryParams: {[k: string]: any};
|
||||
@Input() fragment: string;
|
||||
@Input() queryParamsHandling: QueryParamsHandling;
|
||||
@Input() preserveFragment: boolean;
|
||||
@Input() skipLocationChange: boolean;
|
||||
@Input() replaceUrl: boolean;
|
||||
private commands: any[] = [];
|
||||
private subscription: Subscription;
|
||||
private preserve: boolean;
|
||||
|
||||
// the url displayed on the anchor element.
|
||||
@HostBinding() href: string;
|
||||
|
||||
constructor(
|
||||
private router: Router, private route: ActivatedRoute,
|
||||
private locationStrategy: LocationStrategy) {
|
||||
this.subscription = router.events.subscribe(s => {
|
||||
if (s instanceof NavigationEnd) {
|
||||
this.updateTargetUrlAndHref();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Input()
|
||||
set routerLink(commands: any[]|string) {
|
||||
if (commands != null) {
|
||||
this.commands = Array.isArray(commands) ? commands : [commands];
|
||||
} else {
|
||||
this.commands = [];
|
||||
}
|
||||
}
|
||||
|
||||
@Input()
|
||||
set preserveQueryParams(value: boolean) {
|
||||
if (isDevMode() && <any>console && <any>console.warn) {
|
||||
console.warn('preserveQueryParams is deprecated, use queryParamsHandling instead.');
|
||||
}
|
||||
this.preserve = value;
|
||||
}
|
||||
|
||||
ngOnChanges(changes: {}): any { this.updateTargetUrlAndHref(); }
|
||||
ngOnDestroy(): any { this.subscription.unsubscribe(); }
|
||||
|
||||
@HostListener('click', ['$event.button', '$event.ctrlKey', '$event.metaKey'])
|
||||
onClick(button: number, ctrlKey: boolean, metaKey: boolean): boolean {
|
||||
if (button !== 0 || ctrlKey || metaKey) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof this.target === 'string' && this.target != '_self') {
|
||||
return true;
|
||||
}
|
||||
|
||||
const extras = {
|
||||
skipLocationChange: attrBoolValue(this.skipLocationChange),
|
||||
replaceUrl: attrBoolValue(this.replaceUrl),
|
||||
};
|
||||
this.router.navigateByUrl(this.urlTree, extras);
|
||||
return false;
|
||||
}
|
||||
|
||||
private updateTargetUrlAndHref(): void {
|
||||
this.href = this.locationStrategy.prepareExternalUrl(this.router.serializeUrl(this.urlTree));
|
||||
}
|
||||
|
||||
get urlTree(): UrlTree {
|
||||
return this.router.createUrlTree(this.commands, {
|
||||
relativeTo: this.route,
|
||||
queryParams: this.queryParams,
|
||||
fragment: this.fragment,
|
||||
preserveQueryParams: attrBoolValue(this.preserve),
|
||||
queryParamsHandling: this.queryParamsHandling,
|
||||
preserveFragment: attrBoolValue(this.preserveFragment),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function attrBoolValue(s: any): boolean {
|
||||
return s === '' || !!s;
|
||||
}
|
142
packages/router/src/directives/router_link_active.ts
Normal file
142
packages/router/src/directives/router_link_active.ts
Normal file
@ -0,0 +1,142 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AfterContentInit, ChangeDetectorRef, ContentChildren, Directive, ElementRef, Input, OnChanges, OnDestroy, QueryList, Renderer, SimpleChanges} from '@angular/core';
|
||||
import {Subscription} from 'rxjs/Subscription';
|
||||
import {NavigationEnd} from '../events';
|
||||
import {Router} from '../router';
|
||||
import {RouterLink, RouterLinkWithHref} from './router_link';
|
||||
|
||||
/**
|
||||
* @whatItDoes Lets you add a CSS class to an element when the link's route becomes active.
|
||||
*
|
||||
* @howToUse
|
||||
*
|
||||
* ```
|
||||
* <a routerLink="/user/bob" routerLinkActive="active-link">Bob</a>
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* The RouterLinkActive directive lets you add a CSS class to an element when the link's route
|
||||
* becomes active.
|
||||
*
|
||||
* Consider the following example:
|
||||
*
|
||||
* ```
|
||||
* <a routerLink="/user/bob" routerLinkActive="active-link">Bob</a>
|
||||
* ```
|
||||
*
|
||||
* When the url is either '/user' or '/user/bob', the active-link class will
|
||||
* be added to the `a` tag. If the url changes, the class will be removed.
|
||||
*
|
||||
* You can set more than one class, as follows:
|
||||
*
|
||||
* ```
|
||||
* <a routerLink="/user/bob" routerLinkActive="class1 class2">Bob</a>
|
||||
* <a routerLink="/user/bob" [routerLinkActive]="['class1', 'class2']">Bob</a>
|
||||
* ```
|
||||
*
|
||||
* You can configure RouterLinkActive by passing `exact: true`. This will add the classes
|
||||
* only when the url matches the link exactly.
|
||||
*
|
||||
* ```
|
||||
* <a routerLink="/user/bob" routerLinkActive="active-link" [routerLinkActiveOptions]="{exact:
|
||||
* true}">Bob</a>
|
||||
* ```
|
||||
*
|
||||
* You can assign the RouterLinkActive instance to a template variable and directly check
|
||||
* the `isActive` status.
|
||||
* ```
|
||||
* <a routerLink="/user/bob" routerLinkActive #rla="routerLinkActive">
|
||||
* Bob {{ rla.isActive ? '(already open)' : ''}}
|
||||
* </a>
|
||||
* ```
|
||||
*
|
||||
* Finally, you can apply the RouterLinkActive directive to an ancestor of a RouterLink.
|
||||
*
|
||||
* ```
|
||||
* <div routerLinkActive="active-link" [routerLinkActiveOptions]="{exact: true}">
|
||||
* <a routerLink="/user/jim">Jim</a>
|
||||
* <a routerLink="/user/bob">Bob</a>
|
||||
* </div>
|
||||
* ```
|
||||
*
|
||||
* This will set the active-link class on the div tag if the url is either '/user/jim' or
|
||||
* '/user/bob'.
|
||||
*
|
||||
* @ngModule RouterModule
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[routerLinkActive]',
|
||||
exportAs: 'routerLinkActive',
|
||||
})
|
||||
export class RouterLinkActive implements OnChanges,
|
||||
OnDestroy, AfterContentInit {
|
||||
@ContentChildren(RouterLink, {descendants: true}) links: QueryList<RouterLink>;
|
||||
@ContentChildren(RouterLinkWithHref, {descendants: true})
|
||||
linksWithHrefs: QueryList<RouterLinkWithHref>;
|
||||
|
||||
private classes: string[] = [];
|
||||
private subscription: Subscription;
|
||||
private active: boolean = false;
|
||||
|
||||
@Input() routerLinkActiveOptions: {exact: boolean} = {exact: false};
|
||||
|
||||
constructor(
|
||||
private router: Router, private element: ElementRef, private renderer: Renderer,
|
||||
private cdr: ChangeDetectorRef) {
|
||||
this.subscription = router.events.subscribe(s => {
|
||||
if (s instanceof NavigationEnd) {
|
||||
this.update();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get isActive(): boolean { return this.active; }
|
||||
|
||||
ngAfterContentInit(): void {
|
||||
this.links.changes.subscribe(_ => this.update());
|
||||
this.linksWithHrefs.changes.subscribe(_ => this.update());
|
||||
this.update();
|
||||
}
|
||||
|
||||
@Input()
|
||||
set routerLinkActive(data: string[]|string) {
|
||||
const classes = Array.isArray(data) ? data : data.split(' ');
|
||||
this.classes = classes.filter(c => !!c);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void { this.update(); }
|
||||
ngOnDestroy(): void { this.subscription.unsubscribe(); }
|
||||
|
||||
private update(): void {
|
||||
if (!this.links || !this.linksWithHrefs || !this.router.navigated) return;
|
||||
const hasActiveLinks = this.hasActiveLinks();
|
||||
|
||||
// react only when status has changed to prevent unnecessary dom updates
|
||||
if (this.active !== hasActiveLinks) {
|
||||
this.active = hasActiveLinks;
|
||||
this.classes.forEach(
|
||||
c => this.renderer.setElementClass(this.element.nativeElement, c, hasActiveLinks));
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
private isLinkActive(router: Router): (link: (RouterLink|RouterLinkWithHref)) => boolean {
|
||||
return (link: RouterLink | RouterLinkWithHref) =>
|
||||
router.isActive(link.urlTree, this.routerLinkActiveOptions.exact);
|
||||
}
|
||||
|
||||
private hasActiveLinks(): boolean {
|
||||
return this.links.some(this.isLinkActive(this.router)) ||
|
||||
this.linksWithHrefs.some(this.isLinkActive(this.router));
|
||||
}
|
||||
}
|
113
packages/router/src/directives/router_outlet.ts
Normal file
113
packages/router/src/directives/router_outlet.ts
Normal file
@ -0,0 +1,113 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Attribute, ComponentFactoryResolver, ComponentRef, Directive, EventEmitter, Injector, OnDestroy, Output, ReflectiveInjector, ResolvedReflectiveProvider, ViewContainerRef} from '@angular/core';
|
||||
import {RouterOutletMap} from '../router_outlet_map';
|
||||
import {ActivatedRoute} from '../router_state';
|
||||
import {PRIMARY_OUTLET} from '../shared';
|
||||
|
||||
/**
|
||||
* @whatItDoes Acts as a placeholder that Angular dynamically fills based on the current router
|
||||
* state.
|
||||
*
|
||||
* @howToUse
|
||||
*
|
||||
* ```
|
||||
* <router-outlet></router-outlet>
|
||||
* <router-outlet name='left'></router-outlet>
|
||||
* <router-outlet name='right'></router-outlet>
|
||||
* ```
|
||||
*
|
||||
* A router outlet will emit an activate event any time a new component is being instantiated,
|
||||
* and a deactivate event when it is being destroyed.
|
||||
*
|
||||
* ```
|
||||
* <router-outlet
|
||||
* (activate)='onActivate($event)'
|
||||
* (deactivate)='onDeactivate($event)'></router-outlet>
|
||||
* ```
|
||||
* @ngModule RouterModule
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: 'router-outlet'})
|
||||
export class RouterOutlet implements OnDestroy {
|
||||
private activated: ComponentRef<any>;
|
||||
private _activatedRoute: ActivatedRoute;
|
||||
public outletMap: RouterOutletMap;
|
||||
|
||||
@Output('activate') activateEvents = new EventEmitter<any>();
|
||||
@Output('deactivate') deactivateEvents = new EventEmitter<any>();
|
||||
|
||||
constructor(
|
||||
private parentOutletMap: RouterOutletMap, private location: ViewContainerRef,
|
||||
private resolver: ComponentFactoryResolver, @Attribute('name') private name: string) {
|
||||
parentOutletMap.registerOutlet(name ? name : PRIMARY_OUTLET, this);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void { this.parentOutletMap.removeOutlet(this.name ? this.name : PRIMARY_OUTLET); }
|
||||
|
||||
get locationInjector(): Injector { return this.location.injector; }
|
||||
get locationFactoryResolver(): ComponentFactoryResolver { return this.resolver; }
|
||||
|
||||
get isActivated(): boolean { return !!this.activated; }
|
||||
get component(): Object {
|
||||
if (!this.activated) throw new Error('Outlet is not activated');
|
||||
return this.activated.instance;
|
||||
}
|
||||
get activatedRoute(): ActivatedRoute {
|
||||
if (!this.activated) throw new Error('Outlet is not activated');
|
||||
return this._activatedRoute;
|
||||
}
|
||||
|
||||
detach(): ComponentRef<any> {
|
||||
if (!this.activated) throw new Error('Outlet is not activated');
|
||||
this.location.detach();
|
||||
const r = this.activated;
|
||||
this.activated = null;
|
||||
this._activatedRoute = null;
|
||||
return r;
|
||||
}
|
||||
|
||||
attach(ref: ComponentRef<any>, activatedRoute: ActivatedRoute) {
|
||||
this.activated = ref;
|
||||
this._activatedRoute = activatedRoute;
|
||||
this.location.insert(ref.hostView);
|
||||
}
|
||||
|
||||
deactivate(): void {
|
||||
if (this.activated) {
|
||||
const c = this.component;
|
||||
this.activated.destroy();
|
||||
this.activated = null;
|
||||
this._activatedRoute = null;
|
||||
this.deactivateEvents.emit(c);
|
||||
}
|
||||
}
|
||||
|
||||
activate(
|
||||
activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver, injector: Injector,
|
||||
providers: ResolvedReflectiveProvider[], outletMap: RouterOutletMap): void {
|
||||
if (this.isActivated) {
|
||||
throw new Error('Cannot activate an already activated outlet');
|
||||
}
|
||||
|
||||
this.outletMap = outletMap;
|
||||
this._activatedRoute = activatedRoute;
|
||||
|
||||
const snapshot = activatedRoute._futureSnapshot;
|
||||
const component: any = <any>snapshot._routeConfig.component;
|
||||
const factory = resolver.resolveComponentFactory(component);
|
||||
|
||||
const inj = ReflectiveInjector.fromResolvedProviders(providers, injector);
|
||||
this.activated = this.location.createComponent(factory, this.location.length, inj, []);
|
||||
this.activated.changeDetectorRef.detectChanges();
|
||||
|
||||
this.activateEvents.emit(this.activated.instance);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user