Skip to content

Instantly share code, notes, and snippets.

@sionjlewis
Last active August 29, 2015 14:22
Show Gist options
  • Save sionjlewis/5f4869eed45ede27a3c5 to your computer and use it in GitHub Desktop.
Save sionjlewis/5f4869eed45ede27a3c5 to your computer and use it in GitHub Desktop.
Cascading DropDown List Controls V3

Cascading DropDown List Controls V3 ('-' * 35) Uses Knockout JS to populate and control a pair for cascading drop-down lists. This has been crafted with SharePoint in mind as the data source(s). This example disables the 2nd drop-down list if the first control has not been selected.

A Pen by Siôn J. Lewis on CodePen.

License.

<html>
<head>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.1.0/knockout-min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>
</head>
<body>
<div class="formSection">
<div class="ajaxLoader">Data Loading...</div>
<div class="column divideBy1">
<div class="label">
<h1>Cascading DropDown List Controls V3</h1>
</div>
</div>
<div class="clear"></div>
<div class="column divideBy2">
<div class="label divideBy3">
<label id="lblEntity" class="" for="ddlEntity">Entity: </label>
</div>
<div class="control divideBy2Thirds">
<select id="ddlEntity" class="" title="Entity" tabindex="1" data-bind="options: optEntities, optionsText: 'entity', optionsValue: 'id', optionsCaption: 'Select...', value: selectedEntity"></select>
</div>
</div>
<div class="column divideBy2">
<div class="label divideBy3">
<label id="lblSubEntity" class="" for="ddlSubEntity">Sub Entity: </label>
</div>
<div class="control divideBy2Thirds">
<select id="ddlSubEntity" class="" title="Sub Entity" tabindex="2" data-bind="options: optSubEntities, optionsText: 'subEntity', optionsValue: 'id', optionsCaption: 'Select...', value: selectedSubEntity, enable: isEnabledOptSubEntity"></select>
</div>
</div>
<div class="clear"></div>
<div class="column divideBy1">
<div class="label">
<p><i>To enable or disable the predefined selected values for the drop-down lists, search for the code in-between the "Note" comments and comment in or out as you see fit...</i></p>
</div>
</div>
<div class="clear"></div>
</div>
</body>
</html>
if (SJL === undefined || typeof (SJL) !== 'object') { var SJL = new Object(); }
SJL = function () {
var self = {};
return{
onPageLoad: function () {
SJL.ViewModel.applyViewModelBinding();
}
}
}();
SJL.ViewModel = function () {
var self = {};
self.vmMain = {
optEntities: ko.observableArray([]),
optSubEntities: ko.observableArray([]),
selectedEntity: ko.observable(),
selectedSubEntity: ko.observable(),
isEnabledOptSubEntity: ko.observable(false)
}
self.loadAsync_optEntity = function (isActive) {
var dfd = new jQuery.Deferred();
try {
SJL.Repository.get_optEntity(
function () {
$('.ajaxLoader').show();
},
function (items) {
var optEntities = [];
$.each(items, function (index, element) {
optEntities.push(new SJL.Model.optEntity(element.id, element.entity, element.active, element.order));
});
self.vmMain.optEntities(optEntities);
dfd.resolve('Options: "Entity" loaded.');
},
isActive
);
return dfd.promise();
} catch (ex) {
alert('Error: loadAsync_optEntity() ' + ex);
return dfd.fail();
}
}
self.loadAsync_optSubEntity = function (isActive, entityKey) {
var dfd = new jQuery.Deferred();
try {
SJL.Repository.get_optSubEntity(
function () {
$('.ajaxLoader').show();
},
function (items) {
var optSubEntities = [];
$.each(items, function (index, element) {
optSubEntities.push(new SJL.Model.optSubEntity(element.id, element.subEntity, element.entity, element.active, element.order, element.entityKey, element.entityValue));
});
self.vmMain.optSubEntities(optSubEntities);
dfd.resolve('Options: "Sub-Entity" loaded.');
},
isActive,
entityKey
);
return dfd.promise();
} catch (ex) {
alert('Error: loadAsync_optSubEntity() ' + ex);
return dfd.fail();
}
}
self.set_enabledOptSubEntity = function () {
var x = self.vmMain.selectedEntity();
if (x !== undefined && x !== null) {
self.vmMain.isEnabledOptSubEntity(true);
} else {
self.vmMain.isEnabledOptSubEntity(false);
}
}
self.apply_koSubscriptionsFuncs = function () {
self.vmMain.selectedEntity.subscribe(function (newVal) {
if (newVal !== undefined && newVal !== null) {
$.when(self.loadAsync_optSubEntity(true, newVal)).done(function () {
self.set_enabledOptSubEntity();
$('.ajaxLoader').delay(250).fadeOut(250);
});
}
});
}
self.apply_preSelectedValues = function () {
// Note: Just for demo purposes...
self.vmMain.selectedEntity(2);
// Note: End.
var selectedEntity = self.vmMain.selectedEntity();
if (selectedEntity !== undefined && selectedEntity !== null) {
self.loadAsync_optSubEntity(true, selectedEntity);
// Note: Just for demo purposes...
self.vmMain.selectedSubEntity = 6;
// Note: End.
}
}
return{
applyViewModelBinding: function () {
$('.ajaxLoader').show();
$.when(self.loadAsync_optEntity(true)).done(function () {
self.apply_koSubscriptionsFuncs();
//self.apply_koComputedFuncs();
self.apply_preSelectedValues();
self.set_enabledOptSubEntity();
ko.applyBindings(self.vmMain);
$('.ajaxLoader').delay(250).fadeOut(250);
});
//alert('The code has run.');
}
}
}();
SJL.Model = function () {
return {
optEntity: function (id, entity, active, order) {
this.id = id;
this.entity = entity;
this.action = active;
this.order = order;
},
optSubEntity: function (id, subEntity, entity, active, order, entityKey, entityValue) {
this.id = id;
this.subEntity = subEntity;
this.entity = entity;
this.action = active;
this.order = order;
this.entityKey = entityKey;
this.entityValue = entityValue;
},
optEntitiesWithSubEntities: function (id, entity, active, order, optSubEntities) {
this.id = id;
this.entity = entity;
this.action = active;
this.order = order;
this.optSubEntities = optSubEntities;
}
}
}();
SJL.Repository = function () {
var self = {};
self.get_optEntity = function (beforeComplete, onComplete, isActive) {
// So let's pretend to get data from SharePoint...
fakeWebServiceCall = function () {
var itemsArray = [];
if (isActive === true) {
itemsArray.push(new SJL.Model.optEntity(1, 'Ireland', true, 1));
itemsArray.push(new SJL.Model.optEntity(2, 'New Zealand', true, 3));
itemsArray.push(new SJL.Model.optEntity(3, 'Spain', true, 4));
itemsArray.push(new SJL.Model.optEntity(4, 'Great Britain', true, 2));
}
if (onComplete && typeof (onComplete) === 'function') {
onComplete(itemsArray);
} else {
CC.Utilities.logWarning('get_optEntity()', 'callback function has not been passed.');
}
}
if (beforeComplete && typeof (beforeComplete) === 'function') {
beforeComplete();
}
fakeWebServiceCall();
}
self.get_optSubEntity = function (beforeComplete, onComplete, isActive, entityKey) {
// So let's pretend to get data from SharePoint...
fakeWebServiceCall = function () {
var itemsArray = [];
if (isActive === true) {
if (entityKey === 1) {
itemsArray.push(new SJL.Model.optSubEntity(1, 'Bantry', true, 1, 1, 'Ireland'));
itemsArray.push(new SJL.Model.optSubEntity(2, 'Cork', true, 2, 1, 'Ireland'));
itemsArray.push(new SJL.Model.optSubEntity(3, 'Dublin', true, 3, 1, 'Ireland'));
} else if (entityKey === 2) {
itemsArray.push(new SJL.Model.optSubEntity(4, 'Auckland', true, 1, 2, 'New Zealand'));
itemsArray.push(new SJL.Model.optSubEntity(5, 'Hamilton', true, 2, 2, 'New Zealand'));
itemsArray.push(new SJL.Model.optSubEntity(6, 'Palmerston North', true, 3, 2, 'New Zealand'));
itemsArray.push(new SJL.Model.optSubEntity(7, 'Wellington', true, 4, 2, 'New Zealand'));
itemsArray.push(new SJL.Model.optSubEntity(8, 'Littleton', true, 5, 2, 'New Zealand'));
} else if (entityKey === 3) {
itemsArray.push(new SJL.Model.optSubEntity(9, 'Madrid', true, 3, 3, 'Spain'));
itemsArray.push(new SJL.Model.optSubEntity(10, 'Barcelona', true, 1, 3, 'Spain'));
itemsArray.push(new SJL.Model.optSubEntity(11, 'Valencia', true, 5, 3, 'Spain'));
itemsArray.push(new SJL.Model.optSubEntity(12, 'Seville', true, 4, 3, 'Spain'));
itemsArray.push(new SJL.Model.optSubEntity(13, 'Bilbao', true, 2, 3, 'Spain'));
} else if (entityKey === 4) {
itemsArray.push(new SJL.Model.optSubEntity(14, 'Bath', true, 1, 4, 'Great Britain'));
itemsArray.push(new SJL.Model.optSubEntity(15, 'Cardiff', true, 2, 4, 'Great Britain'));
itemsArray.push(new SJL.Model.optSubEntity(16, 'Edinburgh', true, 3, 4, 'Great Britain'));
itemsArray.push(new SJL.Model.optSubEntity(17, 'Hereford', true, 4, 4, 'Great Britain'));
itemsArray.push(new SJL.Model.optSubEntity(18, 'Liverpool', true, 5, 4, 'Great Britain'));
itemsArray.push(new SJL.Model.optSubEntity(19, 'London', true, 6, 4, 'Great Britain'));
itemsArray.push(new SJL.Model.optSubEntity(20, 'St David\'s', true, 7, 4, 'Great Britain'));
}
}
if (onComplete && typeof (onComplete) === 'function') {
onComplete(itemsArray);
} else {
CC.Utilities.logWarning('get_optSubEntity()', 'callback function has not been passed.');
}
}
if (beforeComplete && typeof (beforeComplete) === 'function') {
beforeComplete();
}
fakeWebServiceCall();
}
return {
get_optEntity: function (beforeComplete, onComplete, isActive) {
return self.get_optEntity(beforeComplete, onComplete, isActive);
},
get_optSubEntity: function (beforeComplete, onComplete, isActive, entityKey) {
return self.get_optSubEntity(beforeComplete, onComplete, isActive, entityKey);
}
}
}();
SJL.Utilises = function () {
self.isEmpty = function (fieldValue, rtnTrue, rtnFalse) {
if (rtnTrue === undefined || rtnTrue === null) {
rtnTrue = "populated";
}
if (rtnFalse === undefined || rtnFalse === null) {
rtnFalse = "empty";
}
if (fieldValue === undefined) {
return rtnFalse;
} else {
var l = fieldValue.toString().length;
return (l > 0) ? rtnTrue : rtnFalse;
}
}
return {
isEmpty: function (fieldValue, rtnTrue, rtnFalse) {
return self.isEmpty(fieldValue, rtnTrue, rtnFalse);
}
}
}();
$(document).ready(function () {
SJL.onPageLoad();
});
.formSection {
clear:both;
display:block;
font-family: Arial, Helvetica, sans-serif;
}
.formSection h1 {
font-family: Arial, Helvetica, sans-serif;
font-size:24px;
}
.formSection .column {
display:block;
float:left;
}
.formSection .divideBy1 {
width:100%;
}
.formSection .divideBy3Quarters {
width:75%;
}
.formSection .divideBy2Thirds {
width:66.6%;
}
.formSection .divideBy2 {
width:50%;
}
.formSection .divideBy3 {
width:33.3%;
}
.formSection .divideBy4 {
width:25%;
}
.formSection .label {
display:block;
float:left;
}
.formSection .control {
display:block;
float:left;
}
.formSection .ajaxLoader {
display:none;
border:0px none #fff;
position:fixed;
left:50%;
top:50%;
z-index:999999;
height:22px;
margin-top:-24px;
width:130px;
margin-left:-24px;
/*background-image: url('/Style%20Library/_Branding/IMG/AjaxLoader.gif');*/
background-repeat: no-repeat;
background-position:50% 50%;
background-color:red;
color:white;
padding:5px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment