Automatically Select All Parent Taxa When Selecting a Taxon in Sitefinity's HierarchicalTaxonField

Automatically Select All Parent Taxa When Selecting a Taxon in Sitefinity's HierarchicalTaxonField

Posted on November 22, 2013 0 Comments

The content you're reading is getting on in years
This post is on the older side and its content may be out of date.
Be sure to visit our blogs homepage for our latest news, updates and information.

I'd like to use the occasion to thank once again to all of you who constantly provide us with the great constructive feedback we need to come up with ideas for a good blog post article. Something that you really feel strongly about, and would like to see accomplished with Sitefinity CMS.

The current blog post is just another example of how our support channel helps us gather ideas of what you'd like to see in our developer blogs.

The use case scenario we started working on this time was the following: As a user I'd like to be able to filter my content items by a certain Hierarchical taxonomy. Currently when I apply filtering by a particular hierarchical taxon Sitefinity would return only the times that have been marked with it. What I'd like to achieve is to get all items marked with this taxon and its parent taxa.

Now, to the point - as some of you know, in Sitefinity we use the HierarchicalTaxonField to provide a convenient UI for selecting the desired hierarchical taxa format review structure. We then store the selected taxa as a List of Guids - a straightforward approach that also allows us to achieve decent performance when filtering by certain taxon. However the current problem at hand is that if we were to implement hierarchical filtering, based on the selected single taxon (or multiple taxa) this would involve a certain performance drawback.

Instead of doing that, the current blog post will demonstrate how we can address the request on the other end - we'll show you how easy it is to make your HierarchicalTaxoField automatically select all parent nodes of the selected taxon, so you'll end up with an entry in the database indicating that this item has been marked with the desired taxon, and all its parents - something that would make the hierarchical filtering much easier, because if you filter your items by a parent taxonomy of the selected taxon the content item will be included in the returned results, as it will have the parent automatically persisted in its List of selected taxa IDs.

The easiest way to achieve this functionality would be to take advantage of Sitefinity's ability to dynamically map an external template for a Sitefinity control.

If we look at the HierarchcialTaxonSelector (this is the control used in our HierarchicalTaxonField to provide the UI for hierarchical taxa representation and handling the user selection) template we'll notice that it actually uses another product of ours, namely the RadTreeView for displaying the taxa in a tree-like structure and enabling the check box selection functionality:

<%@ Register TagPrefix="telerik" Assembly="Telerik.Web.UI" Namespace="Telerik.Web.UI" %>
<%@ Register Assembly="Telerik.Sitefinity" Namespace="Telerik.Sitefinity.Web.UI" TagPrefix="sitefinity" %>
 
<sitefinity:ResourceLinks id="resourcesLinks" runat="server">
    <sitefinity:ResourceFile Name="Styles/Treeview.css" />
    <sitefinity:ResourceFile Name="Styles/Window.css" />
</sitefinity:ResourceLinks>
 
<div class="sfChooseList">
    <asp:Label ID="selectorTitle" runat="server"></asp:Label>
 
    <ul id="rootSelector" runat="server" class="sfRadioList">
        <li>
            <asp:RadioButton ID="rootRadio" runat="server" Checked="true" GroupName="rootSelector" />
            <asp:Label ID="rootRadioLabel" runat="server" AssociatedControlID="rootRadio"></asp:Label>
        </li>
        <li>
            <asp:RadioButton ID="taxaRadio" runat="server" GroupName="rootSelector" />
            <asp:Label ID="taxaRadioLabel" runat="server" AssociatedControlID="taxaRadio"></asp:Label>
        </li>
    </ul>
    <div id="treePanel" runat="server">
        <telerik:RadTreeView id="taxaTree" runat="server"
            ShowLineImages="false"
            CheckBoxes="true"
            Skin="Sitefinity"
            ExpandAnimation-Type="None"
            CollapseAnimation-Type="None" />
        <sitefinity:RadTreeBinder
            id="taxaTreeBinder"
            runat="server"
            ServiceUrl="~/Sitefinity/Services/Taxonomies/HierarchicalTaxon.svc/{0}/"
            ServiceChildItemsBaseUrl="~/Sitefinity/Services/Taxonomies/HierarchicalTaxon.svc/subtaxa/"
            ServicePredecessorBaseUrl="~/Sitefinity/Services/Taxonomies/HierarchicalTaxon.svc/predecessor/"
            TargetId="taxaTree"
            ParentDataKeyName="ParentTaxonId"
            DataKeyNames="Id"
            DataMembers="Title"
            BindOnLoad="false">
            <containers>
                <sitefinity:BinderContainer runat="server" RenderContainer="false">
                   <span>{{ Title }}</span>
                   <span class="sf_binderLocalization_showIfLanguageUnavailable">({$LocalizationResources, NotTranslated$})</span>
                </sitefinity:BinderContainer>
            </containers>
        </sitefinity:RadTreeBinder>
    </div>
    <asp:LinkButton ID="createTaxonButton" runat="server" OnClientClick="return false;" CssClass="sfCreateTaxonBtn"></asp:LinkButton>
    <asp:LinkButton ID="doneButton" runat="server" OnClientClick="return false;" CssClass="sfLinkBtn sfChange">
        <strong class="sfLinkBtnIn">
            <asp:Literal runat="server" ID="Literal1" Text="<%$Resources:Labels, Done %>" /></strong>
    </asp:LinkButton>
