Upon rereading the helpful suggestions from Michael Haufe, I noticed this
link which I had missed before,
https://gist.github.com/mlhaufe/c841b2269b0099c3c52648717f9551cc
containing the very Model, View, and Controller classes in a short and
sweet, barebones implementation. I read through it, copied it by hand
into my notebook, and then retyped the handwritten copy to really get
a good sense of it all. So, I'd like to try to build up a very simple
usage example.
Suppose for simplicity that the model is just represented by a simple
integer and the only modifications are increment and decrement.
I want the page to display the current value and have clickable plus
and minus signs, buttons or divs or whatever.
In the following, incomplete solution, I instantiate a Model and specify
the member functions: setValue, inc, dec; all of which call this.notify()
after modifying the value.
The I instantiate a a View and set its model. And provide override the
default update function with a new function which queries the model and
modifies the DOM.
But I don't quite see how to stitch up the click handlers. And what do
I do with the Controller? Should my 'inc' and 'dec' functions go into
the controller object?
$ for f in eg.html app.js mvc.js; do echo ----- $f ------ ; cat $f ; done ;
----- eg.html ------
<!doctype html>
<html lang=en><meta charset=utf-8>
<script type=module src=app.js> </script>
<main>
<div class=turtle >?</div>
<div class=inc >+</div>
<div class=dec >-</div>
----- app.js ------
let qs = (sel,ctx)=> (ctx || document).querySelector( sel );
import {Model, View, Controller} from './mvc.js';
let turtle = new Model();
turtle.setValue = function( value ){ this.value = value; this.notify() };
turtle.inc = function(){ ++this.value; this.notify() };
turtle.dec = function(){ --this.value; this.notify() };
let panel = new View();
panel.setModel( turtle );
panel.update = function(){
qs('.turtle').innerHTML = this.getModel().value;
}
function main(){
turtle.setValue( 0 );
}
document.addEventListener('DOMContentLoaded', main );
----- mvc.js ------
class Model {
_observers = []
observe( observer ){ this._observers.push( observer ) }
unobserve(observer){ this._observers=this._observers.filter(o=> o!==observer)}
notify( data ){ this._observers.forEach(o=> o.update(data) ) } }
class View {
_subViews = []
update(){}
getModel(){ return this._model }
setModel( model ){ this._setModelAndController( model, this._controller ) }
getDefaultController(){ return new Controller }
getController(){ if( !this._controller )
this.setController(this.getDefaultController())
return this._controller}
setController(controller){this._setModelAndController(this._model,controller)}
_setModelAndController( model, controller ){
if( this._model !== model ){
if( this._model ) this._model.unobserve( this )
if( model ) model.observe( this )
this._model = model
}
if( controller ){ controller.setView( this ); controller.setModel( model ) }
this._controller = controller
}
getSubViews(){ return new Array( this._subViews ) }
addSubView( subView ){
var prev = subView.getSuperView()
if( prev ) prev.removeSubView( subView )
this._subViews.push( subView )
subView.setSuperView( this )
}
removeSubView( subView ){
this._subViews=this._subViews.filter(s=>{if(s===subView) s.setSuperView(null)
return s !== subView })
}
setSuperView( superView ){ this._superView = superView }
getSuperView(){ return this._superView }
destroy(){
if( this._model ) this._model.unobserve( this )
this._subViews.forEach(s=> s.destroy() )
} }
class Controller {
getModel(){ return this._model }
setModel( model ){ this._model = model }
getView(){ return this._view }
setView( view ){ this._view = view } }
export { Model, View, Controller }