Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

A very small MVC prototype, for real this time... but need help... of course

56 views
Skip to first unread message

luserdroog

unread,
Oct 5, 2019, 12:43:28 AM10/5/19
to
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 }

luserdroog

unread,
Oct 5, 2019, 3:52:40 PM10/5/19
to
On Friday, October 4, 2019 at 11:43:28 PM UTC-5, luserdroog wrote:

> 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() };

Small improvement. I can avoid repeating 'this.notify()' if all modifications
are routed through 'setValue()'.

let turtle = new Model();
turtle.setValue = function( value ){ this.value = value; this.notify() }
turtle.inc = function(){ this.setValue( this.value + 1 ) }
turtle.dec = function(){ this.setValue( this.value - 1 ) }

Evertjan.

unread,
Oct 5, 2019, 4:53:18 PM10/5/19
to
luserdroog <luser...@gmail.com> wrote on 05 Oct 2019 in
comp.lang.javascript:

> turtle.inc = function(){ this.setValue( this.value + 1 ) }

why not skip the setValue definition and do:

turtle.inc = function(){ this.value = ++this.value;this.notify(); };

--
Evertjan.
The Netherlands.
(Please change the x'es to dots in my emailaddress)

luserdroog

unread,
Oct 6, 2019, 2:26:42 AM10/6/19
to
On Saturday, October 5, 2019 at 3:53:18 PM UTC-5, Evertjan. wrote:
> luserdroog <luser...@gmail.com> wrote on 05 Oct 2019 in
> comp.lang.javascript:
>
> > turtle.inc = function(){ this.setValue( this.value + 1 ) }
>
> why not skip the setValue definition and do:
>
> turtle.inc = function(){ this.value = ++this.value;this.notify(); };
>

It seems more DRY to call 'notify' in just one place.

luserdroog

unread,
Oct 6, 2019, 2:38:36 AM10/6/19
to
On Friday, October 4, 2019 at 11:43:28 PM UTC-5, luserdroog wrote:
> 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.
>
[snip]

I think I have it worked out a little better. The model has the function
'setValue' which calls the Model class's 'notify' function.

The view modifies the DOM in response to an 'update' from the model.

The controller has functions named to match DOM element classes and
a single click handler function routes all click events to their
respective controller functions.

>
> $ 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=plus >+</div>
<div class=minus >-</div>

> ----- app.js ------

let qs = (sel,ctx)=> (ctx || document).querySelector( sel );
import {Model, View, Controller} from './mvc.js';

function main(){
turtle.setValue( 0 );
}


var turtle = new Model();
turtle.setValue = function( value ){ this.value = value; this.notify() };
turtle.inc = function(){ this.setValue( this.value + 1 ) };
turtle.dec = function(){ this.setValue( this.value - 1 ) };

var panel = new View();
panel.setModel( turtle );
panel.update = function(){ qs('.turtle').innerHTML = this.getModel().value; }

var control = panel.getController();
control.plus = function(){ this.getModel().inc() }
control.minus = function(){ this.getModel().dec() }


function click( e ){
if( typeof( control[ e.target.className ] ) == 'function' )
control[ e.target.className ]()
}

document.addEventListener('DOMContentLoaded', main );
document.addEventListener('click', click );

Evertjan.

unread,
Oct 6, 2019, 5:39:06 AM10/6/19
to
luserdroog <luser...@gmail.com> wrote on 06 Oct 2019 in
comp.lang.javascript:
I more meant why tou needed a setValue function.

So:

turtle.inc = function(){ this.value = ++this.value; };

Silvio

unread,
Oct 6, 2019, 10:42:42 AM10/6/19
to
On 10/6/19 11:39 AM, Evertjan. wrote:
> luserdroog <luser...@gmail.com> wrote on 06 Oct 2019 in
> comp.lang.javascript:
>
>> On Saturday, October 5, 2019 at 3:53:18 PM UTC-5, Evertjan. wrote:
>>> luserdroog <luser...@gmail.com> wrote on 05 Oct 2019 in
>>> comp.lang.javascript:
>>>
>>>> turtle.inc = function(){ this.setValue( this.value + 1 ) }
>>>
>>> why not skip the setValue definition and do:
>>>
>>> turtle.inc = function(){ this.value = ++this.value;this.notify(); };
>>>
>>
>> It seems more DRY to call 'notify' in just one place.
>
> I more meant why tou needed a setValue function.
>
> So:
>
> turtle.inc = function(){ this.value = ++this.value; };
>

this.value = ++this.value ???

luserdroog

unread,
Oct 6, 2019, 2:22:33 PM10/6/19
to
On Sunday, October 6, 2019 at 4:39:06 AM UTC-5, Evertjan. wrote:
> luserdroog <luser...@gmail.com> wrote on 06 Oct 2019 in
> comp.lang.javascript:
>
> > On Saturday, October 5, 2019 at 3:53:18 PM UTC-5, Evertjan. wrote:
> >> luserdroog <luser...@gmail.com> wrote on 05 Oct 2019 in
> >> comp.lang.javascript:
> >>
> >> > turtle.inc = function(){ this.setValue( this.value + 1 ) }
> >>
> >> why not skip the setValue definition and do:
> >>
> >> turtle.inc = function(){ this.value = ++this.value;this.notify(); };
> >>
> >
> > It seems more DRY to call 'notify' in just one place.
>
> I more meant why tou needed a setValue function.
>
> So:
>
> turtle.inc = function(){ this.value = ++this.value; };
>

The Model needs to call 'notify' after any change to the value.
It seemed convenient to combine these into a single action.
I get the feeling that I'm not seeing the point you're trying
to make.

Evertjan.

unread,
Oct 6, 2019, 4:25:49 PM10/6/19
to
luserdroog <luser...@gmail.com> wrote on 06 Oct 2019 in
comp.lang.javascript:

> On Sunday, October 6, 2019 at 4:39:06 AM UTC-5, Evertjan. wrote:
>> luserdroog <luser...@gmail.com> wrote on 06 Oct 2019 in
>> comp.lang.javascript:
>>
>> > On Saturday, October 5, 2019 at 3:53:18 PM UTC-5, Evertjan. wrote:
>> >> luserdroog <luser...@gmail.com> wrote on 05 Oct 2019 in
>> >> comp.lang.javascript:
>> >>
>> >> > turtle.inc = function(){ this.setValue( this.value + 1 ) }
>> >>
>> >> why not skip the setValue definition and do:
>> >>
>> >> turtle.inc = function(){ this.value = ++this.value;this.notify(); };
>> >>
>> >
>> > It seems more DRY to call 'notify' in just one place.
>>
>> I more meant why you needed a setValue function.
>>
>> So:
>>
>> turtle.inc = function(){ this.value = ++this.value; };
>>
>
> The Model needs to call 'notify' after any change to the value.
> It seemed convenient to combine these into a single action.
> I get the feeling that I'm not seeing the point you're trying
> to make.
>

Repeat: why you need a setValue function???

Jonas Thörnvall

unread,
Oct 6, 2019, 5:15:54 PM10/6/19
to
To not have a static model, isn't setValue part of the user interface "guessing wildly"

luserdroog

unread,
Oct 6, 2019, 5:25:52 PM10/6/19
to
To combine two actions which should always be performed together:
storing the new value and notifying observers that a new value
has been stored.

In my current playpen, I've moved it into the Model class so I don't
have to repeat it in each model.

luserdroog

unread,
Oct 12, 2019, 5:15:50 PM10/12/19
to
On Sunday, October 6, 2019 at 3:25:49 PM UTC-5, Evertjan. wrote:
> luserdroog <luser...@gmail.com> wrote on 06 Oct 2019 in
> comp.lang.javascript:
>
> >
> > The Model needs to call 'notify' after any change to the value.
> > It seemed convenient to combine these into a single action.
> > I get the feeling that I'm not seeing the point you're trying
> > to make.
> >
>
> Repeat: why you need a setValue function???
>

Since this exchange, I've learned about getters and setters. Is that
what you were suggesting? To hook my function on property access with
a setter?

Evertjan.

unread,
Oct 12, 2019, 5:53:33 PM10/12/19
to
luserdroog <luser...@gmail.com> wrote on 12 Oct 2019 in
comp.lang.javascript:
I am not suggesting you learned something
I'm not suggesting anything, I am just asking.

"To hook my function on property access"
What does "to hook A on B" mean?
Why would you need that?
0 new messages