Showing the Selected Taxa in Sitefinity Module Items' Backend Grid

Showing the Selected Taxa in Sitefinity Module Items' Backend Grid

Posted on November 03, 2014 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.

In this blog post I will show you how you can resolve the taxa selected in items, in their backend grid. We usually use at least one taxonomy to organize content and selected multiples items, for instance several tags. However, if we would like to show the taxa we have selected for each item in the items grid, we will see that the actual value will be the selected taxa ids. This does not suit our needs, since the ids of the items are not useful for any content editor. I will show you how you can get those ids, make a call to a web service and then display the taxa titles. The blog post requires general developer knowledge, as it concerns JavaScript, WebServices and knowegledge of the Sitefinity server-side API.

 Add a column to the grid

We will add a column to the grid, in which we will then populate the taxa titles for each row. The column insert is done through the Advanced settings. Go to ContentView -> Controls-> Select the module you want to customize -> Views -> BackendList -> View modes -> Grid -> Columns. Create a new column of type DataColumn. Bound it to taxonomy field name. We will get the ids directly from the items in the grid, though, it is good to bind to the according property. Enter the client template, this field is important since using it we will know where to insert the titles later. You can use the following template: <span id="tags">{{Tags}}</span> for tags. The column settings will look like this:

Add Extension script to resolve the taxa items ids

In order to plugin our logic in the grid binding, we will need to use an extension script. We can register one from the Advanced settings -> ContentView -> Controls-> Select the module you want to customize -> Views -> BackendList -> Scripts. Add new script, using your script location and passing the method you want to hook, in our case: OnModuleMasterViewLoaded. Example:

Here the script is used as an Embedded resource.

Getting the taxa ids and calling a service to resolve the items

We will handle the onDataBinding event and will gather all taxa ids corresponding to each item in the grid. This way we can make a single call to the service and obtain them. Then the gathered data is used on itemDataBound, to replace our column values with the titles resolved. Here is the documented JavaScript code:

var masterGridView;
// called by the MasterGridView when it is loaded
function OnModuleMasterViewLoaded(sender, args) {
    // the sender here is MasterGridView
    masterGridView = sender;
    // initialize our extender
    dynamicModuleListViewExtender.init(sender);
 
    var binder = sender.get_binder();
    sender.get_itemsGrid()._binder._itemDataBoundHandler = customItemDataBound;
    binder.add_onDataBinding(handlerOnDataBinding);
};
 
function handlerOnDataBinding(sender, args) {
    var extender = dynamicModuleListViewExtender.get_extender();
    // get items taxa Ids
    extender.getItemsTagsIds(sender, args); // get tags
    extender.getItemsCitiesIds(sender, args);// get cities (custom classification)
}
 
function customItemDataBound(key, dataItem, itemIndex, itemElement) {
    var itemEventArgs = this._getItemEventArgs(key, dataItem, itemIndex, itemElement);
    var h = this.get_events().getHandler('onItemDataBound');
 
    // get taxa models and populate columns data for each item (row)
    var tags = dynamicModuleListViewExtender.get_extender()._tags;
    dynamicModuleListViewExtender.get_extender().setColumnData("tags", tags, itemIndex, itemElement);
 
    var cities = dynamicModuleListViewExtender.get_extender()._cities;
    dynamicModuleListViewExtender.get_extender().setColumnData("cities", cities, itemIndex, itemElement);
 
    // call base handlers
    if (h) h(this, itemEventArgs);
    return itemEventArgs;
}
 
