Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Lazy Model implementation in Knockout (inspired by ngModel)
<!DOCTYPE html>
<html>
<head>
<meta name="description" content="Lazy Model" />
<script src="http://knockoutjs.com/downloads/knockout-3.0.0.js"></script>
<script src="http://code.jquery.com/jquery.min.js"></script>
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.1/css/font-awesome.css" rel="stylesheet">
<link href="http://getbootstrap.com/2.3.2/assets/css/bootstrap.css" rel="stylesheet" type="text/css" />
<link href="http://getbootstrap.com/2.3.2/assets/css/bootstrap-responsive.css" rel="stylesheet" type="text/css" />
<script src="http://getbootstrap.com/2.3.2/assets/js/bootstrap.js"></script>
<meta charset=utf-8 />
<title>Lazy Model</title>
</head>
<body>
<div class="container">
<form data-bind="submit: onSubmit">
<!-- ko model: {value:'name',target:model} -->
<label for="name">Name</label>
<input required type="text" data-bind="value:model.name, valueUpdate: 'keydown'"/>
<!-- /ko -->
<!-- ko model: {value:'dob',target:model} -->
<label for="dob">Date of Birth </label>
<input required type="date" data-bind="value:model.dob, valueUpdate: 'keydown'" />
<!-- /ko -->
<!-- ko model:{value:'address.city',target:model} -->
<label for="city">City </label>
<input required type="text" id="city" data-bind="value:model.address.city, valueUpdate: 'keydown'"/>
<!-- /ko -->
<!-- ko model:{value:'address.zip',target:model} -->
<label for="zip">Zip Code</label>
<input type="text" id="zip" data-bind="value:model.address.zip, valueUpdate: 'keydown'"/>
<!-- /ko -->
<p><button type="submit" class="btn">Submit</button></p>
<pre data-bind="text:ko.toJSON(model)"></pre>
</form>
</div>
</body>
</html>
ko.bindingHandlers.model = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var model = valueAccessor().target,
value = valueAccessor().value;
//Get the value of an object using property path syntax
function getValue(namespace, parent) {
var parts = namespace.split('.'),
current = parent;
for (var i = 0; i < parts.length; i += 1) {
if (current[parts[i]]) {
current = current[parts[i]];
} else {
if (i >= parts.length - 1)
return undefined;
}
}
return current;
}
//function to create an arbitrarily nested object with property path syntax
function deepen(o) {
var oo = {}, parts, t, k, key;
t = oo;
parts = o.split('.');
key = parts.pop();
while (parts.length) {
part = parts.shift();
t = t[part] = t[part] || {};
}
t[key] = ko.observable(o[k]);
return oo;
}
//merge two objects recursively without breaking nested objects
function MergeRecursive(obj1, obj2) {
for (var p in obj2) {
try {
// Property in destination object set; update its value.
if (obj2[p].constructor == Object) {
obj1[p] = MergeRecursive(obj1[p], obj2[p]);
} else {
obj1[p] = obj2[p];
}
} catch (e) {
// Property in destination object not set; create it and set its value.
obj1[p] = obj2[p];
}
}
return obj1;
}
//check if the property doesn't exist
if (!getValue(value, model)) {
//merge the values
MergeRecursive(model, deepen(value));
}
}
}
ko.virtualElements.allowedBindings.model = true;
var vm = function () {
this.model = {};
this.onSubmit = function () {
console.log(ko.toJSON(this.model));
};
}
ko.applyBindings(new vm());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment