Skip to content

Commit

Permalink
feat: add custom url matchers for product and category route
Browse files Browse the repository at this point in the history
  • Loading branch information
fmalcher committed Nov 21, 2019
1 parent 0eddb6b commit f44e1b9
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 87 deletions.
26 changes: 0 additions & 26 deletions src/app/core/pipes/category-route.pipe.spec.ts

This file was deleted.

3 changes: 2 additions & 1 deletion src/app/core/pipes/category-route.pipe.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Pipe, PipeTransform } from '@angular/core';

import { Category } from 'ish-core/models/category/category.model';
import { generateCategoryRoute } from 'ish-core/route-formats/category.route';

@Pipe({ name: 'ishCategoryRoute', pure: true })
export class CategoryRoutePipe implements PipeTransform {
transform(category: Category): string {
return '/category/' + category.uniqueId;
return generateCategoryRoute(category);
}
}
30 changes: 1 addition & 29 deletions src/app/core/pipes/product-route.pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,7 @@ import { Pipe, PipeTransform } from '@angular/core';

import { Category } from 'ish-core/models/category/category.model';
import { Product } from 'ish-core/models/product/product.model';

function generateProductSlug(product: Product) {
return product && product.name ? product.name.replace(/[^a-zA-Z0-9-]+/g, '-').replace(/-+$/g, '') : undefined;
}

/**
* Generate a product detail route with optional category context.
* @param product The Product to genereate the route for
* @param category The optional Category that should be used as context for the product route
* @returns Product route string
*/

function generateProductRoute(product: Product, category?: Category): string {
if (!(product && product.sku)) {
return '/';
}
let productRoute = '/product/' + product.sku;
const productSlug = generateProductSlug(product);
if (productSlug) {
productRoute += '/' + productSlug;
}

if (category) {
productRoute = `/category/${category.uniqueId}${productRoute}`;
} else {
// TODO: add defaultCategory to route once this information is available with the products REST call
}
return productRoute;
}
import { generateProductRoute } from 'ish-core/route-formats/product.route';

@Pipe({ name: 'ishProductRoute', pure: true })
export class ProductRoutePipe implements PipeTransform {
Expand Down
15 changes: 15 additions & 0 deletions src/app/core/route-formats/category.route.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Category } from 'ish-core/models/category/category.model';

import { generateCategoryRoute } from './category.route';

describe('Category Route', () => {
let cat: Category;

beforeEach(() => {
cat = { uniqueId: 'cate' } as Category;
});

it('should generate category route for category', () => {
expect(generateCategoryRoute(cat)).toEqual('/category/cate');
});
});
23 changes: 23 additions & 0 deletions src/app/core/route-formats/category.route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { UrlSegment } from '@angular/router';

import { Category } from 'ish-core/models/category/category.model';

export function generateCategoryRoute(category: Category) {
return '/category/' + category.uniqueId;
}

/**
* UrlMatcher for product route
* Defines a specific URL format for the product page
*/
export function categoryRouteMatcher(url: UrlSegment[]) {
// Format: category/:categoryUniqueId
if (url[0].path === 'category') {
return {
posParams: {
categoryUniqueId: url[1],
},
consumed: url,
};
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,8 @@
import { TestBed } from '@angular/core/testing';
import * as using from 'jasmine-data-provider';

import { ProductRoutePipe } from './product-route.pipe';

describe('Product Route Pipe', () => {
let productRoutePipe: ProductRoutePipe;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [ProductRoutePipe],
});
productRoutePipe = TestBed.get(ProductRoutePipe);
});

it('should be created', () => {
expect(productRoutePipe).toBeTruthy();
});
import { generateProductRoute } from './product.route';

