Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@cswing
Created March 10, 2012 04:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cswing/2010137 to your computer and use it in GitHub Desktop.
Save cswing/2010137 to your computer and use it in GitHub Desktop.
Dojo: Implementing a ViewModelStore
<!DOCTYPE html>
<!--
Copyright Craig Swing, 2012. All rights reserved.
-->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.7/dojo/resources/dojo.css" />
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.7/dijit/themes/dijit.css" />
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.7/dijit/themes/claro/claro.css" />
<style type="text/css">
body {
padding: 2em;
}
#filter {
padding: 0.5em 0;
}
div.order {
border: solid 1px #CCCCCC;
background: #eeeeee;
width: 800px;
height: 350px;
padding: 1em;
margin-bottom: 1em;
}
</style>
<title>Dojo Store API - ViewModelStore</title>
</head>
<body class="claro">
<div><a target="_blank" href="http://swingingcode.blogspot.com/2012/03/dojo-implementing-viewmodelstore.html">Blog Post</a></div>
<div id="filter">
<div class="dijitInline">Customer:<div id="customerNode"></div></div>
<div class="dijitInline">Status:<div id="statusNode"></div></div>
</div>
<div id="content"></div>
<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.7/dojo/dojo.js.uncompressed.js" data-dojo-config="isDebug: true"></script>
<script type="text/javascript">
require(["dojo/domReady!", "dojo/dom-construct", "dojo/parser", "dojo/store/Memory", "dojo/currency", "dijit/form/ComboBox"], function(dr, domConstruct, p, MemoryStore, curr, ComboBox) {
// The ViewModelStore
dojo.declare("ViewModelStore", [MemoryStore], {
referenceProperty: null,
setData: function(data){
if(data.items){
this.idProperty = data.identifier;
data = this.data = data.items;
}else{
this.data = data;
}
this.index = {};
// when a reference to an item has not been registered,
// a setter function is added and will be called when
// the item is registered.
this._referenceSetters = {};
// loop through the flat list of items
dojo.forEach(this.data, function(itm, idx) {
var itmId = itm[this.idProperty];
this.index[itmId] = idx;
for(var prop in itm) {
this._registerItem(itm, prop);
}
// process the queue of deferred setters for this object.
if (this._referenceSetters[itmId]) {
dojo.forEach(this._referenceSetters[itmId], function(fnSet) {
fnSet(itm);
});
this._referenceSetters[itmId] = null;
}
}, this);
delete this._referenceSetters;
},
_inspectValue: function(val, fnSetter) {
var refProperty = this.referenceProperty || '_reference';
// not a 'referece', so return the value.
if (!val || !dojo.isObject(val) || !val[refProperty]) {
fnSetter(val);
return;
}
var refId = val[refProperty];
var refItm = this.get(refId);
// the referenced item has already been loaded, return it.
if (refItm) {
fnSetter(refItm);
return;
}
// we don't have the reference yet, qo queue a deferred setter that
// will set the value once the item is registered.
if (! this._referenceSetters[refId]) {
this._referenceSetters[refId] = [];
}
this._referenceSetters[refId].push(fnSetter);
},
_registerItem: function(itm, prop) {
var propValue = itm[prop];
if (dojo.isArray(propValue)) {
dojo.forEach(propValue, function(propItm, propIdx) {
this._inspectValue(propValue[propIdx], function(refItm) {
propValue[propIdx] = refItm;
});
}, this);
} else {
this._inspectValue(itm[prop], function(refItm) {
itm[prop] = refItm;
});
}
}
});
// credit where due: names & addresses from http://www.fakenamegenerator.com
var store = new ViewModelStore({data: [
{ id: 'customer::1', _type: 'customer', name: 'Anderson, Jose', address: { _reference: 'address::1' } },
{ id: 'address::1', _type: 'address', address1: '2272 Stratford Drive', address2: 'Honolulu, HI 96814' },
{ id: 'customer::2', _type: 'customer', name: 'Gomez, Marcus', address: { _reference: 'address::2' } },
{ id: 'address::2', _type: 'address', address1: '2718 College Avenue', address2: 'Dayton, OH 45459' },
{ id: 'customer::3', _type: 'customer', name: 'Kelley, Eleanor', address: { _reference: 'address::3' } },
{ id: 'address::3', _type: 'address', address1: '1611 Mandan Road', address2: 'Steelville, MO 65565' },
{ id: 'customer::4', _type: 'customer', name: 'Deen, Martha', address: { _reference: 'address::4' } },
{ id: 'address::4', _type: 'address', address1: '4104 Poe Lane', address2: 'Kansas City, KS 66215' },
{ id: 'customer::5', _type: 'customer', name: 'Marquez, Elaine', address: { _reference: 'address::5' } },
{ id: 'address::5', _type: 'address', address1: '3800 Deans Lane', address2: 'Bedford Village, NY 10506' },
{ id: 'product::1', _type: 'product', name: 'Ball Original Classic Pectin Small Batch', price: 1.07 },
{ id: 'product::2', _type: 'product', name: 'Ball 4-Pk 16 Oz. Wide Mouth Class Canning Jars with Lids', price: 4.48 },
{ id: 'product::3', _type: 'product', name: 'Compost Wizard Universal Composter Base', price: 61.00 },
{ id: 'product::4', _type: 'product', name: 'Exaco 187-Gallon Composter', price: 270.56 },
{ id: 'product::5', _type: 'product', name: 'Behlan Country 10\' x 6\' Outdoor Kennel', price: 299.00 },
{ id: 'product::6', _type: 'product', name: 'PetSafe Wireless Pet Containment System', price: 249.00 },
{ id: 'product::7', _type: 'product', name: 'Red Toolbox Toolbox House Woodworking Kit', price: 15.41 },
{ id: 'product::8', _type: 'product', name: 'Top Choice 2 x 6 x 8 #2 Prime Treated Lumber', price: 4.27 },
{ id: 'orderStatus::1', _type: 'orderStatus', name: 'Pending', displayOrder: 10 },
{ id: 'orderStatus::2', _type: 'orderStatus', name: 'Back Ordered', displayOrder: 30 },
{ id: 'orderStatus::3', _type: 'orderStatus', name: 'Shipped', displayOrder: 20 },
{ id: 'order::1', _type: 'order', orderNumber: 1234, customer: { _reference: 'customer::1' }, orderStatus: { _reference: 'orderStatus::1' },
lineItems:[ { _reference: 'orderLine::1' }, { _reference: 'orderLine::2' } ]},
{ id: 'orderLine::1', _type: 'orderLine', order: { _reference: 'order::1' }, price: 1.07, quantity: 3, product: { _reference: 'product::1' } },
{ id: 'orderLine::2', _type: 'orderLine', order: { _reference: 'order::1' }, price: 4.48, quantity: 1, product: { _reference: 'product::2' } },
{ id: 'order::2', _type: 'order', orderNumber: 1235, customer: { _reference: 'customer::2' }, orderStatus: { _reference: 'orderStatus::1' },
lineItems:[ { _reference: 'orderLine::3' }, { _reference: 'orderLine::4' }]},
{ id: 'orderLine::3', _type: 'orderLine', order: { _reference: 'order::2' }, price: 4.48, quantity: 1, product: { _reference: 'product::2' } },
{ id: 'orderLine::4', _type: 'orderLine', order: { _reference: 'order::2' }, price: 61.00, quantity: 1, product: { _reference: 'product::3' } },
{ id: 'order::3', _type: 'order', orderNumber: 1236, customer: { _reference: 'customer::2' }, orderStatus: { _reference: 'orderStatus::3' },
lineItems:[ { _reference: 'orderLine::5' }, { _reference: 'orderLine::6' } ]},
{ id: 'orderLine::5', _type: 'orderLine', order: { _reference: 'order::3' }, price: 270.56, quantity: 1, product: { _reference: 'product::4' } },
{ id: 'orderLine::6', _type: 'orderLine', order: { _reference: 'order::3' }, price: 299.00, quantity: 2, product: { _reference: 'product::5' } }
]});
var customer = new ComboBox({
store: store,
query: { _type: 'customer' },
labelAttr: 'name'
}, dojo.byId('customerNode'));
customer.startup();
var status = new ComboBox({
store: store,
query: { _type: 'orderStatus' },
labelAttr: 'name'
}, dojo.byId('statusNode'));
status.startup();
var fnDisplayOrders = function() {
var contentNode = dojo.byId("content");
var displayedOrder = false;
domConstruct.empty(contentNode);
var result = store.query({
_type: 'order',
customer: customer.get('item'),
orderStatus: status.get('item'),
});
result.forEach(function(order) {
displayedOrder = true;
var div = dojo.create('div', {}, contentNode);
dojo.addClass(div, 'order');
var header = dojo.create('div', {}, div);
dojo.addClass(header, 'orderHeader');
header.innerHTML = dojo.replace(
'Order #: {orderNumber}<br/>{customer.name}<br/>{customer.address.address1}<br/>{customer.address.address2}',
order );
dojo.create('hr', {}, div);
var table = dojo.create('table', { width: '100%' }, div);
var itemsHeaderRow =
dojo.create('tr', {},
dojo.create('thead', {}, table));
dojo.create('th', { style: 'text-align:left;'}, itemsHeaderRow).innerHTML = 'Product';
dojo.create('th', { style: 'text-align:right;'}, itemsHeaderRow).innerHTML = 'Unit<br/>Price';
dojo.create('th', { style: 'text-align:right;'}, itemsHeaderRow).innerHTML = 'Quantity';
dojo.create('th', { style: 'text-align:right;'}, itemsHeaderRow).innerHTML = 'Extended<br/>Amount';
var items = dojo.create('tbody', {}, table);
var fnCreateRightAlignedCell = function(tr, content) {
var td = dojo.create('td', {}, tr);
dojo.style(td, 'text-align', 'right');
td.innerHTML = content;
};
dojo.forEach(order.lineItems, function(lineItem) {
var tr = dojo.create('tr', {}, items);
dojo.create('td', {}, tr).innerHTML = lineItem.product.name;
fnCreateRightAlignedCell(tr, curr.format(lineItem.price));
fnCreateRightAlignedCell(tr, lineItem.quantity);
fnCreateRightAlignedCell(tr, curr.format(lineItem.price * lineItem.quantity));
});
});
if (displayedOrder == false) {
var div = dojo.create('div', {}, contentNode);
dojo.addClass(div, 'order');
div.innerHTML = "There were no orders to display.";
}
};
dojo.connect(customer, 'onChange', fnDisplayOrders);
dojo.connect(status, 'onChange', fnDisplayOrders);
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment