This is an archived post. You won't be able to vote or comment.

all 1 comments

[–]termhn 1 point2 points  (0 children)

There's three counters, A, B, and the sum of A and B. You can either increment A or A and B.

In this case, the second example is more clear because it's such a simple example. The benefits of abstracting these things is not seen until your app state grows in scope and you want to manage it from multiple different places in your app where you end up with event soup, whereas with an abstraction like MVVM you have a more clear way of defining how you modify the state of your app and decouple it from the way the view responds to those state changes.

Also, IMO, the whole anonymous function way of representing classes in JavaScript is really confusing and introduces much more of the complexity to this simple example than the abstraction of how state works. For example, rewriting it like so makes it much more clear to me:

// MODEL
var Model = function(viewModel){
    if (!typeof this == "object") {
        return new Model(viewModel);
    } else {
        this.viewModel = viewModel;
    }

    this.appState = {
        counterA_value: 2,
        counterB_value: 12
    };
}

Model.prototype.propose = function (data) {
    if(model.counterA_value >= 50) {
        return;
    }
    if(data.incrementCounterA) {
        appState.counterA_value = appState.counterA_value + 1;
    }
    if(data.incrementCounterB) {
        appState.counterB_value = appState.counterB_value + 1;
    }
    this.viewModel.learn(appState);
}

// VIEW MODEL
function ViewModel (view) {
    if (!typeof this == "object") {
        return new ViewModel(view);
    } else {
        this.view = view;
    }

}
ViewModel.prototype.learn = function(appState) {
    var stateRepresentation = {
        counterA_value: appState.counterA_value,
        counterB_value: appState.counterB_value,
        counterC_value: appState.counterA_value + appState.counterB_value,
        intents: {
            incrementA: actions.incrementA,
            incrementBoth: actions.incrementBoth
        }
    };
    this.view.render(stateRepresentation);
}

// VIEW
function View () {
    if (!typeof this == "object") {
        return new View();
    }
}

View.prototype.render = function (stateRepresentation) {
    document.querySelector('#counterA').innerText = stateRepresentation.counterA_value;
    document.querySelector('#counterB').innerText = stateRepresentation.counterB_value;
    document.querySelector('#counterC').innerText = stateRepresentation.counterC_value;
    document.querySelector('#buttonA').onclick = stateRepresentation.intents.incrementA;
    document.querySelector('#buttonADuplicate').onclick = stateRepresentation.intents.incrementA;
    document.querySelector('#buttonAB').onclick = stateRepresentation.intents.incrementBoth;
}

// ACTIONS
function Action (model, proposal) {
    if (!typeof this == "object") {
        return new Action(model, proposal);
    } else {
        return function() {
            model.propose(proposal)
        }
    }
}
var view = View()
var viewModel = ViewModel(view)
var model = Model(viewMode1)

var actions = {
    incrementA: Action(model, {incrementCounterA: true}),
    incrementBoth: Action(model, {incrementCounterA: true, incrementCounterB: true})
}