Lazy loading an observable in KnockoutJS
by kougiland
HTML
<script src="http://rniemeyer.github.com/KnockMeOut/Scripts/jquery.tmpl.js"></script>
<script src="http://knockoutjs.com/downloads/knockout-2.2.1.debug.js"></script>
<ul data-bind="template: { name: 'tabsTmpl', foreach: tabs }"></ul>
<hr />
<div data-bind="template: { name: 'detailsTmpl', data: selectedTab }"></div>
<script id="tabsTmpl" type="text/html">
<li data-bind="css: { selected: viewModel.selectedTab() === $data }">
<a href="#" data-bind="text: name, click: function() { viewModel.showDetails($data) }"></a>
</li>
</script>
<script id="detailsTmpl" type="text/html">
<p>
<img src="http://rniemeyer.github.com/KnockMeOut/Images/loading.gif" data-bind="visible: !details.loaded()" />
<a href="#" data-bind="visible: details.loaded, click: details.refresh"><img src="http://rniemeyer.github.com/KnockMeOut/Images/refresh.png" /></a>
<span data-bind="text: details, css: { stale: !details.loaded() }"></span>
</p>
</script>
CSS
li { display: inline; margin: 5px; }
img { margin: 2px; }
.stale { color: #666; }
.selected { font-weight: bold; }
JavaScript
var fakeText = " - lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ut tempus lorem. Etiam laoreet volutpat viverra. Vestibulum non nunc eu justo adipiscing rhoncus et eget leo. Ut tincidunt, orci a tristique aliquet, ipsum leo rutrum nisi, ac scelerisque leo magna quis justo. In quis lacus a tortor lacinia euismod et ut lectus. Quisque suscipit iaculis lacus. Pellentesque varius volutpat lacus, ac semper arcu porttitor sit amet. In feugiat pharetra laoreet. Nam condimentum gravida suscipit. Pellentesque ac lectus nec elit aliquam lobortis in sit amet massa.";
//an observable that retrieves its value when first bound
ko.onDemandObservable = function(callback, target) {
var _value = ko.observable(); //private observable
var result = ko.dependentObservable({
read: function() {
//if it has not been loaded, execute the supplied function
if (!result.loaded()) {
callback.call(target);
}
//always return the current value
return _value();
},
write: function(newValue) {
//indicate that the value is now loaded and set it
result.loaded(true);
_value(newValue);
},
deferEvaluation: true //do not evaluate immediately when created
});
//expose the current state, which can be bound against
result.loaded = ko.observable();
//load it again
result.refresh = function() {
result.loaded(false);
};
return result;
};
function Tab(id, name) {
this.id = id;
this.name = ko.observable(name);
//sample of using an on-demand observable here
this.details = ko.onDemandObservable(this.getDetails, this);
}
Tab.prototype.getDetails = function() {
$.ajax({
type: 'POST',
url: '/echo/json/',
data: {
json: ko.toJSON({
details: new Date().toLocaleTimeString() + " " + this.name() + fakeText
}),
...