- 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)
.
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.
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.
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.
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.
var view = new PanelController();
view.loadView();
Craft.Core.Context.getRootViewController().appendSubView(view);
🛺 Try CraftKit Playground:
https://github.com/craftkit/craftkit-playground
Top comments (0)