describe('Product Route', () => {
function dataProvider() {
return [
{
Expand Down Expand Up @@ -44,7 +30,7 @@ describe('Product Route Pipe', () => {
it(`should return ${dataSlice.expected} when supplying product '${JSON.stringify(
dataSlice.product
)}' and category '${JSON.stringify(dataSlice.category)}'`, () => {
expect(productRoutePipe.transform(dataSlice.product, dataSlice.category)).toEqual(dataSlice.expected);
expect(generateProductRoute(dataSlice.product, dataSlice.category)).toEqual(dataSlice.expected);
});
});
});
59 changes: 59 additions & 0 deletions src/app/core/route-formats/product.route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { UrlSegment } from '@angular/router';

import { Category } from 'ish-core/models/category/category.model';
import { Product } from 'ish-core/models/product/product.model';

function generateProductSlug(product: Product) {
return product && product.name ? product.name.replace(/[^a-zA-Z0-9-]+/g, '-').replace(/-+$/g, '') : undefined;
}

/**
* Generate a product detail route with optional category context.
* @param product The Product to genereate the route for
* @param category The optional Category that should be used as context for the product route
* @returns Product route string
*/
export function generateProductRoute(product: Product, category?: Category): string {
if (!(product && product.sku)) {
return '/';
}
let productRoute = '/product/' + product.sku;
const productSlug = generateProductSlug(product);
if (productSlug) {
productRoute += '/' + productSlug;
}

if (category) {
productRoute = `/category/${category.uniqueId}${productRoute}`;
} else {
// TODO: add defaultCategory to route once this information is available with the products REST call
}
return productRoute;
}

/**
* UrlMatcher for product route
* Defines a specific URL format for the product page
*/
export function productRouteMatcher(url: UrlSegment[]) {
// Format: product/:sku/:productSlug
if (url[0].path === 'product') {
return {
posParams: {
sku: url[1],
},
consumed: url,
};
}

// Format: category/:categoryUniqueId/product/:sku/:productSlug
if (url.length >= 4 && url[0].path === 'category' && url[2].path === 'product') {
return {
posParams: {
categoryUniqueId: url[1],
sku: url[3],
},
consumed: url,
};
}
}
12 changes: 10 additions & 2 deletions src/app/pages/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@ import { RouterModule, Routes } from '@angular/router';
import { FeatureToggleGuard } from 'ish-core/feature-toggle.module';
import { AuthGuard } from 'ish-core/guards/auth.guard';
import { LogoutGuard } from 'ish-core/guards/logout.guard';
import { categoryRouteMatcher } from 'ish-core/route-formats/category.route';
import { productRouteMatcher } from 'ish-core/route-formats/product.route';

const routes: Routes = [
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: 'home', loadChildren: () => import('./home/home-page.module').then(m => m.HomePageModule) },
{ path: 'error', loadChildren: () => import('./error/error-page.module').then(m => m.ErrorPageModule) },
{ path: 'product', loadChildren: () => import('./product/product-page.module').then(m => m.ProductPageModule) },
{ path: 'category', loadChildren: () => import('./category/category-page.module').then(m => m.CategoryPageModule) },
{
matcher: productRouteMatcher,
loadChildren: () => import('./product/product-page.module').then(m => m.ProductPageModule),
},
{
matcher: categoryRouteMatcher,
loadChildren: () => import('./category/category-page.module').then(m => m.CategoryPageModule),
},
{
path: 'account',
loadChildren: () => import('./account/account-page.module').then(m => m.AccountPageModule),
Expand Down
6 changes: 1 addition & 5 deletions src/app/pages/category/category-page.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,9 @@ import { CategoryTileComponent } from './components/category-tile/category-tile.

const categoryPageRoutes: Routes = [
{
path: ':categoryUniqueId',
path: '', // completely consumed by custom route matcher
component: CategoryPageContainerComponent,
},
{
path: ':categoryUniqueId/product',
loadChildren: () => import('../product/product-page.module').then(m => m.ProductPageModule),
},
];

@NgModule({
Expand Down
9 changes: 2 additions & 7 deletions src/app/pages/product/product-page.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,8 @@ import { ProductPageContainerComponent } from './product-page.container';

const productPageRoutes: Routes = [
{
path: ':sku',
children: [
{
path: '**',
component: ProductPageContainerComponent,
},
],
path: '', // completely consumed by custom route matcher
component: ProductPageContainerComponent,
},
];

Expand Down

0 comments on commit f44e1b9

Please sign in to comment.