Continent-Country-Region drop down list as dojo property
I have looked at different ways of implementing a dropdown list with dependencies. Have looke at Doong Nguyen’s solution but had problems making it also work with continent.
First of all I made a ContinentCountryRegionSelectionFactory to get all items needed.
class DependedItem : ISelectItem
{
public string Parent { get; set; }
public object Value { get; set; }
public int Level { get; set; }
public string Text { get; set; }
public string Name { get { return Text; } }
}
Then I populated it as follows using the GeoLoation store in episerver to get the different kind of options and created first a class to store the values
public class ContinentCountryRegionSelectionFactory : ISelectionFactory
{
public IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata)
{
var _geolocationProviderBase = ServiceLocator.Current.GetInstance<GeolocationProviderBase>();
var _localizationService = ServiceLocator.Current.GetInstance<LocalizationService>();
var list = new List<ISelectItem>();
foreach (var continentKey in _geolocationProviderBase.GetContinentCodes())
{
var continent = new DependedItem()
{
Value = continentKey,
Parent = "",
Level=0,
Text = _localizationService.GetString("/shell/cms/visitorgroups/criteria/geographiclocation/continents/" + continentKey)
};
list.Add(continent);
foreach (var countryKey in _geolocationProviderBase.GetCountryCodes(continentKey))
{
var country = new DependedItem()
{
Value = continentKey+"-"+ countryKey,
Parent= continentKey,
Level=1,
Text = _localizationService.GetString("/shell/cms/visitorgroups/criteria/geographiclocation/countries/" + countryKey)
};
list.Add(country);
foreach (var regionKey in _geolocationProviderBase.GetRegions(countryKey))
{
var region = new DependedItem()
{
Value = continentKey + "-" + countryKey+"-"+ regionKey,
Parent= continentKey + "-" + countryKey,
Level=2,
Text = regionKey
};
list.Add(region);
}
}
}
return list;
}
}
After I have done this the scary part remains. First I add a file ClientResources/scripts/editors/SelectAreaEditor.js Then made a dojo property where one text box is the value of the property. And 3 other FilteringSelect dropdowns are Continent/Country/Region
After one select for instance a continent I update the store for country and updates the value and display the country area.
/*
Dojo widget for editing a list of strings. Also see property type PropertyStringList in /Models/Properties.
*/
define([
"dojo/_base/array",
"dojo/_base/connect",
"dojo/_base/declare",
"dojo/_base/lang",
"dijit/_CssStateMixin",
"dijit/_Widget",
"dijit/_TemplatedMixin",
"dijit/_WidgetsInTemplateMixin",
"dijit/form/TextBox",
"epi/epi",
"epi/shell/widget/_ValueRequiredMixin",
"dojo/store/Memory",
"dijit/form/FilteringSelect"
],
function (
array,
connect,
declare,
lang,
_CssStateMixin,
_Widget,
_TemplatedMixin,
_WidgetsInTemplateMixin,
Textarea,
epi,
_ValueRequiredMixin,
Memory,
FilteringSelect
) {
return declare([_Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _CssStateMixin, _ValueRequiredMixin], {
templateString: "<div class=\"dijitInline\">\
<div data-dojo-attach-point=\"stateNode, tooltipNode\" >\
<div data-dojo-attach-point=\"textBox\" type=\"hidden\" data-dojo-type=\"dijit.form.TextBox\"></div>\
</div>\
<div data-dojo-attach-point=\"inputLevel1\" data-dojo-type=\"dijit.form.FilteringSelect\" style=\"min-width: 300px;\">\
</div>\
<div data-dojo-attach-point=\"area2\" style=\"display:block;padding-left:30px;display:none;\">\
<div data-dojo-attach-point=\"inputLevel2\" data-dojo-type=\"dijit.form.FilteringSelect\" style=\"min-width: 300px;\">\
</div>\
<div data-dojo-attach-point=\"area3\" style=\"display:block;padding-left:30px;display:none;\">\
<div data-dojo-attach-point=\"inputLevel3\" data-dojo-type=\"dijit.form.FilteringSelect\" style=\"min-width: 300px\">\
</div>\
</div>\
</div>\
</div>",
intermediateChanges: false,
value: null,
allItemsStore: null,
selected: null,
onChange: function (value) {
// Event
},
postCreate: function () {
// call base implementation
this.inherited(arguments);
// Init textarea and bind event
this.textBox.set("intermediateChanges", this.intermediateChanges);
this.connect(this.textBox, "onChange", this._onTextBoxChanged);
this._setUpStores(1);
this.selected = this._findSelected("");
this.connect(this.inputLevel1, "onChange", this._onLevelBoxChanged1);
this.connect(this.inputLevel2, "onChange", this._onLevelBoxChanged2);
this.connect(this.inputLevel3, "onChange", this._onLevelBoxChanged3);
},
postMixInProperties: function () {
this.inherited(arguments);
var test = this.selections;
this.allItemsStore = new Memory({ idProperty: "value", IsType: "Continent", data: this.selections });
},
isValid: function () {
return !this.required || (this.value.length > 0 && this.value!= "");
},
// Setter for value property
_setValueAttr: function (value) {
this._setValue(value, true,true);
},
_setReadOnlyAttr: function (value) {
this._set("readOnly", value);
this.textBox.set("readOnly", value);
},
_setIntermediateChangesAttr: function (value) {
this.textBox.set("intermediateChanges", value);
this._set("intermediateChanges", value);
},
_onTextBoxChanged: function (value) {
this._setValue(value, false,false);
},
_onLevelBoxChanged1: function (value) {
var me = this._findSelected(value);
//this.selected = this._findSelected(value);
if (this.selected.level1 != me.level1) {
this.selected = me;
this._setUpStores(2);
this._setValuesInDropDowns();
this._setValue(value, true, false);
} else {
//this._setValue(value, true, false);
}
},
_onLevelBoxChanged2: function (value) {
var me = this._findSelected(value);
if (this.selected.level2!=me.level2)
{
this.selected = me;
this._setUpStores(3);
this._setValuesInDropDowns();
this._setValue(value, true, false);
}
//this._setUpStores();
//this._setValue(value, true,false);
},
_onLevelBoxChanged3: function (value) {
var me = this._findSelected(value);
if (this.selected.level3 != me.level3) {
this.selected = me;
//this._setUpStores(3);
this._setValuesInDropDowns();
this._setValue(value, true, false);
}
},
_findSelected: function (value) {
var result = { level1: "", level2: "", level3: "" };
var item = this.allItemsStore.query({ value: value });
if (item.length != 0) {
if (item[0].level == 2) {
result.level3 = item[0].value;
item = this.allItemsStore.query({ value: item[0].parent });
}
if (item[0].level == 1) {
result.level2 = item[0].value;
item = this.allItemsStore.query({ value: item[0].parent });
}
if (item[0].level == 0) {
result.level1 = item[0].value;
}
}
return result;
},
_setValuesInDropDowns: function () {
this.inputLevel1.set("value", this.selected.level1);
this.inputLevel2.set("value", this.selected.level2);
this.inputLevel3.set("value", this.selected.level3);
this.area2.style.display=(this.selected.level1!="")?"block":"none";
this.area3.style.display=(this.selected.level2!="")?"block":"none";
},
_setUpStores: function (level) {
if (level == 1 || level == 0) {
var level1 = this.allItemsStore.query({ level: 0 });
var level1Store = new Memory({ idProperty: "value", data: level1 });
this.inputLevel1.set("store", level1Store);
}
if ((level == 2 || level == 0) && this.selected.level1 != "") {
var level2 = this.allItemsStore.query({ level: 1, parent: this.selected.level1});
var level2Store = new Memory({ idProperty: "value", data: level2 });
this.inputLevel2.set("store", level2Store);
}
if ((level == 3 || level == 0) && this.selected.level2 != "") {
var level3 = this.allItemsStore.query({ level: 2, parent: this.selected.level2 });
var level3Store = new Memory({ idProperty: "value", data: level3 });
this.inputLevel3.set("store", level3Store);
}
},
_setValue: function (value, updateTextbox,updateDropDown) {
if (updateDropDown) {
this.selected = this._findSelected(value);
this._setUpStores(0);
this._setValuesInDropDowns();
}
if (this._started && epi.areEqual(this.value, value)) {
return;
}
// set value to this widget (and notify observers)
this._set("value", value);
// set value to textarea
if (updateTextbox) {
this.textBox.set("value", value);
}
if (this._started && this.validate()) {
// Trigger change event
this.onChange(value);
}
}
});
});
The I needed to add the descriptor the the string property
[ClientEditor(ClientEditingClass = "iterasite/editors/SelectAreaEditor", SelectionFactoryType = typeof(ContinentCountryRegionSelectionFactory))]
public virtual string SelectArea { get; set; }
And after done that the result is
This code should work with any data as long as the parent key and level is set. But the value need to be unique accross all values.
Hope this can help someone. It did take some time to figure it out :)
DOJO...ouch!
Well done to tie it together and staying on target :)
Thanks for sharing this, it will be very useful for many people I'm sure!
Hi,
the link to the dojo code doesn't work anymore