fix(urlSerializer): improve findLinkByComponentData

This commit is contained in:
Adam Bradley
2016-09-16 12:36:38 -05:00
parent 1158a96288
commit 9d563f5438
3 changed files with 134 additions and 2 deletions

View File

@ -118,6 +118,7 @@ export interface NavLink {
partsLen?: number;
staticLen?: number;
dataLen?: number;
dataKeys?: {[key: string]: boolean};
defaultHistory?: any[];
}

View File

@ -1,5 +1,5 @@
import { NavLink, NavSegment } from '../nav-util';
import { UrlSerializer, isPartMatch, fillMatchedUrlParts, parseUrlParts, createMatchedData, normalizeLinks } from '../url-serializer';
import { UrlSerializer, isPartMatch, fillMatchedUrlParts, parseUrlParts, createMatchedData, normalizeLinks, findLinkByComponentData } from '../url-serializer';
import { mockDeepLinkConfig, noop, MockView1, MockView2, MockView3, MockView4, MockView5 } from '../../util/mock-providers';
@ -7,6 +7,18 @@ describe('UrlSerializer', () => {
describe('serializeComponent', () => {
it('should create segement when config has multiple links to same component', () => {
const link1 = { component: MockView1, name: 'viewone', segment: 'view' };
const link2 = { component: MockView1, name: 'viewtwo', segment: 'view/:param1' };
const link3 = { component: MockView1, name: 'viewthree', segment: 'view/:param1/:param2' };
serializer = mockSerializer([link1, link2, link3]);
serializer.createSegment = noop;
spyOn(serializer, 'createSegment');
serializer.serializeComponent(MockView1, null);
expect(serializer.createSegment).toHaveBeenCalledWith(link1, null);
});
it('should create segment if component found in links', () => {
serializer.createSegment = noop;
spyOn(serializer, 'createSegment');
@ -533,6 +545,88 @@ describe('UrlSerializer', () => {
});
describe('findLinkByComponentData', () => {
it('should get matching link by component w/ data and multiple links using same component, 2 matches', () => {
const link1 = { component: MockView1, name: 'viewone', segment: 'view' };
const link2 = { component: MockView1, name: 'viewtwo', segment: 'view/:param1' };
const link3 = { component: MockView1, name: 'viewthree', segment: 'view/:param1/:param2' };
let links = normalizeLinks([link1, link2, link3]);
let foundLink = findLinkByComponentData(links, MockView1, {
param1: false,
param2: 0,
param3: 0
});
expect(foundLink.name).toEqual('viewthree');
});
it('should get matching link by component w/ data and multiple links using same component, 1 match', () => {
const link1 = { component: MockView1, name: 'viewone', segment: 'view' };
const link2 = { component: MockView1, name: 'viewtwo', segment: 'view/:param1' };
const link3 = { component: MockView1, name: 'viewthree', segment: 'view/:param1/:param2' };
let links = normalizeLinks([link1, link2, link3]);
let foundLink = findLinkByComponentData(links, MockView1, {
param1: false,
param3: 0
});
expect(foundLink.name).toEqual('viewtwo');
});
it('should get matching link by component w/ no data and multiple links using same component', () => {
const link1 = { component: MockView1, name: 'viewone', segment: 'view' };
const link2 = { component: MockView1, name: 'viewtwo', segment: 'view/:param1' };
const link3 = { component: MockView1, name: 'viewthree', segment: 'view/:param1/:param2' };
let links = normalizeLinks([link1, link2, link3]);
let foundLink = findLinkByComponentData(links, MockView1, null);
expect(foundLink.name).toEqual('viewone');
});
it('should get matching link by component data and link data', () => {
const link1 = { component: MockView1, name: 'viewone', segment: 'view' };
const link2 = { component: MockView2, name: 'viewtwo', segment: 'view/:param1' };
const link3 = { component: MockView3, name: 'viewthree', segment: 'view/:param1/:param2' };
let links = normalizeLinks([link1, link2, link3]);
let foundLink = findLinkByComponentData(links, MockView3, {
param1: null,
param2: false,
param3: 0,
param4: 'hello'
});
expect(foundLink.name).toEqual('viewthree');
});
it('should get matching link by component without data and link without data', () => {
const link1 = { component: MockView1, name: 'viewone', segment: 'view' };
const link2 = { component: MockView2, name: 'viewtwo', segment: 'view/:param1' };
const link3 = { component: MockView3, name: 'viewthree', segment: 'view/:param1/:param2' };
let links = normalizeLinks([link1, link2, link3]);
let foundLink = findLinkByComponentData(links, MockView1, null);
expect(foundLink.name).toEqual('viewone');
});
it('should get no matching link by component without data, but link requires data', () => {
const link1 = { component: MockView1, name: 'viewone', segment: 'view' };
const link2 = { component: MockView2, name: 'viewtwo', segment: 'view/:param1' };
const link3 = { component: MockView3, name: 'viewthree', segment: 'view/:param1/:param2' };
let links = normalizeLinks([link1, link2, link3]);
let foundLink = findLinkByComponentData(links, MockView2, null);
expect(foundLink).toEqual(null);
});
});
describe('normalizeLinks', () => {
it('should sort with four parts, the most number of paths w/out data first', () => {

View File

@ -59,7 +59,7 @@ export class UrlSerializer {
*/
serializeComponent(component: any, data: any): NavSegment {
if (component) {
const link = this.links.find(l => component === l.component || component.name === l.name);
const link = findLinkByComponentData(this.links, component, data);
if (link) {
return this.createSegment(link, data);
}
@ -203,6 +203,41 @@ export const createMatchedData = (matchedUrlParts: string[], link: NavLink): any
return data;
};
export const findLinkByComponentData = (links: NavLink[], component: any, instanceData: any): NavLink => {
let foundLink: NavLink = null;
let foundLinkDataMatches = -1;
for (var i = 0; i < links.length; i++) {
var link = links[i];
if (link.component === component) {
// ok, so the component matched, but multiple links can point
// to the same component, so let's make sure this is the right link
var dataMatches = 0;
if (instanceData) {
var instanceDataKeys = Object.keys(instanceData);
// this link has data
for (var j = 0; j < instanceDataKeys.length; j++) {
if (isPresent(link.dataKeys[instanceDataKeys[j]])) {
dataMatches++;
}
}
} else if (link.dataLen) {
// this component does not have data but the link does
continue;
}
if (dataMatches >= foundLinkDataMatches) {
foundLink = link;
foundLinkDataMatches = dataMatches;
}
}
}
return foundLink;
};
export const normalizeLinks = (links: NavLink[]): NavLink[] => {
for (var i = 0, ilen = links.length; i < ilen; i++) {
var link = links[i];
@ -211,6 +246,7 @@ export const normalizeLinks = (links: NavLink[]): NavLink[] => {
link.segment = link.name;
}
link.dataKeys = {};
link.parts = link.segment.split('/');
link.partsLen = link.parts.length;
@ -222,6 +258,7 @@ export const normalizeLinks = (links: NavLink[]): NavLink[] => {
if (link.parts[j].charAt(0) === ':') {
link.dataLen++;
stillCountingStatic = false;
link.dataKeys[link.parts[j].substring(1)] = true;
} else if (stillCountingStatic) {
link.staticLen++;