Kendo UI Chart Legend Overlaps with the Plot area and the pie numbers

I kept running into an issue where the chart kept drawing over itself. Basically, the chart would draw the lines and numbers that extend from the pie area and overlap the legend at the bottom. It did it when I had it on the right as well. This is stupid, and stupid I had to use a workaround, but here is the workaround.

My style. Edit as you need for yours.

        .chart-wrapper, .chart-wrapper .k-chart
        {
            height: 300px;
            width: 325px;
            margin-bottom: 20px;
        }

The code I had to add to my chart create:

 $(phaseContainerId).kendoChart({
                title: {
                    position: "bottom",
                    visible: false

                },
                legend: {
                    visible: true,
                    offsetX: -0,
                    offsetY: 20,
                    position: 'bottom'
                }
                ,........

I also added the following to avoid clipping, which can happen.

 chartArea: {
                    background: "",
                    margin: { top: 30, left: 5, right: 5, bottom: 15 }, padding: { top: 10, left: 10, right: 10, bottom: 10 }
                },

Notice the offset. That moves the legend around. You may need to play with the numbers depending on your font and other design choices.

Happy coding!

How to escape the hash(#) sign in a Kendo Grid column template?

If you are writing a Kendo Grid Column template and you run into the malformed template issue when trying to add a hyperlink that executes a javascript function (i.e. <a href=’#’ onclick=’function(me)’> blah </a> ), then here is the easy fix.

As with all the Telerik lack of documentation, or ease of finding, this one isn’t obvious but is easy. Just add to backslashes \#.

This

<a href=’#’ onclick=’function(me)’> blah </a>

becomes this

<a href=’\#’ onclick=’function(me)’> blah </a>

Example template of mine:

   <script type="text/x-kendo-tmpl" id="myPortfolio_PlanningTemplate">
      <a class='noStyleActionLink' href='\#' onclick='showPlanning("#: Id #")'> <div> 
        # var imgurl='redLight.png',statusText='Unknown - ERROR' #
        # if(PlanningStatus==PlanningStatusEnum.Not_Started){ imgurl='greyLight.png';statusText='Not Started' }  #
        # if(PlanningStatus==PlanningStatusEnum.On_Track){ imgurl='greenLight.png';statusText='On Track' }  #
        # if(PlanningStatus==PlanningStatusEnum.Deadline_Approaching){ imgurl='yellowLight.png';statusText='Deadline Approaching' }  #
        # if(PlanningStatus==PlanningStatusEnum.Overdue){ imgurl='redLight.png';statusText='Overdue' }  #
        # if(PlanningStatus==PlanningStatusEnum.Complete){ imgurl='greenLight.png';statusText='Complete' }  #            
         <img src='@Url.Content("~/Content/Images/lights/")#: imgurl #'/>  #: statusText # 
        </div>          
        </a>
    </script>

Happy coding!

Fix: Kendo Grid Template gives error “Unable to get value of the property ‘replace’: object is null or undefined”

I got the lovely unhelpful error “Unable to get value of the property ‘replace’: object is null or undefined” from the Kendo Grid when I added a template to a column. This was a completely useless error that led me to staring at the javascript stack and walking through the minified code. Which, by the way, is very painful to look through obfuscated minified code.

After entirely too long analyzing the code, I finally figured out the root cause. I had an ever so small typo in my template retrieval declaration and it was returning back a null template. Why on earth didn’t the Telerik guys NULL check this and fire a friendly error? All of two lines of code would have saved me some trouble.

See, javascript is entirely too friendly to typing mistakes.

Here is the errant code:

    {
                             title: "Planning", width: "100px",                                       
                             template: kendo.template($("myPortfolio_PlanningTemplate").html())
                         },

Here is the fixed code.

    {
                             title: "Planning", width: "100px",                                       
                             template: kendo.template($("#myPortfolio_PlanningTemplate").html())
                         },

Painful lesson in remember to put in the hashtag for ID lookups. Easy to forget when in a hurry.

Happy Coding!

How to animate a slide from the right with the Jquery show command

I didn’t see this in the documentation, but I’m sure its there somewhere. Here is an easy way to slide in a container/div/etc from the right.

$("#myActionsAuditUnitContainer").show({ effect: "slide",direction:"right", duration: 900 });

Use the direction option. Nice and easy.

Happy Coding!

How to add complex headers to a Kendo grid using simple jQuery javascript

I was trolling through the horrible documentation provided by Telerik for the kendo grid. I needed to create a complex header. Basically, a two row header so that I could group columns in a visually meaningful way.

simple example:

I spent far too much time reading how it couldn’t be done. A light bulb finally hit me, this is html. Yeah, flexibility abounds if you can be clever. I simply opened up the old browser developer tool and looked at the generated html for the grid.I then realized, this is a simple table, why not just inject another table row.

So, here is the easy way to add another row to your kendo grid and make a nice complex header. You can use this technique to go to town on adding additional features to your grid as well.

//create grid.

function BuildMyActionsList(tasks) {
            var list = $("#tasksContainer");
            list.empty();

            list.kendoGrid(
                {
                    dataSource: {
                        data: tasks,
                        schema: {
                            model: {
                                id: "Id",
                                fields: {
                                    Name: { type: "string" },                                    
                                    LessThanEGAs: { type: "number" },
                                    GreaterThanEGAs: { type: "number" },
                                    LessThanRisks: { type: "number" },
                                    GreaterThanRisks: { type: "number" },
                                    LessThanCNs: { type: "number" },
                                    GreaterThanCNs: { type: "number" },                                    
                                    ReportSignDate: { type: "date" }
                                }
                            }
                        },
                        //     pageSize: 20
                    },
                    dataBound: addExtraStylingToTasksGrid,
                    filterable: true,
                    scrollable: true,
                    sortable: true,
                    columns: [
                        {
                            field: "Name", title: "Engagement", width: "175px",
                            filterable: {
                                extra: false,
                                operators: {
                                    strings: {
                                        startswith: "Starts with",
                                        eq: "Is equal to",
                                        neq: "Is not equal to"
                                    }
                                }
                            }
                        },                        
                        {
                            field: "LessThanEGAs", title: "EGA's", width: "80px"
                        },
                        {
                            field: "GreaterThanEGAs", title: "CN's", width: "80px"
                        },
                        {
                            field: "LessThanRisks", title: "Risk's", width: "80px"
                        },
                        {
                            field: "GreaterThanRisks", title: "EGA's", width: "80px"
                        },
                        {
                            field: "LessThanCNs", title: "CN's", width: "80px"
                        },
                        {
                            field: "GreaterThanCNs", title: "Risk's", width: "80px"
                        },
                        {
                            field: "ReportSignDate", title: "Report Signing", width: "125px", filterable: false                            
                        }
                    ]
                });

            list.find("thead").first().prepend("<tr><th></th><th class='lessThanDaysGrouping' colspan='3'>&lt;= 2 Days</th><th  class='greaterThanDaysGrouping' colspan='3'>&gt; 7 Days</th><th></th></tr>");

        }

Notice , the line at the end. This is where we search the table for the header and then prepend the row. Just make sure your new row definition aligns with the same column structure as your table. If you need help with this, search on html tables on google.

Line of code that does the magic:

  list.find("thead").first().prepend("<tr><th></th><th class='lessThanDaysGrouping' colspan='3'>&lt;= 2 Days</th><th  class='greaterThanDaysGrouping' colspan='3'>&gt; 7 Days</th><th></th></tr>");

 

Happy Coding!

 

Click event bug with the iPad when using jQuery

I am writing a web application that uses a kendo grid with a detail pane. I hooked up a click event on a table in the detail pane to navigate to another page. It worked beautifully on the desktop. I tested on the iPad, and when you clicked a row, it would immediately fire off the click event and navigate away. WTF.

This drove me nuts. I found a person that also had a click issue posting about their oddity. Slightly different for them, but the fix is the one and the same.

It turns out that on the iPad, you can bind to the touchevent event instead of the click event and the problem goes away. It also takes out the 300ms delay some complain about.

Implementation is rather easy. Instead of using .click(function….) use bind.

Put this at the top of your script section.

  var ua = navigator.userAgent,
            clickEventBindingName = (ua.match(/iPad/i)) ? "touchstart" : "click";//stupid ipad workaround

Now, in your code, you simply using this to capture the events.

  $("#YOURELEMENT").bind(clickEventBindingName,
                function () {
                    doStuff();
                }
                );

Problem solved.

 

Happy Coding.

 

How to do a smooth scroll to an element/div with jquery

I was looking for a neat way to load dynamic data and then scroll the user to it. I wanted it to look smooth as well. Quick jumps make it look too harsh, like a page refresh.

Here is a simply function you can use. Just pass an element to it, jquery wrapped.

function…..

[javascript]
     function scrollToElement(ele) {
            var position = ele.position();
            $('html, body').stop().animate({
                scrollTop: position.top
            }, 500);
        }
[/javascript]

usage.

<div id=’foo’/>

You want to scroll to foo.

scrollToElement($(‘#foo’));

that easy.

Happy Coding!

How to display a complex type/list in a Kendo UI Grid client side using a simple template

I had the need to display multiple values from a list that was attached to each row. Simply put, I had a list of Roles the user could be in for each record. I wanted to display them delimited. Turns out, not that hard.

I at first was not loving the idea of using the Telerik kendo grid. I preferred to roll my own html using javascript alone. Since this is a large team based project, I decided to use a more consistent approach and utilize the Kendo library. Painful at first, since the documentation sucks more than an industrial vacuum.

The beauty lies in the power of the template. Telerik is nice enough to let you use powerful javascript in the templates, which makes it wonderful to configure and tailor each cell to your specifications. For this post, the need was to write a pipe delimited list to the cell of all the role names.

Here is the magic. The template I created. You obviously need to tailor for your field names.

    <script id="rolesColumnTemplate" type="text/x-kendo-tmpl">    

        # for(var iRoleIndex=0;iRoleIndex< Roles.length;iRoleIndex++){ #
        # if(iRoleIndex==0){  #
        #: Roles[iRoleIndex].Name #
        # } else { #
        #: " | " + Roles[iRoleIndex].Name #
        # } } #
    </script>

You need to bind it to a column which is also easy.

  columns: [

                        {
                            field: "Roles", title: "Roles", width: "80px",
                            template: kendo.template($("#rolesColumnTemplate").html()),
                            filterable:
                                {
                                    operators: {
                                        string: {
                                            eq: "Is equal to",
                                            neq: "Is not equal to"
                                        }
                                    }
                                }
                        }
                    ]

Notice how easy that was. Well, easy once you know it.  Not easy if you had to figure it out on your lonesome.

Happy Coding!

How to set specific filter options with the Kendo UI Grid in Javascript!

I spent about an hour trying to decipher the cryptic documentation that Telerik provides. It’s really difficult to use, especially with hundreds of method on one page and not even organized. Clicking each property should give access to its member properties and be navigable heirarchicaly. Well, its not.

Anyway, rant over. I was trying to limit the options for a filter on one of my columns. Seems easy enough, right? Well, I kept pasting the stupid example code in and nothing happened.

It turns out, that you can indeed set column specific filter options. Which made me happy, but you need to configure the filter options based on the data type of the column. Yeah, stupid to me. If I am including the names, which are generic across most of the types, this should have been easier.

Take note of the member property you set. See, one instance is called strings. One is called number. I love the inconsistency with the pluralization too, so fun.

So, here is an example of a string column.

 {
                            field: "EngagementName", title: "Engagement", width: "175px",
                            filterable: {
                                        extra: false,
                                        operators: {
                                            strings: {
                                                    startswith: "Starts with",
                                                    eq: "Is equal to",
                                                    neq: "Is not equal to"
                                            }
                                        }
                                    }
                        },

Here is an example of a number(int) column.

{
                            field: "SystemType", title: "Type", width: "80px",
                            template: function(dataItem) {
                                return systemTypeToString(dataItem.SystemType);
                            },
                            filterable:
                                {
                                    ui: systemTypeFilter,
                                    operators: {
                                        number: {
                                            eq: "Is equal to",
                                            neq: "Is not equal to"
                                        }
                                    }
                                }
                        },

Look through the docs and you will see there is a date, enums, etc. Just make sure to match that property name to the type of the column.

In case that confused you, here is a full column definition with filters.

 
  columns: [
                        {
                            field: "EngagementName", title: "Engagement", width: "175px",
                            filterable: {
                                        extra: false,
                                        operators: {
                                            strings: {
                                                    startswith: "Starts with",
                                                    eq: "Is equal to",
                                                    neq: "Is not equal to"
                                            }
                                        }
                                    }
                        },
                        { field: "EntityName", title: "Entity",  filterable: false},
                        {
                            field: "SystemType", title: "Type", width: "80px",
                            template: function(dataItem) {
                                return systemTypeToString(dataItem.SystemType);
                            },
                            filterable:
                                {
                                    ui: systemTypeFilter,
                                    operators: {
                                        number: {
                                            eq: "Is equal to",
                                            neq: "Is not equal to"
                                        }
                                    }
                                }
                        },
                        {
                            field: "DateAssigned", title: "Assigned", width: "125px", filterable: false,
                            template: function(dataItem) {
                                //var taskDate = new Date(dataItem.DateAssigned.match(/d+/)[0] * 1);
                                var taskDate = new Date(dataItem.DateAssigned);
                                var offset = new Date().getTimezoneOffset();
                                    taskDate.setMinutes(taskDate.getMinutes() - offset);
                                      return  humaneDate(taskDate);
                                }
                        }
                    ]

Happy coding!