</div>
 
<telerik:RadWindow id="newTaxonDialog" runat="server"
    Skin="Sitefinity"
    Behaviors="Close"
    AutoSizeBehaviors="Width,Height"
    VisibleTitlebar="true"
    VisibleStatusbar="false"
    Modal="true"
    Width="395"
    ShowContentDuringLoad="false"
    Height="250">
</telerik:RadWindow>
 
<script type="text/javascript">
    var updateClientState = Telerik.Web.UI.RadWebControl.prototype.updateClientState;
    Telerik.Web.UI.RadTreeView.prototype.updateClientState = function () {
        updateClientState.apply(this, arguments);
        var clientStateField = $get(this.get_clientStateFieldID());
        clientStateField.value = clientStateField.value.replace(/</ig, "<");
    }
</script>

As with all other Telerik controls for ASP.NET AJAX the TreeView has an awesome client-side API that allows us to achieve our desired functionality in just a couple of lines of JavaScript.

1. We are going to subscribe to the OnClientNodeChecked event of the TreeView control, as this would allow us to plug our logic when a user checks a particular taxon in the TreeView:

<telerik:RadTreeView id="taxaTree" runat="server"
            OnClientNodeChecked="clientNodeChecked"
            ShowLineImages="false"
            CheckBoxes="true"
            Skin="Sitefinity"
            ExpandAnimation-Type="None"
            CollapseAnimation-Type="None" />

2. Then we can get the checked node from the event arguments and traverse up the tree while we reach the root element, setting each node to checked = true on our way up:

<script type="text/javascript">
        function clientNodeChecked(sender, eventArgs) {
            var node = eventArgs.get_node();
            if (node.get_checked()) {
                while (node.get_parent().set_checked != null) {
                    node.get_parent().set_checked(true);
                    node = node.get_parent();
                }
            }
        }
    </script>

3. The last step would be to just save the template and map it for the Telerik.Sitefinity.Taxonomies.Web.UI.Hierarchical.HierarchicalTaxonSelector host type using the ViewMap approach, which will make Sitefinity use our new template in all instances of the control throughout the system. 

For those of you who are not familiar with this approach, all you need to do is:

3.1 Save the modified template in a folder inside your project
3.2 Go to Sitefinity's backend -> Administration->Settings -> Advanced -> Controls-> ViewMap and click on the Add new button
3.3 For HostType enter 
Telerik.Sitefinity.Taxonomies.Web.UI.Hierarchical.HierarchicalTaxonSelector
and for TemplatePath enter the relative path to your template
Click on Save and enjoy the new functionality (please note that Sitefinity caches the configuration entries for performance optimization purposes. Sometimes you might need to recycle your site's  Application Pool in order for the changes to take effect)

You can find a demonstrative video showing the before and after behavior of the HierarchicalTaxonField, as well as adding the entry in the ViewMap here.

You can also download the modified template form here: HierarchicalTaxonSelectorCustom
IMG_0765

Boyan Barnev

Boyan Barnev is a Principal Information Developer at Progress. His mission is to demonstrate the unlimited capabilities of Sitefinity CMS via product documentation, SDK samples, and technical blog posts. He has graduated from the American University in Bulgaria and joined Telerik in 2011. Since then Boyan has held various positions in the company, leading the strategy and operation of the Sitefinity CMS Technical support service worldwide.

Comments

Comments are disabled in preview mode.
Topics

Sitefinity Training and Certification Now Available.

Let our experts teach you how to use Sitefinity's best-in-class features to deliver compelling digital experiences.

Learn More
Latest Stories
in Your Inbox

Subscribe to get all the news, info and tutorials you need to build better business apps and sites

Loading animation