npm package: @specno/routes
Description: The official [Routes](https://bitbucket.org/specno/npm-packages/src/main/packages/routes/) library for [Specno](https://www.specno.com/).
Latest version: 1.0.5

# SpecnoRoutes

The official [Routes](https://bitbucket.org/specno/npm-packages/src/main/packages/routes/) library for [Specno](https://www.specno.com/).

<strong><pre>npm i @specno/routes</pre></strong>

SpecnoRoutes assists in making routing within [Angular](https://angular.io/) more explicit and typesafe. It aims to provide a more conventionalized approach to declaring and interacting with Routes throughout your application

## Example use

```ts
// core/consts/paths.ts
import { BuildPaths, Path, ParamPath } from '@specno/routes';

// config is optional
const config = {
    shouldEncodeURIComponents: true,
};
export const PATHS = BuildPaths(
    {
        Home: Path('home', {
            AboutUs: Path('about-us')
        }),
        Store: ParamPath('storeId')
        Product: ParamPath('productId')
    },
    config
);
```

```ts
// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { PATHS } from './core/consts';

const routes: Routes = [
  {
    path: PATHS.Home._RelativePath,
    children: [
      {
        path: '',
        component: HomeComponent,
      },
      {
        path: PATHS.Home.AboutUs,
        component: _RelativePath,
      },
    ],
  },
  {
    path: PATHS.Store._RelativePath,
    component: StoreComponent,
  },
  {
    path: PATHS.Product._RelativePath,
    component: ProductComponent,
  },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}
```

```ts
// app.component.ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { PATHS } from './core/consts';

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.scss'],
})
export class AppComponent implements OnInit {
  constructor(private router: Router) {}

  ngOnInit() {}

  navigateToHome() {
    this.router.navigateByUrl(PATHS.Home._Path);
  }
  navigateToAboutUs() {
    this.router.navigateByUrl(PATHS.Home.AboutUs._Path);
  }
  navigateToStore(storeId: string) {
    this.router.navigateByUrl(PATHS.Store._PathWithParams(storeId));
  }
  navigateToProduct(productId: string) {
    this.router.navigateByUrl(PATHS.Product._PathWithParams(productId));
  }
}
```

## Compatibility

Since this library has been built using only typescript - it should be compatible with most, if not all, other typescript based projects.

### Having troubles?

Try get help on our [Dev Slack Channel](https://specno.slack.com/archives/C01C9LQNW56) first. If your issue is still persisting and there's no assistance provided by our slack channel - then I would suggest submitting a ticket to our [Jira Board](https://specno.atlassian.net/jira/software/c/projects/SI/boards/25/backlog).

> **NOTE:** SpecnoRoutes is maintained by Specnites so any issue you can _encounter, document and fix_, is an issue another Specnite won't have deal with in the future - _saving blood, sweat and potentially tears_.

## Developer Guide

Using SpecnoRoutes is really straight forward and shouldn't be difficult to interact with.

### `Path`

Should be used when creating paths **without** any parameters.

```ts
const home = Path('home');
// path is set to "home"

const parent = Path('parent', {
  child: Path('child'),
});
// path is set to "parent" and "child" respectfully
```

### `ParamPath`

Should be used when creating paths **with** parameters.

```ts
const store = ParamPath('storeId');
// path is set to ":storeId"
// notice the ":" prefixing the provided path

const parent = ParamPath('parent', {
  child: ParamPath('child'),
});
// path is set to ":parent" and ":child" respectfully
// notice the ":" prefixing the provided paths
```

> **NOTE:** [Path](#path) and [ParamPath](#parampath) should **_not_** be used outside of [BuildPaths](#buildpaths) as they provide no value outside of [BuildPaths](#buildpaths).

### `BuildPaths`

This is where a majority of the work is done. [BuildPaths](#buildpaths) essentially compiles your [Paths](#path) and [ParamPaths](#parampath) into a dictionary that provides some additional utility.

[BuildPaths](#buildpaths) takes in two arguments:

1. [Path Dictionary](#path-dictionary)
2. [Config](#config)

#### Path Dictionary

A simple key-value pair with the values being a [Path](#path) or [ParamPath](#parampath). The `keys` will be used when interacting with the result of [BuildPaths](#buildpaths).

```ts
const PathDictionary = {
  Home: Path('home', {
    AboutUs: Path('about-us', {
      ContactUs: Path('contact-us'),
    }),
  }),
};
```

#### Config

Currently only supports configuring `shouldEncodeURIComponents`.

```ts
const config = {
  shouldEncodeURIComponents: true,
};
```

`shouldEncodeURIComponents` Indicates whether or not you wish to URI Encode the result of [\_PathWithParams](#_pathwithparams-and-_relativepathwithparams) and [\_RelativePathWithParams](#_pathwithparams-and-_relativepathwithparams).

```ts
const PATHS = BuildPaths({
  Product: Path('product', {
    ProductId: ParamPath('productId'),
  }),
});
PATHS.Product.ProductId._Path;
// 'product/:productId'

// completely valid ShopifyId
const productId = 'SomeIdThatEndsInEquals==';

// NON-URI encoded: Unsafe as a URL
// shouldEncodeURIComponents = FALSE
PATHS.Product.ProductId._PathWithParams(productId);
// 'product/SomeIdThatEndsInEquals=='
// == is not a valid URL and will likely break your navigation

// URI encoded: Completely safe as a URL
// shouldEncodeURIComponents = TRUE
PATHS.Product.ProductId._PathWithParams(productId);
// 'product/SomeIdThatEndsInEquals%3D%3D'
```

### Compiled Path Properties

Each path defined within [BuildPaths](#buildpaths) has a number of default properties:

#### `_Path`:

Your path **including** its parent's path

```ts
const PATHS = BuildPaths({
  Home: Path('home', {
    AboutUs: Path('about-us', {
      ContactUs: Path('contact-us'),
    }),
  }),
});

PATHS.Home.AboutUs._Path;
// home/about-us

PATHS.Home.AboutUs.ContactUs._Path;
// home/about-us/contact-us
```

#### `_RelativePath`:

Your path **excluding** its parent's path

```ts
const PATHS = BuildPaths({
  Home: Path('home', {
    AboutUs: Path('about-us', {
      ContactUs: Path('contact-us'),
    }),
  }),
});

PATHS.Home.AboutUs._RelativePath;
// about-us

PATHS.Home.AboutUs.ContactUs._RelativePath;
// contact-us
```

#### `_PathWithParams` and `_RelativePathWithParams`:

Functions that return the appropriate path with the params swapped out with the provided values

```ts
const PATHS = BuildPaths({
  Store: ParamPath('storeId', {
    Product: ParamPath('productId'),
  }),
});

PATHS.Store._Path;
// :storeId
PATHS.Store._PathWithParams(1234);
// 1234
PATHS.Store.Product._Path;
// :storeId/:productId
PATHS.Store.Product._PathWithParams(1234, 5678);
// 1234/5678
```

> **WARNING:** You may notice all of the attributes are prefixed with `_`, this is purely to avoid any conflict between an attribute name and a child path. _Writing paths that overlap with these attributes may result in unexpected problems._