fix(router): router-link works without params
Router-link attaches a listener to prevent default behavior and navigate. Closes: 1689
This commit is contained in:
parent
c2a42d5d2b
commit
77d1fc149a
@ -127,7 +127,12 @@ export class Parse5DomAdapter extends DomAdapter {
|
|||||||
return this.createEvent(eventType);
|
return this.createEvent(eventType);
|
||||||
}
|
}
|
||||||
createEvent(eventType) {
|
createEvent(eventType) {
|
||||||
return {type: eventType};
|
var evt = {
|
||||||
|
type: eventType,
|
||||||
|
defaultPrevented: false,
|
||||||
|
preventDefault: () => {evt.defaultPrevented = true}
|
||||||
|
};
|
||||||
|
return evt;
|
||||||
}
|
}
|
||||||
getInnerHTML(el) {
|
getInnerHTML(el) {
|
||||||
return serializer.serialize(this.templateAwareRoot(el));
|
return serializer.serialize(this.templateAwareRoot(el));
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {RegExp, RegExpWrapper, RegExpMatcherWrapper, StringWrapper, isPresent} from 'angular2/src/facade/lang';
|
import {RegExp, RegExpWrapper, RegExpMatcherWrapper, StringWrapper, isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||||
import {Map, MapWrapper, StringMap, StringMapWrapper, List, ListWrapper} from 'angular2/src/facade/collection';
|
import {Map, MapWrapper, StringMap, StringMapWrapper, List, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
import {escapeRegex} from './url';
|
import {escapeRegex} from './url';
|
||||||
@ -27,6 +27,9 @@ class DynamicSegment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
generate(params:StringMap) {
|
generate(params:StringMap) {
|
||||||
|
if (!StringMapWrapper.contains(params, this.name)) {
|
||||||
|
throw new BaseException(`Route generator for '${this.name}' was not included in parameters passed.`)
|
||||||
|
}
|
||||||
return StringMapWrapper.get(params, this.name);
|
return StringMapWrapper.get(params, this.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,6 +121,7 @@ export class PathRecognizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
generate(params:StringMap):string {
|
generate(params:StringMap):string {
|
||||||
return ListWrapper.join(ListWrapper.map(this.segments, (segment) => '/' + segment.generate(params)), '');
|
return ListWrapper.join(ListWrapper.map(this.segments, (segment) =>
|
||||||
|
'/' + segment.generate(params)), '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
20
modules/angular2/src/router/router_link.js
vendored
20
modules/angular2/src/router/router_link.js
vendored
@ -1,5 +1,6 @@
|
|||||||
import {Directive} from 'angular2/src/core/annotations_impl/annotations';
|
import {Directive, onAllChangesDone} from 'angular2/src/core/annotations_impl/annotations';
|
||||||
import {ElementRef} from 'angular2/core';
|
import {ElementRef} from 'angular2/core';
|
||||||
|
import {StringMap, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
import {isPresent} from 'angular2/src/facade/lang';
|
import {isPresent} from 'angular2/src/facade/lang';
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
@ -32,33 +33,40 @@ import {Router} from './router';
|
|||||||
properties: {
|
properties: {
|
||||||
'route': 'routerLink',
|
'route': 'routerLink',
|
||||||
'params': 'routerParams'
|
'params': 'routerParams'
|
||||||
}
|
},
|
||||||
|
lifecycle: [onAllChangesDone]
|
||||||
})
|
})
|
||||||
export class RouterLink {
|
export class RouterLink {
|
||||||
_domEl;
|
_domEl;
|
||||||
_route:string;
|
_route:string;
|
||||||
_params:any;
|
_params:any;
|
||||||
_router:Router;
|
_router:Router;
|
||||||
//TODO: handle click events
|
_href:string;
|
||||||
|
|
||||||
constructor(elementRef:ElementRef, router:Router) {
|
constructor(elementRef:ElementRef, router:Router) {
|
||||||
this._domEl = elementRef.domElement;
|
this._domEl = elementRef.domElement;
|
||||||
this._router = router;
|
this._router = router;
|
||||||
|
this._params = StringMapWrapper.create();
|
||||||
|
DOM.on(this._domEl, 'click', (evt) => {
|
||||||
|
evt.preventDefault();
|
||||||
|
this._router.navigate(this._href);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
set route(changes) {
|
set route(changes) {
|
||||||
this._route = changes;
|
this._route = changes;
|
||||||
this.updateHref();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set params(changes) {
|
set params(changes) {
|
||||||
this._params = changes;
|
this._params = changes;
|
||||||
this.updateHref();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateHref() {
|
onAllChangesDone() {
|
||||||
if (isPresent(this._route) && isPresent(this._params)) {
|
if (isPresent(this._route) && isPresent(this._params)) {
|
||||||
var newHref = this._router.generate(this._route, this._params);
|
var newHref = this._router.generate(this._route, this._params);
|
||||||
|
this._href = newHref;
|
||||||
|
// Keeping the link on the element to support contextual menu `copy link`
|
||||||
|
// and other in-browser affordances.
|
||||||
DOM.setAttribute(this._domEl, 'href', newHref);
|
DOM.setAttribute(this._domEl, 'href', newHref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
modules/angular2/src/router/router_outlet.js
vendored
2
modules/angular2/src/router/router_outlet.js
vendored
@ -18,7 +18,7 @@ export class RouterOutlet {
|
|||||||
_router:routerMod.Router;
|
_router:routerMod.Router;
|
||||||
_viewContainer:ViewContainerRef;
|
_viewContainer:ViewContainerRef;
|
||||||
|
|
||||||
constructor(viewContainer:ViewContainerRef, compiler:Compiler, router:routerMod.Router, injector:Injector, @Attribute('name') nameAttr) {
|
constructor(viewContainer:ViewContainerRef, compiler:Compiler, router:routerMod.Router, injector:Injector, @Attribute('name') nameAttr:String) {
|
||||||
if (isBlank(nameAttr)) {
|
if (isBlank(nameAttr)) {
|
||||||
nameAttr = 'default';
|
nameAttr = 'default';
|
||||||
}
|
}
|
||||||
|
38
modules/angular2/test/router/outlet_spec.js
vendored
38
modules/angular2/test/router/outlet_spec.js
vendored
@ -34,7 +34,7 @@ import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_meta
|
|||||||
export function main() {
|
export function main() {
|
||||||
describe('Outlet Directive', () => {
|
describe('Outlet Directive', () => {
|
||||||
|
|
||||||
var ctx, tb, view, rtr;
|
var ctx, tb, view, rtr, location;
|
||||||
|
|
||||||
beforeEachBindings(() => [
|
beforeEachBindings(() => [
|
||||||
Pipeline,
|
Pipeline,
|
||||||
@ -46,10 +46,11 @@ export function main() {
|
|||||||
}, [RouteRegistry, Pipeline, Location])
|
}, [RouteRegistry, Pipeline, Location])
|
||||||
]);
|
]);
|
||||||
|
|
||||||
beforeEach(inject([TestBed, Router], (testBed, router) => {
|
beforeEach(inject([TestBed, Router, Location], (testBed, router, loc) => {
|
||||||
tb = testBed;
|
tb = testBed;
|
||||||
ctx = new MyComp();
|
ctx = new MyComp();
|
||||||
rtr = router;
|
rtr = router;
|
||||||
|
location = loc;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function compile(template:string = "<router-outlet></router-outlet>") {
|
function compile(template:string = "<router-outlet></router-outlet>") {
|
||||||
@ -131,8 +132,18 @@ export function main() {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should generate link hrefs without params', inject([AsyncTestCompleter], (async) => {
|
||||||
|
compile('<a href="hello" router-link="user"></a>')
|
||||||
|
.then((_) => rtr.config({'path': '/user', 'component': UserCmp, 'as': 'user'}))
|
||||||
|
.then((_) => rtr.navigate('/a/b'))
|
||||||
|
.then((_) => {
|
||||||
|
view.detectChanges();
|
||||||
|
expect(DOM.getAttribute(view.rootNodes[0].childNodes[0], 'href')).toEqual('/user');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should generate link hrefs', inject([AsyncTestCompleter], (async) => {
|
it('should generate link hrefs with params', inject([AsyncTestCompleter], (async) => {
|
||||||
ctx.name = 'brian';
|
ctx.name = 'brian';
|
||||||
compile('<a href="hello" router-link="user" [router-params]="{name: name}">{{name}}</a>')
|
compile('<a href="hello" router-link="user" [router-params]="{name: name}">{{name}}</a>')
|
||||||
.then((_) => rtr.config({'path': '/user/:name', 'component': UserCmp, 'as': 'user'}))
|
.then((_) => rtr.config({'path': '/user/:name', 'component': UserCmp, 'as': 'user'}))
|
||||||
@ -145,6 +156,27 @@ export function main() {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should generate link hrefs without params', inject([AsyncTestCompleter], (async) => {
|
||||||
|
compile('<a href="hello" router-link="user"></a>')
|
||||||
|
.then((_) => rtr.config({'path': '/user', 'component': UserCmp, 'as': 'user'}))
|
||||||
|
.then((_) => rtr.navigate('/a/b'))
|
||||||
|
.then((_) => {
|
||||||
|
view.detectChanges();
|
||||||
|
var anchorEl = view.rootNodes[0].childNodes[0];
|
||||||
|
expect(DOM.getAttribute(anchorEl, 'href')).toEqual('/user');
|
||||||
|
|
||||||
|
var dispatchedEvent = DOM.createMouseEvent('click');
|
||||||
|
DOM.dispatchEvent(anchorEl, dispatchedEvent);
|
||||||
|
expect(dispatchedEvent.defaultPrevented).toBe(true);
|
||||||
|
|
||||||
|
// router navigation is async.
|
||||||
|
rtr.subscribe((_) => {
|
||||||
|
expect(location.urlChanges).toEqual(['/user']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,5 +68,11 @@ export function main() {
|
|||||||
recognizer.addConfig('/app/user/:name', handler, 'user');
|
recognizer.addConfig('/app/user/:name', handler, 'user');
|
||||||
expect(recognizer.generate('user', {'name' : 'misko'})).toEqual('/app/user/misko');
|
expect(recognizer.generate('user', {'name' : 'misko'})).toEqual('/app/user/misko');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw in the absence of required params URLs', () => {
|
||||||
|
recognizer.addConfig('/app/user/:name', handler, 'user');
|
||||||
|
expect(() => recognizer.generate('user', {})).toThrowError(
|
||||||
|
'Route generator for \'name\' was not included in parameters passed.');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user