Cascading ViewController as a Facade pattern - craftkit best practice
Tags: craftkit, javascript, shadowdom, webdev
Published: 2020-11-09 14:19:22


![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/4gnfwh73vlwyfcvbp2p8.png)

* ViewController is cascaded to its sub views
* ViewController can be described as Facade object

## Cascading ViewController

When you append a sub-view to View, its ViewController is automatically cascaded.

This is done by `Craft.Core.Component#appendSubView(|appendView)`. 

```javascript 
if( this.viewController ){
	component.setViewController(this.viewController);
}
``` 

When you append a sub-view to ViewController, the controller is set to the sub-view.

This is done by `Craft.Core.DefaultViewController#appendSubView(|appendView)`. 

So, to implement your own ViewController, you have to inherit DefaultRootViewControler.

```javascript 
component.setViewController(this);
``` 

Appending ViewController to View or ViewController is same as above.

So, multiple ViewController on the same page(=DOM), like ModalViewController on PageController, run intuitively.


## ViewController as Facade

To avoid complex object relation across your components, it is recommended to design your application by Delegate pattern.

Here is an example application using Delegate pattern. 
This application places 9 panels on the screen. Click one, show selected num.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/iaeqiw0ptewrjmdo33b8.png)

```javascript 
class PanelController extends Craft.UI.DefaultViewController {
	viewDidLoad(callback){
		for( let i=0; i<9; i++ ){
			let p = new Panel({
				delegate:this, num:i
			});
			this.appendView({
				id : 'container',
				component : p
			});
		}
		if(callback){ callback(); }
	}
	focus(num){
		this.shadow.getElementById('focus').innerHTML = num;
	}
	style(componentId){
		return `
			#container {
				width: 318px;
				display:flex; flex-direction:row; flex-wrap:wrap;
				margin-right:auto; margin-left:auto;
			}
		`;
	}
	template(componentId){
		return `
			<div>
				<div>
					Selected: <span id="focus"></span>
				</div>
				<div id="container"></div>
			</div>
		`;
	}
}
class Panel extends Craft.UI.View {
	constructor(options){
		super(options);
		this.delegate = options.delegate;
		this.data = { num:options.num };
	}
	style(componentId){
		return `
			.root {
				box-sizing:border-box;
				width:100px; height:100px; margin:3px;
				background-color:#eee;
				display: flex;
				justify-content: center;
				align-items: center;
			}
		`;
	}
	template(componentId){
		return `
			<div class="root">
				<div onclick="${componentId}.delegate.focus(${componentId}.data.num)">
					select: ${this.data.num}
				</div>
			</div>
		`;
	}
}
```

ViewController is always cascaded. 

So, you can write above sample like this. 

```javascript 
class PanelController extends Craft.UI.DefaultViewController {
	viewDidLoad(callback){
		for( let i=0; i<9; i++ ){
			let p = new Panel({
				num:i // without delegate
			});
			this.appendView({
				id : 'container',
				component : p
			});
		}
		if(callback){ callback(); }
	}
	focus(num){
		this.shadow.getElementById('focus').innerHTML = num;
	}
	style(componentId){
		return `
			.root { box-sizing:border-box; }
			#container {
				width: 318px;
				display:flex; flex-direction:row; flex-wrap:wrap;
				margin-right:auto; margin-left:auto;
			}
		`;
	}
	template(componentId){
		return `
			<div class="root">
				<div>
					Selected: <span id="focus"></span>
				</div>
				<div id="container"></div>
			</div>
		`;
	}
}
class Panel extends Craft.UI.View {
	constructor(options){
		// constructor without delegate
		super(options);
		this.data = { num:options.num };
	}
	style(componentId){
		return `
			.root {
				box-sizing:border-box;
				width:100px; height:100px; margin:3px;
				background-color:#eee;
				display: flex;
				justify-content: center;
				align-items: center;
			}
		`;
	}
	template(componentId){
		return `
			<div class="root">
				<!-- call via viewController -->
				<div onclick="${componentId}.viewController.focus(${componentId}.data.num)">
					select: ${this.data.num}
				</div>
			</div>
		`;
	}
}
```

In this code, PanelController looks more like implicit Facade pattern than Delegate pattern. 


## NOTE

Above examples are runnable on playground.

```javascript 
var view = new PanelController();
view.loadView();
Craft.Core.Context.getRootViewController().appendSubView(view);
``` 

🛺 Try CraftKit Playground:
https://github.com/craftkit/craftkit-playground