// The extender object, which we will use to get the data
var dynamicModuleListViewExtender = (function () {
    this._extender = null;
    // initialize variables
    var extender = function (sender) {
        this._sender = sender,
        this._itemsTagsIds = [];
        this._tags = [];
        this._itemsCitiesIds = [];
        this._cities = [];
    }
 
    extender.prototype = {
        // get the items tags Ids - called on DataBinding
        getItemsTagsIds: function (sender, args) {
            this._itemsTagsIds = [];
            // all grid items, which will be bound
            var dataItems = args.get_dataItem().Items;
            if (dataItems && dataItems.length > 0) {
                for (var i = 0; i < dataItems.length; i++) {
                    // get the tags Ids for each item from the property
                    var tags = dataItems[i].Tags;
                    // populate the associative array
                    this._itemsTagsIds.push(tags);
                }
            }
 
            if (this._itemsTagsIds) {
                // get the taxa items using the service
                this._tags = this.getTaxaData(this._itemsTagsIds);              
            }
        },
        // get the items cities (custom classification) Ids - called on DataBinding
        getItemsCitiesIds: function (sender, args) {
            this._itemsCitiesIds = [];
            var dataItems = args.get_dataItem().Items;
            if (dataItems && dataItems.length > 0) {
                for (var i = 0; i < dataItems.length; i++) {
                    var cities = dataItems[i].cities;
                    this._itemsCitiesIds.push(cities);
                }
            }
 
            if (this._itemsCitiesIds) {
                this._cities = this.getTaxaData(this._itemsCitiesIds);
            }
        },
        getTaxaData: function (ids) {
            var _self = this;
            // url to our service
            var taxaServiceUrl = "/Sitefinity/Services/TaxaService.svc/";
            // stringify the data to be sent
            var itemsData = JSON.stringify(ids);
            var items = null;
            // post request to get the data
            jQuery.ajax({
                type: "POST",
                async: false,
                url: taxaServiceUrl + "GetTaxaByIds",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                data: itemsData,
                success: function (data) {
                    items = data;
                },
                error: function (data) {
                    console.log(data);
                }
            });
            // return the taxa models
            return items;
        },
        // set the grid column content
        // selectorName: the selector to be used to find the column element (the id of the element in the column's client template
        // items: the taxa collection
        // itemIndex: the index of the items in the grid
        // itemElement: the item row element - the <tr> of the grid
        setColumnData: function (selectorName, items, itemIndex, itemElement) {
            var result = "";
            // check if there are any taxa for this item
            if (items[itemIndex]) {
                for (var i = 0; i < items[itemIndex].length; i++) {
                    // build the result string
                    result += items[itemIndex][i].Title + "; ";
                }
            }
            // find the grid row and column and set its content
            // example: tags column for first item in the grid -> #tags0
            jQuery(itemElement).find("#" + selectorName + itemIndex).html(result);
        }
    };
 
    return {
        get_extender: function () {
            if (this._extender) {
                return this._extender;
            }
            else {
                alert("extender not initialized");
            }
        },
        init: function (sender) {
            this._extender = new extender(sender);
            return this._extender;
        }
    }
}());

The service we will use is a specially prepared to serve the passed data and return the same associative array of TaxonModel:

[ServiceContract]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
    [ServiceBehavior(IncludeExceptionDetailInFaults = true, InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class TaxaService
    {
        [OperationContract]
        [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        public virtual SimpleTaxonModel[][] GetTaxaByIds(Guid[][] taxaIds)
        {
            TaxonomyManager manager = TaxonomyManager.GetManager();
 
            // Get All Ids
            List<Guid> allIds = new List<Guid>();
            for (int i = 0; i < taxaIds.Length; i++)
            {
                if (taxaIds[i].Length > 0)
                {
                    allIds.AddRange(taxaIds[i]);
                }
            }
 
            // Make a call to the database to get all taxa
            var allTags = manager.GetTaxa<Taxon>().Where(t => allIds.Contains(t.Id))
                .Select(t => new SimpleTaxonModel() // Convert the taxon to the model
                {
                    Id = t.Id,
                    Title = t.Title
                }).ToArray();
 
            // populate the result
            SimpleTaxonModel[][] result = new SimpleTaxonModel[taxaIds.Length][];
            for (int i = 0; i < taxaIds.Length; i++)
            {
                if (taxaIds[i].Length > 0)
                {
                    // set the correct taxa for each item
                    result[i] = allTags.Where(c => taxaIds[i].Contains(c.Id)).ToArray();
                }
            }
 
            return result;
        }
    }
 
    public class SimpleTaxonModel
    {
        public Guid Id { get; set; }
        public string Title { get; set; }
    }

We register the service in the global application file:

       SystemManager.RegisterWebService(typeof(SitefinityWebApp.ExtensionScripts.TaxaService),
"Sitefinity/Services/TaxaService.svc");

Result:

Here is a video of the end result:

Unable to display content. Adobe Flash is required.

You can download the files from GitHub.

Nikola Zagorchev

Nikola Zagorchev is a Tech Support Engineer at Telerik. He joined the Sitefinity Support team in March 2014.

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