Handling popstate by Route object - craftkit code pattern
Tags: craftkit, javascript, shadowdom, webdev
Published: 2020-11-10 14:25:41

![routing](https://dev-to-uploads.s3.amazonaws.com/i/lku8q75is4lj588f4gmk.png)

In craftkit application, routing is managed by RootViewController, a special type of ViewController. 

It defines interface for handling popstate event, by `resolveRoutingRequest(route)` method. 

When you implement your own RootViewController, you have to implement this method. 

The routing request is encapsulated by Route object, and it is passed to the method. 

## Route object

| param  | desc |
|:-------|:-----|
| launch | true if called from RootViewController#bringup | 
| path   | parsed path | 
| event  | defined if this is called by browser back/forward with popstate event | 

Parsing and normalizing `Route.path` is responsibility of your RootViewController implementation. (This is not the same as `location.pathname`)

To control History correctly, you have to check popstate event and launch flag like this:

```javascript 
let event  = route ? route.event  : null;
let launch = route ? route.launch : false;

if( launch ){
	// your are in launching sequence, and you should update history
	window.history.replaceState({},title,path);
}else{
	if( !event ){
		// called by browser back/forward with popstate event
		window.history.pushState({},title,path);
	}
}
``` 

This is idiom. 

Every time you implement your application's RootViewController, your will write this kind of block. 

## Example

```javascript 
class Apple extends Craft.UI.View {
	template(componentId){
		return `
			<div class="root">🍎</div>
		`;
	}
}
class Orange extends Craft.UI.View {
	template(componentId){
		return `
			<div class="root">🍊</div>
		`;
	}
}
class PageController extends Craft.UI.DefaultRootViewController {
	constructor(options){
		super(options);
		this.data = {
			map: {
				apple: new Apple(),
				orange: new Orange()
			}
		};
	}
	resolveRoutingRequest(route){
		let path = route.path || 'apple'
		let view = this.data.map[path];
		this.replaceView(view);
		if( route.launch ){
			// launching the app
			window.history.replaceState({},'','#/'+path);
		}else{
			if( !route.event ){
				// in app navigation
				window.history.pushState({},'','#/'+path);
			}
		}
	}
}
``` 

This example code can be run on playground like following:

```javascript
var rootViewController = new PageController();
Craft.Core.Context.setRootViewController(rootViewController);

rootViewController.bringup();
``` 

`bringup` method starts resolving Route object from popstate. 
In this case, default view 🍎 is shown. 

Then, focus url bar (command-l), and change trailing 'apple' to 'orange'. 
🍊 is shown. 

Push back button, 🍎 is shown again. 

## NOTE

Path normalization is supported by Craft.Core.HashRouter and Craft.Core.PathRouter. This is GoF Strategy pattern. 

HashRouter is default. You can set it at the boot time of your application.  

```javascript 

// in your bootloader

Craft.Core.Bootstrap.boot({
	router : Craft.Core.HashRouter,
	didBootApplication : function(){
		Craft.Core.Defaults.ALLOW_COMPONENT_SHORTCUT = true;
		let rootViewController = new PageController();
		Craft.Core.Context.setRootViewController(rootViewController);
		rootViewController.bringup();
	}
})

// in your RootViewController 

let normalized_path = Craft.Core.Context.getRouter().normalize(path);

if( route.launch ){
	window.history.replaceState({},'',path);
}else{
	if( !route.event ){
		window.history.replaceState({},'',path);
	}
}
``` 

See GitHub for more about boot setting. 

https://github.com/craftkit/craft-uikit