Jonatan Dahl
Dec 17, 2016
(6 votes)

Checkbox tree Dojo widget


Due to a customer request, to display selectable hierarchical data in an editor, I finally wrote this post.
Unfortunately there are no editors for selectable tree data (except for the category picker which requires a category list property) in Episerver.

More specifically the request was to be able to select multiple units from an organization tree, located in a separate database.
So below is my solution, a checkbox tree dojo editor for hierarchical data...

Since I can´t show their organization tree, below is an example for namespaces:

Image Untitled.png

Editor descriptor

[EditorDescriptorRegistration(TargetType = typeof(IEnumerable<int>), UIHint = Global.UIHint.CheckBoxTreeEditor)]
public class CheckBoxTreeEditorDescriptor : EditorDescriptor
    public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable<Attribute> attributes)
        ClientEditingClass = "mysite/editors/CheckBoxTreeEditor/checkBoxTreeEditor";
        base.ModifyMetadata(metadata, attributes);

Selection factory

The selection factory is responsible for feeding data to the dojo widget. Since the SelectItem class isn´t sufficient we create a new class called TreeNodeSelectItem.
The HasChildren property is implemented to be able to tell of a node has any children. This is optional but you will get a plus sign icon in front of each unexpanded node unless you supply a way to determine if it has any children.

public class TreeNodeSelectItem : SelectItem
    public int Id { get; set; }
    public int? ParentId { get; set; }
    public bool HasChildren { get; set; }

Below you see an example of a selection factory for an organizational unit tree.
The dijit tree can only have one root node and since the unit tree data used in this example has several root nodes
we have to create a dummy root node and attach the unit data root nodes to it.
For this example the HasChildren property is set using a linq query against the retrieved units.

