Skip to content

View Model Patterns

Leon Sorokin edited this page Jan 26, 2016 · 3 revisions
  1. Data & View (impractical, not recommended)

    This is the most basic working example and is here only to demonstrate the minimum code required. If you ever need to monkey-patch a solution without causing view layer breakage or resorting to manual DOM manip, jQuery, etc., this is good to keep in mind.

    // some data
    var name = "Jack";
    var age = 33;
    
    // view constructor
    function MyView() {				// view closure
    	return function() {				// render()
    		return ["div",					// template
    			["strong", name],
    			["em", age],
    		];
    	};
    }
    
    // init the 'view model' (vm) and render it to DOM
    var vm = domvm(MyView).mount(document.body);
    
    // now we can update the data & redraw the view
    age = 42;
    vm.redraw();
  2. Disjoint Model & View

    Reworking the example above, we wrap the same data in an object (our basic model) and explicitly pass the model into the view during init. This way, you don't need to rely on data being in-scope of the view definition; of course nothing stops you from using both in-scope data and an explicit model.

    // the model can be any object or array and can expose
    // whatever domain API, methods or properties you need
    var myModel = {name: "Jack", age: 33};
    
    // the view closure can hold any needed view state and
    // should return a template-generating `render` function
    // or an object containing it: {render: function() {}}
    function MyView(vm, model) {								// passed-in model lands here
    	return {
    		render: function() {
    			return ["div",
    				["strong", model.name],
    				["em", model.age],
    			];
    		}
    	}
    }
    
    // init the 'view model' (vm) and render it to DOM
    var vm = domvm(MyView, myModel).mount(document.body);		// pass model into init
    
    // now we can update the model & redraw the view
    myModel.age = 42;
    vm.redraw();
  3. Model-Integrated Binding

    Building on the concepts above, you can choose to create a constructor-based model that incorporates a view-model binding.

    function Person(name, age) {
    	this.name = name;
    	this.age = age;
    
    	this.viewOne = [PersonView, this];		// view-model binding
    }
    
    function PersonView(vm, person) {
    	person.vm = vm;							// export the vm, so we can call <person>.vm.redraw() externally
    	return {
    		render: function() {
    			return ["div",
    				["strong", person.name],
    				["em", person.age],
    			];
    		}
    	}
    }
    
    var jack = new Person("Jack", 33);
    
    domvm(jack.viewOne).mount(document.body);
    
    jack.age = 42;
    jack.vm.redraw();
  4. Model-Integrated View

    More monolithic still, you can wrap the view definition itself in the constructor closure and keep everything private. This is also not very practical, but here to demonstrate what can be done.

    function Person(name, age) {
    	var vm0;
    	
    	function PersonView(vm) {
    		vm0 = vm;
    		return {
    			render: function() {
    				return ["div",
    					["strong", name],
    					["em", age],
    				];
    			}
    		}
    	}
    
    	this.renderTo = function(el) {
    		domvm(PersonView).mount(el);
    	};
    
    	this.setAge = function(newAge) {
    		age = newAge;
    		vm0.redraw();
    	};
    }
    
    var jack = new Person("Jack", 33);
    
    jack.renderTo(document.body);
    
    jack.setAge(42);
Clone this wiki locally