Currently, Craft.UI.View mainly provides UI classes to encapsulate its method, data and DOM structure. View component is automatically rendered by ViewController according to view's lifycycle method implementation.
After you append view on the screen, you have to update its view (=DOM) by your self. (Therefore this is named as craft kit)
Shadow access pattern
Most primitive pattern is finding DOM element via this.shadow
.
class CounterView extends Craft.UI.View {
constructor(options){
super(options);
this.data = { count:0 };
}
countup(){
this.data.count++;
this.shadow.getElementById('counter').innerHTML = this.data.count;
}
template(componentId){
return `
<div id="root" class="root">
<span id="counter">${this.data.count}</span>
</div>
`;
}
}
In the latest chrome, you can use replaceChildren
instead of innerHTML
.
renderView pattern
Most basic pattern is using Craft.UI.View#renderView method.
This example is just replacing above this.shadow.getElementById
to renderView
.
class CounterView extends Craft.UI.View {
constructor(options){
super(options);
this.data = { count:0 };
}
countup(){
this.data.count++;
this.renderView();
}
template(componentId){
return `
<div id="root" class="root">
${this.data.count}
</div>
`;
}
}
Every time you call countup() method, template is rendered again. This pattern is useful while your data is simple.
If your view does not have this.data at the time it is instantiated, your template will be defined by optional chain somthing like this: ${this.data.YourDataSet?.count}
.
Swap and renderView pattern
In more complex application, you might swap template dynamically.
class CounterView extends Craft.UI.View {
constructor(options){
super(options);
this.data = { count:0 };
this.templates = {
even : `
<div id="root" class="root">
Some complex even view: ${this.data.count}
</div>`,
odd : `
<div id="root" class="root">
Some complex odd view: ${this.data.count}
</div>`,
};
this.crr_template = this.templates.even;
}
countup(){
this.data.count++;
if( this.data.count % 2 == 0 ){
this.crr_template = this.templates.even;
}else{
this.crr_template = this.templates.odd;
}
this.renderView();
}
template(componentId){
return this.crr_template;
}
}
renderView() renders template defined by this.crr_template.
Factorize pattern
This pattern defines more macro structure.
Also described in the post "How to manage sub views for array data", let's break down your template structure until it is almost atomically resolved.
class CounterView extends Craft.UI.View {
constructor(options){
super(options);
this.data = { count:0 };
this.views = { numbox:null };
}
countup(){
this.data.count++;
}
updateNumBoxView(){
let view = new NumBox({num:this.data.count});
this.replaceView({
id : 'container',
component : view,
});
this.views.numbox.unloadView();
this.views.numbox = view;
}
viewDidLoad(callback){
this.updateNumBoxView();
if(callback){ callback(); }
}
template(componentId){
return `
<div id="root" class="root">
<div id="container"></div>
</div>
`;
}
}
class NumBoxView extends Craft.UI.View {
constructor(options){
super(options);
this.data = options;
}
template(componentId){
return `
<div id="root" class="root">
${this.data.count}
</div>
`;
}
}
Every time you update count, new view component will be created and placed in the #id:container
. Call unloadView if you have to expire its memory.
Combined
Actual web application will load its data from remote api, or will be generated by user in run time, or so on. So you may have to combine several patterns.
class CounterView extends Craft.UI.View {
constructor(options){
super(options);
this.data = { count:0 };
this.views = {
even : new EvenNumBoxView({ delegate:this }),
odd : new OddNumBoxView({ delegate:this })
};
}
viewDidLoad(callback){
this.updateNumBoxView();
if(callback){ callback(); }
}
countup(){
this.data.count++;
this.updateNumBoxView();
}
updateNumBoxView(){
if( this.data.count % 2 == 0 ){
this.views.even.renderView();
this.replaceView({
id : 'container',
component : this.views.even,
});
}else{
this.views.odd.renderView();
this.replaceView({
id : 'container',
component : this.views.odd,
});
}
}
template(componentId){
return `
<div id="root" class="root">
<div id="container"></div>
</div>
`;
}
}
class EvenNumBoxView extends Craft.UI.View {
constructor(options){
super(options);
this.delegate = options.delegate;
}
template(componentId){
return `
<div id="root" class="root">
Some complex even view structure:
${this.delegate.data.count}
</div>
`;
}
}
class OddNumBoxView extends Craft.UI.View {
constructor(options){
super(options);
this.delegate = options.delegate;
}
template(componentId){
return `
<div id="root" class="root">
Some complex odd view structure:
${this.delegate.data.count}
</div>
`;
}
}
In this example, EvenNumBoxView and OddNumBoxView delegate data management to CounterView. (This is called as "delegate pattern")
NOTE
Above examples are runnable on playground.
var view = new CounterView();
view.loadView();
Craft.Core.Context.getRootViewController().appendSubView(view);
view.countup();
🛺 Try CraftKit Playground:
https://github.com/craftkit/craftkit-playground
Top comments (0)