public class UnitTreeSelectionFactory : ISelectionFactory
    public IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata)
        var unitService = ServiceLocator.Current.GetInstance<IUnitService>();
        var items = new List<TreeNodeSelectItem>();
        var units = unitService.GetAll();

        // Dummy root
        items.Add(new TreeNodeSelectItem
            Id = -1,
            HasChildren = units.Any(),
            Value = -1

        foreach (var unit in units)
             var item = new TreeNodeSelectItem
                 Id = unit.Id,
                 ParentId = unit.ParentId != null ? unit.ParentId : items[0].Id,
                 Text = unit.Name,
                 Value = unit.Id,
                 HasChildren = units.Exists(x => x.ParentId == unit.Id)


        return items;

EPiServer property

For storing the unit ids I use a PropertyDefinitionTypePlugin called PropertyIntegerList that stores the ids (IEnumerable<int>) as a string, based on this solution:

 The PropertyList<T> might be a cleaner solution but it is still in beta.

[Display(Name = "Enhet")
[ClientEditor(SelectionFactoryType = typeof(UnitTreeSelectionFactory))]
public virtual IEnumerable<int> UnitIds { get; set; }

Dojo widget template


<div class="dijitInline checkbox-tree-editor-widget">
    <div data-dojo-attach-point="container" id="container">

Dojo widget CSS


.checkbox-tree-editor-widget {
    width: 580px;
.checkbox-tree-editor-widget .dijitTreeContent img {
    display: none;
.checkbox-tree-editor-widget .dijitTreeLabel .tree-checkbox {
    margin: 0 7px 0 5px;

Dojo widget

Below is the empty shell of the dojo widget, the methods are described further down

], function (
) {
    return declare("mysite/editors/CheckBoxTreeEditor/checkBoxTreeEditor", [
            templateString: template,
            value: null,
            postCreate: function () {
                var main = this;
            _refreshDescendantCount: function (container) {
            _getDescendantsCheckCountByItemId: function (itemId) {
            _updateValue: function (value, checked) {
            _setValueAttr: function (value) {
            _refreshTree: function (container) {
            _valueIsSet: function () {
            _valueHasItems: function () {


So to be able to view hierarchical data we use the dijit.Tree. As described in the documentation is consists of the following components: Data store -> Model -> Tree

Data store:

Since the depth of the tree I´m using isn´t that deep I use a Memory store which means that I load all the data at once. It´s possible to lazy load the tree by using another type of store such as “dojo/store/JsonRest”.

var store = new Memory({
     data: this.selections,
     idProperty: "id",
     getChildren: function (object) {
         return this.query({ parentId: });

I set the store data to “this.selections” which is the TreeNodeSelectItems supplied by the SelectionFactory. The “idProperty” specifies which property of the TreeNodeSelectItem that represents the item id. The “getChildren” method is responsible for returning the children of an item.


var model = new ObjectStoreModel({
    store: store,
    query: { id: -1 },
    labelAttr: "text",
    mayHaveChildren: function (item) {
         return item.hasChildren;

We create the model and specify our memory store.
query: Specifies how to find the root node (which in this case is id=-1).
labelAttr: Property that holds the text to display
mayHaveChildren: If this is not specified all nodes will initially be prefixed with a “expand-icon” regardless of whether they have children or not.


var tree = new Tree({
    model: model,
    persist: false,
    showRoot: false,
    _createTreeNode: function (args) {
        var tnode = new Tree._TreeNode(args);
        tnode.labelNode.innerHTML = args.label;
        var cb = new CheckBox({
            value: args.item.value,
            class: "tree-checkbox"
        cb.placeAt(tnode.labelNode, "first");

        connect.connect(cb, "onClick", function () {
            var treeNode = dijit.getEnclosingWidget(this.domNode.parentNode);
            main._updateValue(treeNode.item.value, this.checked);
        return tnode;
    onClick: function (item, node, event) {
        if ( != 'checkbox')
            this._onExpandoClick({ node: node });
    onOpen: function (item, node, event) {
        node.labelNode.childNodes[1].nodeValue = node.item.text;
    onClose: function (item, node, event) {
        node.labelNode.childNodes[1].nodeValue = node.item.text;

        if (main._valueHasItems()) {
            var count = main._getDescendantsCheckCountByItemId(;

            if (count > 0)
                node.labelNode.childNodes[1].nodeValue += " (" + count + ")";


We create the tree and specify the model.
_createTreeNode: Adds a CheckBox to each tree node and creates an onClick event for each CheckBox.
onClick: Node click event, open/close the node unless the actual checkbox is clicked
onOpen: Node open event
onClose: Node close event

Finally we add the tree to our container.


Refreshes descendant count for all visible leaf nodes

_refreshDescendantCount: function (container) {
    var main = this;

    if (this._valueHasItems()) {
        dojo.query(".dijitTreeNode", container).forEach(function (node) {
            var nodeItem = dijit.getEnclosingWidget(node);

            if (nodeItem.labelNode.childNodes[1] != null)
                nodeItem.labelNode.childNodes[1].nodeValue = nodeItem.item.text;

            if (nodeItem.isExpandable && !nodeItem.isExpanded) {
                var count = main._getDescendantsCheckCountByItemId(;
                if (count > 0)
                    nodeItem.labelNode.childNodes[1].nodeValue += " (" + count + ")";


Recursive function that returns descendant check count for a specific item

_getDescendantsCheckCountByItemId: function (itemId) {
    var count = 0;

    var children = this.selections.filter(function (x) {
        return x.parentId == itemId;

    for (var i = 0, len = children.length; i < len; i++) {
        if (this.value.indexOf(children[i].id) != -1)

        count += this._getDescendantsCheckCountByItemId(children[i].id);

    return count;


Adds or removes a value from the value property

_updateValue: function (value, checked) {
    var values = this.value;

    if (values == null)
        values = [];

    if (checked == true) {
        if (values.indexOf(value) == -1)
    else {
        var index = values.indexOf(value);

        if (index > -1)
            values.splice(index, 1);

    if (values.length == 0)
        values = null;

    // Sets value and notifies watchers
    this._set('value', values);

    // Notifies EPiServer that the property value has changed


Property setter for the value variable, only called initially and if value is not null

_setValueAttr: function (value) {
    var main = this;
    var values = value;

    if (!lang.isArray(values)) {
        values = null;
    else {
        // Removes values not matching a select item
        values = array.filter(values, function (currentValue) {
        var matchingSelectItems = array.filter(main.selections, function (selectItem) {
            return selectItem.value == currentValue;

        return matchingSelectItems.length > 0;

    if (values.length == 0)
        values = null;

    // Sets value and notifies watchers
    this._set('value', values);



Checks or unchecks visible checkboxes under specified container

_refreshTree: function (container) {
    var main = this;

    if (this._valueIsSet()) {
        dojo.query(".tree-checkbox", container).forEach(function (node) {
            var checkbox = dijit.getEnclosingWidget(node);

            if (array.indexOf(main.value, checkbox.value) != -1) {
                checkbox.set('checked', true);
            else {
                checkbox.set('checked', false);


_valueIsSet: function() {
    return (this.value != null);


_valueHasItems: function() {
    return (this._valueIsSet() && this.value.length > 0);
Dec 17, 2016


Dec 20, 2016 12:53 PM

Nice! Thanks for sharing!

Please login to comment.
Latest blogs
Plug-in manager is back in CMS 12

Plug-in manager is back in the UI, what is it and how can i use it?

Luc Gosso (MVP) | Oct 6, 2022 | Syndicated blog

Display Child Pages in Content Delivery API Response

The below example will implement an instance of IContentConverterProvider to customise the serialisation of PageData and output child pages in the...

Minesh Shah (Netcel) | Oct 4, 2022

Bring the Report Center back in Optimizely CMS 12

The Report Center has been a part of Optimizely CMS since its first debut in version 5R2 in 2008, but in CMS 12, it's removed! Don't despair! Make...

Tomas Hensrud Gulla | Oct 4, 2022 | Syndicated blog

Customizing Property Lists in Optimizely CMS

Generic property lists is a cool editorial feature that has gained a lot of popularity - in spite of still being unsupported (officially). But if y...

Allan Thraen | Oct 2, 2022 | Syndicated blog