Quantcast
Channel: Kendo UI – Falafel Software Blog
Viewing all 43 articles
Browse latest View live

Kendo: Model defaultValues not applied to partial objects

$
0
0

Introduction

If you’ve used the Kendo DataSource class very much, you have probably noticed that Kendo comes with a nice way to define server data with the Model class. One of the convenient things about a Model is that it lets you define fields with default values, so that when a new instance is created, the instance already has all of the fields you defined, reducing the possibility of referencing a property that is undefined. However, you may soon discover to your sorrow as I did, that this will only occur if you do not pass a values object to the Model constructor. This seemed so bizarre and obviously wrong that it must be a bug, so I reported it, only to be told that this behavior was designed. To this day I do not understand why and no attempt to address my question was given, but it was correctly pointed out that I could create my own Model subclass that implements the behavior I desire. So I did, and I’m publishing the results here so that others may benefit.

Show me the code!

If you know what you’re doing, it takes minimal code to implement the desired behavior:

var PartialDefaultModel = kendo.data.Model.extend({
  init: function(options) {
    kendo.data.Model.fn.init.call(this, $.extend({}, this.defaults, options));
  }
});

PartialDefaultModel.define = function(options) {
  return kendo.data.Model.define(PartialDefaultModel, options);
};

It’s a little code, but there’s a lot going on, so let’s unpack it. On the first line is a call to Model.extend(). The extend function is introduced by Kendo’s Class type and is how you create inheritance hierarchies in Kendo. The object you pass in will be used to define the new subclass. By convention, all Kendo Class subtypes will call their init function during construction. This is where you add logic that would go in a constructor in an classical object-oriented language. However, by default the base class’ init function is not called, so you must call it manually, as seen in the body of the init function. On any descendant of a Kendo Class, the fn property contains the prototype that is shared by all instances of that Class, so kendo.data.Model.fn refers to the prototype object shared by all instances of Model. That’s where shared methods like init are defined, not on the instances themselves. The init function is going to want its context set to the current object, so you use Function.prototype.call to call it while setting its context (a.k.a. “this”). Lastly, I use jQuery’s extend function to merge the object passed to the constructor with the defaults, which are already stored by Model.define in the defaults property in a name-value object. Finally, to make is easier to define models based on this new Model subclass, I added a define method to it. This call to define looks different than the one in the documentation, but if you look at the source code you’ll see that Model.define actually takes two parameters, the first of which is an optional base class. If only one parameter is passed to Model.define, then Model is used as the base.

I created a JSBin to demonstrate how this new model can be used. It has three parts to it. The first part defines an out-of-the box Kendo Model that demonstrates the undesirable behavior:

var modelOptions = {
  fields: {
    name: { type: 'string' },
    kind: { type: 'string', defaultValue: 'Cat' }
  }
};

var Pet = kendo.data.Model.define(modelOptions);
var pet = new Pet({ name: 'Boris' });

console.log({
  name: pet.name,
  kind: pet.kind
});

Output:

[object Object] {
  kind: undefined,
  name: "Boris"
}

Next I create the new Model class and define a model with it:

var PartialDefaultModel = kendo.data.Model.extend({
  init: function(options) {
    kendo.data.Model.fn.init.call(this, $.extend({}, this.defaults, options));
  }
});

PartialDefaultModel.define = function(options) {
  return kendo.data.Model.define(PartialDefaultModel, options);
};

var PartialPet = PartialDefaultModel.define(modelOptions);
var partialPet = new PartialPet({ name: 'Natasha' });

console.log({
  name: partialPet.name,
  kind: partialPet.kind
});

Output:

[object Object] {
  kind: "Cat",
  name: "Natasha"
}

So we can see that the partial application of the “kind” default is working! Lastly, I created a simple DataSource and bound it to a Grid to show that it plays nicely with the rest of Kendo. The AJAX response contains one object that specifies a kind, and you will see that the grid output correctly shows the default for the objects that didn’t specify it, and shows the kind of the object that did specify it.

Even though I might not understand or agree with Kendo’s maintainers about their decision not to implement this behavior, it’s pretty cool that it’s so easy to extend and drop in a replacement that does what you want!

The post Kendo: Model defaultValues not applied to partial objects appeared first on Falafel Software Blog.


Learning to Export Kendo UI Widgets without a Browser Using PhantomJS Part 2: External Input

$
0
0

In my previous post and example, we created a simple PhantomJS export-to-PDF project that loaded a page with a Kendo ListView and exported it to a PDF. In that code, we used data that already existed in the phantomexport.js file. But that isn’t really a good example, since in the real world that data would be fetched externally and then fed to the export process.

If the idea is to ‘export’ a Kendo page using the same data the page would have fetched itself, we will need to fetch that data using some other method like calling the server-side data methods directly, then converting to JSON and pass those results into the PhantomJS exporter.

So, what would that look like?

First, we need some sample data to input to our export method. I am using the same format as in the previous example, but with a few more items. We will use an -infile option to include the data, making sure to escape it and enclose it in quotes.

static void Main(string[] args)
        {
            var inputString = GetStringInput();

            Process compiler = new Process();

            compiler.StartInfo.FileName = "phantomjs";
            compiler.StartInfo.Arguments = "phantomexport.js";
            compiler.StartInfo.Arguments = "phantomexport.js -infile '" + inputString + "'";
            
            compiler.StartInfo.CreateNoWindow = false;
            compiler.StartInfo.UseShellExecute = false;
            compiler.Start();
            Console.ReadLine();
        }

        private static string GetStringInput()
        {
            return
                "[{\"name\":\"Jane Doe\",\"age\":30},{\"name\":\"John Doe\",\"age\":33},{\"name\":\"Bob Smith\",\"age\":45},{\"name\":\"Barbara Smith\",\"age\":42}]";
        }

Great, now we need to let the javascript know that there is data there to use. In phantomexport.js, we will add a function to map the command line arguments to an args object which will have a property named infile.

mapCLArguments = function () {
        var map = {},
            i,
            key;

        for (i = 0; i < system.args.length; i += 1) {
            if (system.args[i].charAt(0) === "-") {
                key = system.args[i].substr(1, i.length);
                map[key] = system.args[i + 1];
            }
        }
        return map;
    };

And then pass those arguments into our render function.

//execution starts here
    args = mapCLArguments();

    config.tmpDir = fs.workingDirectory + "/tmp";

    render(args, function (msg) {
        console.log(msg);
        setTimeout(function () {
            phantom.exit();
        }, 0);
    });

Now we can pass the infile string to the creation of our Kendo ListView.

render = function (params, exitCallback) {
        var page = require("webpage").create(),
            exit,
            input = params.infile,
            n = new Date().getUTCMilliseconds(),
            output = config.tmpDir + "/ExportedListViewTest_" + n + ".pdf",
            createListView;

        page.onConsoleMessage = function (msg) {
            console.log(msg);
        };

        page.paperSize = {
            width: '8.5in',
            height: '11in',
            margin: {
                top: '50px',
                left: '20px',
                bottom: '50px',
                right: '20px'
            }
        };

        page.open("kendotestpage.html", function (status) {

            page.evaluate(createListView, input);

            page.evaluate(function () {
                var body = document.body;
                body.style.backgroundColor = '#fff';
            });
            page.render(output);

            exit("output file created");
        }););

And parse the JSON to use in our DataSource.

createListView = function (inputJSON) {
            try {
                // parse the input data and use it to populate the dataSource
                var inputData = JSON.parse(inputJSON);
                var dataSource = new kendo.data.DataSource({
                    data: inputData
                });
                $("#test-listview").kendoListView({
                    dataSource: dataSource,
                    template: kendo.template($("#template").html()),
                });

            } catch (e) {
                console.log(e);
                console.log("ERROR : Exception while creating Kendo listview");
            }
        };

And we can run the console app and look at the exported file, which contains the input data just as we would expect to see it in our ListView.

2015-06-19 09_31_15-ExportedListViewTest_933.pdf - Adobe Reader

You can check out the Example 2 for this series here, and stay tuned to see how we can debug this project in case things don’t go as planned.

The post Learning to Export Kendo UI Widgets without a Browser Using PhantomJS Part 2: External Input appeared first on Falafel Software Blog.

Range Sliders for Dates and Times using Kendo UI

$
0
0

Ever needed to create one of those range-style sliders for Dates or Times? Like what you might see when selecting flight times for airline tickets – show me flights that take off after 7am, but before 12pm, thanks! You can do something similar with the Kendo UI RangeSlider widget. But let me point out a few tips to help.

Normally I like to use declarative initialization whenever possible, especially when I am using MVVM binding anyway. But in this case, not everything you need can be done declaratively. But that’s ok.

First, let’s start with a Time slider. We need to bind the numeric part of the slider to the milliseconds of the time, so I’ll save you a little math. Here is how to create a range slider for a 24 hour period. Yes, we can do most of this declaratively.

A range slider requires a div and two input elements, as shown below. Also, note that the value will be an array with two items, the min and max selection. I have set my step size to be one hour. Also, I have set the tooltip to display the values formatted as times.

<div id="timeSlider" data-bind="value: selectedTimeRange" data-role="rangeslider" data-small-step="3600000" data-min="21600000" data-max="108000000" data-large-step="3600000" data-tickplacement="none" data-tooltip="{
                 template:'#=kendo.toString(new Date(selectionStart), \'h:mm tt\' ) # to #=kendo.toString(new Date(selectionEnd), \'h:mm tt\' ) #'
                 }" >
    <input />     
    <input />
</div>

Displaying labels on the tick marks doesn’t really work too well, so instead we can hide the tick labels altogether with a little CSS:

.k-tick > .k-label{
    display: none;
}

And finally, we can display the values below the slider using Kendo MVVM and the following function.

timeRangeChanged: function(){
            var timeRange = viewModel.selectedTimeRange;
            var startTime = kendo.toString(new Date(timeRange[0]), 'h:mm tt' );
            var endTime = kendo.toString(new Date(timeRange[1]), 'h:mm tt' );
            viewModel.set('hoursStart', startTime);
            viewModel.set('hoursEnd', endTime);
        },

2015-08-06 20_47_58-RangeSliders for hours and days - JSFiddle

Now, after understanding how all of those pieces fit together, we can start on the date range slider. I added two DatePicker widgets to set the start and end DateTime values, but here’s the tricky part. In order to change the options of the Kendo UI RangeSlider, we have to recreate the entire widget. It’s not my favorite solution, but it is functional!

updateDateSlider: function(){
            if ( viewModel.selectedStartDate <= viewModel.selectedEndDate)
            {
                // reinitialize the date slider, since sliders do not support dynamic changes
                var slider = $("#dateSlider").data("kendoRangeSlider");
                    if (slider){
                var wrapper = slider.wrapper;
                var element = slider.element;
                slider.destroy();
                wrapper.before(element.show());
                wrapper.remove();
            }
            $("#dateSlider").kendoRangeSlider({
                largeStep: 86400000,
                smallStep: 86400000,
                min: viewModel.selectedStartDate.getTime(),
                max: viewModel.selectedEndDate.getTime(),
                value: [viewModel.selectedStartDate.getTime(), viewModel.selectedEndDate.getTime()],
                tooltip: {
                    template: kendo.template('#=kendo.toString(new Date(selectionStart), \'M/d/yyyy\' ) # to #=kendo.toString(new Date(selectionEnd), \'M/d/yyyy\' ) #')
                },
                change: viewModel.dateRangeChanged
            });
                viewModel.resizeSliders();
                viewModel.dateRangeChanged();
            }
        },

And finally, one more tip. To get the RangeSliders to expand to fill the width of their container, we have to do a little more manipulation. I call this function after initialization, but you may also want to use it for window resizing.

resizeSliders: function(){
            // resize the sliders to fill the container
              var sliders = $("[data-role='rangeslider']");
                sliders.each(function (index, ele) {        
                var slider = $(ele).getKendoRangeSlider();
                slider.wrapper.css("width", "100%");
                slider.resize();
            });
        }

So, here’s how it looks altogether.

2015-08-06 16_48_47-RangeSliders for hours and days - JSFiddle

And also the full example you can try out for yourself:

The post Range Sliders for Dates and Times using Kendo UI appeared first on Falafel Software Blog.

Learning to Export Kendo UI Widgets without a Browser Using PhantomJS Part 3: Debugging

$
0
0

In our first 2 exercises, we completed a simple Kendo widget initialization and output using PhantomJS. But if you want to use this technique for something a little more complex with a few more widgets and a more complicated UI, it’s likely you will also want to know how to debug when things don’t go as planned.

Even though the whole point may be ‘browserless’, we can still use a WebKit browser to debug our PhantomJS process, and I will show you how.

First, add a debugger option to the PhantomJS command text.

compiler.StartInfo.Arguments = " --remote-debugger-port=9000 phantomexport.js -infile  '" + inputString + "'";

And, so that the debugger will have a place to stop within the JavaScript, add a debugger statement there as well.

createListView = function (inputJSON) {
            try {
                debugger;
                // parse the input data and use it to populate the dataSource
                var inputData = JSON.parse(inputJSON);
                var dataSource = new kendo.data.DataSource({
                    data: inputData
                });
                $("#test-listview").kendoListView({
                    dataSource: dataSource,
                    template: kendo.template($("#template").html()),
                });

            } catch (e) {
                console.log(e);
                console.log("ERROR : Exception while creating Kendo listview");
            }
        };

Now, run the project from Visual Studio. The console window will open, but the output message will not display yet. When the debugger option is used, PhantomJS waits to be run from the WebKit console. To do that, navigate in your browser (I used Chrome) to localhost:9000. You should see a link in the browser like this:

2015-08-16 15_34_42-Remote Web Inspector

Click the link, and you will see the WebKit Inspector. Type the command __run(); into the console window, and PhantomJS will execute and break at your debugger statement.

2015-08-16 15_35_26-localhost_9000_webkit_inspector_inspector.html_page=1

From here you can add more breakpoints and debug the script by stepping through. But you can also view the HTML, which is really useful.

2015-08-16 15_39_49-localhost_9000_webkit_inspector_inspector.html_page=2

Or view the other scripts on the page:

2015-08-16 15_39_35-localhost_9000_webkit_inspector_inspector.html_page=2

And you can continue the execution and view the output file as normal from this point. You can also view the full example here.

Did you learn something? I really recommend you attend FalafelCON 2015, where there will be many deep dives on Kendo UI.

The post Learning to Export Kendo UI Widgets without a Browser Using PhantomJS Part 3: Debugging appeared first on Falafel Software Blog.

The Hazards of Shared Kendo DataSources

$
0
0

Kendo DataSources are a foundational component of many of Kendo’s widgets and indeed of many data-driven applications. The Kendo docs point out that they are shareable, but their example only shares them among different widgets. Sharing them among multiple observable ViewModels, however, is fraught with pitfalls, many of which I have recently felt the displeasure of stumbling into. As usual, diagnosing the problem led to some interesting insights on how Kendo bindings work, so let’s take a look together.

Initial Setup

In this first JSBin, I’ve configured a DataSource and then used it in a ViewModel which is then bound to part of the page. When an list item rendered from a DataSource item is clicked, it sets a property on the ViewModel which another function depends on, prompting the binding to refresh the UI to show the text of the selected item. Pretty simple. However, stop for a moment and consider the template for the list items:

<li data-bind="click: onItemClick">#= text #</li>

One property used by the template (text) is a property of the data items in the DataSource, but the other property (onItemClick) is a property of the ViewModel. Bwah? If you dive into the Kendo source code, what you will find is that data bindings (and only data bindings) will look for a property matching the binding in the current object, and if not found, it will navigate to the parent object and look there, until it reaches the topmost object. Again, this only occurs for data bindings; if you tried to use get(‘onItemClick’) on a data item, it would not return anything. This functionality requires that the DataSource have been set up to be aware that it even has a parent, and this happens automatically for any object passed to the initializer of a Kendo ObservableObject, or for anything added to an existing one with the set method.

Problem Demonstration

Now let’s pretend you wanted so use this same DataSource in multiple ViewModels throughout your application. Here’s a one-page simplification of this kind of usage. Different HTML, different ViewModel, but same DataSource. If you try clicking the list items, you will see something strange happening: clicking the list items of the first ViewModel appears to be triggering events on the second one! How can this be? Well, any object can only have one parent at a time, that’s how. When the same DataSource instance is passed into the second ViewModel, the link to the first ViewModel as a parent is lost and clicks become routed to the second. No matter how you slice it, you simply cannot share the same DataSource instance in this kind of scenario, because each instance can only have a single parent. Two ViewModels require two DataSource instances. However, there are many ways to prevent duplication of the DataSource configuration. Here are a few.

Separate config from DataSource creation

Instead of creating a DataSource in a shared variable, just save the config options in the shared variable and reference it any time you need to create a new instance of an identically-configured DataSource. (Example)

Define a factory function to construct DataSource instances

Taking the first idea a step further, define a function that constructs DataSource instances. This encapsulates DataSource creation a little better, but hides the configuration a little more. This may be desirable or not, depending on the specific situation. (Example)

Use the DataSource’s config to clone it

The quick and dirty approach, you can clone the config of an existing DataSource instance by referencing its options property. (Example)

To summarize:

  1. MVVM bindings don’t just look at the current object being bound; if a binding specifies a property that is not found on the current object, then the binder will climb up the ancestry tree until it finds the property in a parent object.
  2. In order for this behavior to work, the current object’s parent must be set. This happens automatically for you if you initialize an ObservableObject by passing it another ObservableObject, or if you add it to an existing ObservableObject with the set() method.
  3. Since every object can only have one parent at a time, in the scenario above you have no choice but to have two DataSource instances.

Concerned about cloning a lot of data? There’s a workaround for that, too! Yes, you can have two different DataSource instances, each with their own Observable parents, but sharing the same data! The key is in understanding what will cause a DataSource to clone data versus using the exact data you passed in. (See one of my previous post about this: Dude, Where’s My Data?) If you don’t pass an ObservableArray to the DataSource, it will wrap the passed data, essentially cloning and disconnecting the data you passed from what is within the DataSource. However, if you make sure to create an ObservableArray first, then the DataSource will use it as-is. See this example, where the code operates directly on the shared data and both views are updated.

The post The Hazards of Shared Kendo DataSources appeared first on Falafel Software Blog.

Kendo DataSource: Why Isn’t cancelChanges() Working?

$
0
0

Introduction

There are a lot of different ways to get data into a Kendo DataSource. For example, you can just pass some plain objects to the data() method and the DataSource will helpfully wrap the objects for you in a Model instance if one is specified, or else in a generic ObservableObject. If you want to maintain a reference to the same object that will reside in the DataSource, you can pre-emptively wrap the objects yourself before you pass them in, and Kendo will detect that the objects are already the correct type and use them as-is. You can even skirt around the DataSource and work directly with the ObservableArray that the DataSource uses to hold its data by calling the data() method without any parameters and manipulating the returned ObservableArray directly. I’ve used all of these approaches in various situations, and they all work similarly. That is, until you try to cancel local changes by calling cancelChanges. That’s when things start to get weird.

The Weirdness

Let’s take a little bit of plain data:

var plainData = [
  { id: 1, text: 'foo' },
  { id: 2, text: 'bar' }
];

And put the same data into three different DataSources, each using a different way of getting the data in there:

this.plainDataSource.data(plainData);
    this.wrappedDataSource.data(plainData.map(wrapPlainData));
    this.splicedDataSource.data().splice.apply(this.splicedDataSource.data(), [0, this.splicedDataSource.data().length].concat(plainData));

If you were to bind these DataSources to a UI, they would all render the same so far. Here’s a sample JSBin that illustrates the whole thing. Run it and click the button labeled “Init DataSources” to see the results so far. Now, let’s set up some changes to try to undo with cancelChanges(), assuming you manipulate your data in a consistent way. So each DataSource will apply its changes slightly differently.

this.plainDataSource.at(0).set('text', 'changed');
    this.wrappedDataSource.at(0).set('text', 'changed');
    this.splicedDataSource.at(0).set('text', 'changed');
    var newPlainObj = { id: 3, text: 'new' };
    this.plainDataSource.add(newPlainObj);
    this.wrappedDataSource.add(wrapPlainData(newPlainObj));
    this.splicedDataSource.data().push(newPlainObj);

Clicking the button labeled “Change DataSources” will apply these changes. The UI will update reflecting the edits and changes. (Aside: I added a new item to each DataSource to trigger the UI to refresh; simply changing an item will not update the UI in this case. Other solutions include using a data binding instead of a template, or manually triggering the event which causes the UI to refresh. Check out the prior version of the JSBin to see how to manually trigger the requisite event.)

OK, everything’s all set up. Ready for the weirdness? Click the button labeled “Cancel Changes.” Whoa! What has happened? The plainDataSource’s changes reverted as expected. The wrappedDataSource’s changed item remained unchanged, but the new item is gone. And the splicedDataSource is completely empty now!

There is a rational explanation for all of this within the innards of the Kendo DataSource. I’m going to try to highlight the important parts so you can gain understanding as to why all these things happened. First of all, take a look at this snippet from the data() method that shows what happens when you assign data to a DataSource:

data: function(value) {
            var that = this;
            if (value !== undefined) {
                that._data = this._observe(value);

                that._pristineData = value.slice(0);

…and this little bit from _observe:

_observe: function(data) {
            var that = this,
                model = that.reader.model,
                wrap = false;

            if (model && data.length) {
                wrap = !(data[0] instanceof model);
            }

            if (data instanceof ObservableArray) {
                if (wrap) {
                    data.type = that.reader.model;
                    data.wrapAll(data, data);
                }
            } else {
                data = new ObservableArray(data, that.reader.model);
                data.parent = function() { return that.parent(); };
            }

…So, if you pass an ObservableArray it will use that as-is, and if any of the objects in the array are already the configured model type for the DataSource, it will use them as-is. This makes sense and seems efficient, but consider what happens if that._data == value == that._pristineData. Here’s how cancelChanges works:

that._destroyed = [];
                that._data = that._observe(that._pristineData);

So, if _data is the same as _pristineData, then the changes that have been made to the items in the DataSource are also being made to the items in _pristineData because they are the same objects. That’s why the changed item in wrappedDataSource did not revert. The new item did not stick around because while the items in the array were already Models, the array itself was not an ObservableArray. In fact, even passing an ObservableArray will not cause the new item to remain because _pristineData is assigned the result of value.slice(0), which returns a new array. At this point it should also be quite clear why the splicedDataSource is empty now: by operating directly on the ObservableArray, the code that establishes the baseline _pristineData never executed.

Not So Weird After All!

Once you take a look under the hood, it all becomes perfectly clear what’s going on and why. Could it perhaps be a bit more consistent? Probably. I do find it odd that _pristineData will always create a new array but that it might contain references to the same objects in _data; it would be more consistent if _pristineData always went out of its way to construct shallow copies of the objects instead. But when you consider that these are not exactly your typical use cases to begin with, it’s really a pretty minor quibble. Anyway,  I hope you enjoyed seeing a little apparent chaos and the quick dive into the Kendo source code that explained the cause.

Happy Coding!

The post Kendo DataSource: Why Isn’t cancelChanges() Working? appeared first on Falafel Software Blog.

Extending Kendo UI Editor Functionality in Sitefinity

$
0
0

In my previous post I detailed a feature where users can upload files to the Sitefinity Document Library asynchronously. However, the interface for doing so was a bit basic. There was an HTML file input that automatically uploaded the selected file immediately. It worked, but the user wasn’t left with much to do with the link that was returned. Extending Kendo UI Editor functionality, however, can be used to make the return value more useful and make the whole user experience much better.

This post will show you how to take the feature and add it to the set of tools available for a Kendo UI Editor. We can re-use the code we developed in the previous post, altering the JavaScript and HTML some, while leaving the C# API controller the way it’s already written.

Hide the File Input and Add Textarea

Because we’ll be interacting with the Kendo UI Editor, we no longer need to present the file input to the user. We do still need it so users can select files, however. We also need a textarea that we’ll morph into a Kendo UI Editor. As such, our final required HTML will look like this:

<input type="file" id="fileUploadInput" name="fileUploadInput" style="visibility: hidden;" />
<textarea id="kendoTextArea"></textarea>

Note that we’re using “visibility: hidden” instead of “display: none” because the file might not get sent otherwise. Both accomplish the same goal of removing the HTML file input from view.

Initialize the Kendo UI Editor

We’ll be modifying the JavaScript from the previous post when incorporating these changes. For convenience, here is the JavaScript from the last post:

(function ($) {
    "use strict";

    var $fileUploadInput = $("#fileUploadInput");

    $fileUploadInput.on("change", function () {
        var newFile = $fileUploadInput[0].files[0];
        if (newFile === undefined || newFile === null) {
            return;
        }

        var formData = new FormData();
        formData.append("newFile", newFile);
        $.ajax({
            url: "/api/Files/Upload",
            type: "POST",
            data: formData,
            cache: false,
            contentType: false,
            processData: false
        })
        .done(function (fileUrl) {
            // Handle returned fileUrl here.
        })
        .always(function () {
            $fileUploadInput.val("");
        });
    });
})(jQuery);

The first thing we’ll do is declare a variable referencing our textarea HTML element, right below the other variable:

var $fileUploadInput = $("#fileUploadInput");
var $kendoTextArea = $("#kendoTextArea");

Then below the on(“change”) statement but still within our self-executing anonymous function, add the statement that transforms our textarea into a Kendo UI Editor. This initializes a Kendo UI editor and makes the specified set of toolbar icons appear across the top:

$kendoTextArea.kendoEditor({
    tools: [
        "bold",
        "italic",
        "underline",
        /* ...Other toolbar items... */
        "createLink",
        "unlink"
    ]
});

Adding the Custom Feature

Now we’ll start incorporating our custom functionality. We’ll accomplish this by adding a new object to the tools collection of the Kendo UI Editor and tell it to interact with our file selector input when clicked:

$kendoTextArea.kendoEditor({
    tools: [
        "bold",
        "italic",
        "underline",
        /* ...Other toolbar items... */
        "createLink",
        "unlink",
        {
            name: "uploadFile",
            tooltip: "Upload a File",
            exec: function () { $fileUploadInput.trigger("click"); }
        }
    ]
});

We trigger the “click” event of our file selector, which brings up the usual file selection dialog. This is how we get away with hiding the file input HTML element from the user.

To display an appropriate icon for our new tool, we can borrow an already-existing icon (in this case, a paperclip) from Kendo UI. This is accomplished by adding a simple CSS rule to our page (you can do this either inline, or in your site’s CSS):

.k-uploadFile {
    background-position: -288px -216px; /* Show the paperclip icon for the custom toolbar item for uploading files. */
}

The “uploadFile” part of the CSS class comes from the name we gave our tool in the tools JavaScript collection. Kendo UI automatically prepends “k-” to the class, so our style rule effects “.k-uploadFile” at the end of the day.

Here is what our editor looks like currently, with our custom tool appended to the end of the toolbar icons (the paperclip):

kendo-editor

Handling the Returned URL

At this point, you can click the new toolbar icon, select a file, and the process of uploading it and creating a new Document within Sitefinity will take place. All we need to do now is modify the original done() promise to insert the returned URL into our Kendo UI editor. We don’t want to simply append it, but would rather insert it where the user’s cursor currently is. This context-sensitive insert is a built-in feature of Kendo UI, and is accomplished easily:

$.ajax({
    url: "/api/Files/Upload",
    type: "POST",
    data: formData,
    cache: false,
    contentType: false,
    processData: false
})
.done(function (fileUrl) {
    var fileHtml = "<a href=\"" + fileUrl + "\">File</a>";
    var editor = $kendoTextArea.data("kendoEditor");
    editor.exec("inserthtml", { value: fileHtml });
})
.always(function () {
    $fileUploadInput.val("");
});

In our function, we create a new hyperlink with the text “File” and supply the URL as the href. Then we get our Kendo UI Editor and exec the “inserthtml” command, passing our newly-formed hyperlink along. And we’re done! The file is now linked, and exists in the Sitefinity backend:

kendo-editor-with-file-link

uploaded-file-in-document-library

In my local site, the URL that “File” links to is http://localhost:12345/docs/default-source/my-document-library/file-to-upload-txt.txt?sfvrsn=0, which loads the text file as expected.

Putting it all Together

After extending Kendo UI editor functionality and implementing our new JavaScript to do so, here is what the final JavaScript for this post looks like:

(function ($) {
    "use strict";
    
    var $fileUploadInput = $("#fileUploadInput");
    var $kendoTextArea = $("#kendoTextArea");
        
    $fileUploadInput.on("change", function () {
        var newFile = $fileUploadInput[0].files[0];
        if (newFile === undefined || newFile === null) {
            return;
        }

        var formData = new FormData();
        formData.append("newFile", newFile);
        $.ajax({
            url: "/api/Files/Upload",
            type: "POST",
            data: formData,
            cache: false,
            contentType: false,
            processData: false
        })
        .done(function (fileUrl) {
            var fileHtml = "<a href=\"" + fileUrl + "\">File</a>";
            var editor = $kendoTextArea.data("kendoEditor");
            editor.exec("inserthtml", { value: fileHtml });
        })
        .always(function () {
            $fileUploadInput.val("");
        });
    });
    
    $kendoTextArea.kendoEditor({
        tools: [
            "bold",
            "italic",
            "underline",
            /* ...Other toolbar items... */
            "createLink",
            "unlink",
            {
                name: "uploadFile",
                tooltip: "Upload a File",
                exec: function () { $fileUploadInput.trigger("click"); }
            }
        ]
    });
})(jQuery);

Possibilities for Expansion

This is a bare-bones demonstration of how you can take a custom-developed feature and, by extending Kendo UI editor functionality, make it easily-accessible and useful to your users. There is plenty of room for expansion or improvement as your needs see fit. We could return the name of the file as well as the URL and use that as the text for the hyperlink, or even prompt the user for what they want to use for the hyperlink text, for instance. Overall, though, this should be enough to get you started to extending Kendo UI editor functionality!

The post Extending Kendo UI Editor Functionality in Sitefinity appeared first on Falafel Software Blog.

Kendo UI: Prevent Event Bubbling in Mobile ListView

$
0
0

Today I was working on a Kendo UI Mobile application and I ran into a situation where I had a ListView widget and I wanted to handle the click event on the widget itself as well as having buttons in each ListView item and handle their click events.  Right away I knew I would have to stop the event from bubbling up when one of the buttons was clicked.  This typically isn’t a problem, I would just use the stopPropagation function that comes along with the event…but I quickly found that Kendo UI widget events do not provide the stopPropagation method.  In order to get around this limitation I had to keep track of whether or not I wanted to prevent event bubbling on each click of a button.

Here is an example:

<html>
<head>
    <title>Prevent Event Bubbling in Kendo UI Mobile ListView</title>

    <link rel="stylesheet" href="http://cdn.kendostatic.com/2014.3.1314/styles/kendo.common.min.css">
    <link rel="stylesheet" href="http://cdn.kendostatic.com/2014.3.1314/styles/kendo.mobile.all.min.css">

    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="http://cdn.kendostatic.com/2014.3.1314/js/kendo.all.min.js"></script>
</head>
<body>

    <div data-role="view">
      <ul data-role="listview" id="foo" data-click="listViewClick">
        <li>
            <span>ListView Item </span>
            <button data-role="button" data-role="click" data-click="doNotPreventBubblingClick">Do Not Prevent Bubbling</button>
            <button data-role="button" data-role="click" data-click="preventBubblingClick">Prevent Bubbling</button>
        </li>
      </ul>
    </div>

    <script>
        var preventBubbling = false;

        function listViewClick(e) {
            if(!preventBubbling){
              alert('You clicked the ListView Item')
            } 
            else {
                preventBubbling = false;
            }
            
        }
        
        function doNotPreventBubblingClick(){
            alert("You clicked the 'Do Not Prevent Bubbling' button");
        }

        function preventBubblingClick(){
            alert("You clicked the 'Prevent Bubbling' button");
            preventBubbling = true
        }

        new kendo.mobile.Application("body");   
      
    </script>

</body>
</html>

Let’s break down what is going on here:

  • Line #24:  Variable to track whether or not event bubbling should be prevented
  • Line #36:  Standard button click handler.
  • Line #40:  Button click handler where I am setting preventBubbling = true
  • Line #26:  ListView widget click handler where I am checking if bubbling should be prevented, if it isn’t then continue handling the event, otherwise set preventBubbling = false

Although this isn’t as clean as using the events stopPropagation function it is a fairly minor and straightforward workaround.

The post Kendo UI: Prevent Event Bubbling in Mobile ListView appeared first on Falafel Software Blog.


Kendo UI: Easily share templates in ASP.NET MVC

$
0
0

Sharing Kendo UI templates among multiple pages in your web application is not a straightforward task.  The Kendo UI documentation does provide a detailed explanation of how to accomplish this but it requires an abundance of messy code.  Fortunately though, if you happen to be using ASP.NET MVC there is a much simpler way with the help of Partial Views.

 

First create a partial view that includes the templates:

<!-- Kendo templates used in multiple areas of the application -->

<script type="text/x-kendo-template" id="name_template">
    <div>#: lastName #, #: firstName #</div>
</script>

<script type="text/x-kendo-template" id="address_template">
    <div>#: street #</div>
    <div>#: city #, # state # # zip #</div>
</script>

 

 

And finally render the templates partial view in your page:

@{
    ViewBag.Title = "Home Page";
}

<div>
    <h1>People</h1>
    <div data-role="kendolistview" data-bind="source: people" data-template="name_template"></div>
</div>

@{ Html.RenderPartial("_KendoTemplates"); }

@section scripts {
    <script type="text/javascript" src="/Scripts/home_page.js"></script>
}

 

 

The post Kendo UI: Easily share templates in ASP.NET MVC appeared first on Falafel Software Blog.

Kendo UI Map Visualizations with Bar Charts

$
0
0
I have been having some fun with Kendo UI map visualizations recently by combining CSS3 styling with custom marker elements. Applying custom data-based HTML elements to the map opens up endless possibilities for mapping, and the Kendo UI map makes it really straightforward. If your data has a geographic component, then viewing it on a map is a powerful visualization.
The key to these bar “charts” is the use of a box-shadow to give the 3D effect. For any color, I found that a base color for the forward-facing rectangle, and then a shade of the same color but 25% darker on the box shadow makes a nice shadow side. I used a simple color picker to help.
 bars
Then I did a little fine-tuning on my visualization before trying to apply it to the map markers, since we have to manually construct the html element string to do the actual mapping. It’s also a good idea to keep the visualization simple, both to decrease the possibility of errors when adding it to the markers, and to keep the map drawing nice and fast. The more points you map, the more effect it will have.

Here’s what I came up with:

The next piece here is the extension of the map point to include the metadata that will be used to generate the bars. We don’t just want to hard-code these values, we want to be able to generate the marker’s bar height according to any data set. You can see here that a three-element array contains the count data, and I am manually adding two points to the markers collection. This data could just as easily be fetched from a remote service, then added to the markers collection  after the map is already initialized. I also found that a maximum height of about 200 pixels looks nice on the map, so it is a good idea to normalize the data if you have very large or very small values to plot.
Bar map
And that’s all there is to it. The bars are plotted as the content of the marker elements, and so we get a nice bar chart effect on the map just through the magic of CSS and the Kendo UI Map.

Here is the full example:

The post Kendo UI Map Visualizations with Bar Charts appeared first on Falafel Software Blog.

Kendo Grid Multi-Level Headers

$
0
0

Kendo Grid Multi Level Headers LegoOne thing I really like about using Kendo UI is the frequency of updates and the number of new or improved features in each release. I don’t always have the time to read all of the release notes, so it is a nice surprise when I discover something I didn’t know about. This isn’t a brand-new feature, but a new discovery for me this week has been multi-column headers. I remember having the requirement a few years ago to build a grid with group headers that spanned more than one column. It was certainly possible, but it took more templating and styling work than I preferred.

When the requirement came up in a recent project, I was happy to find that it is now built in to Kendo. And thanks to the dynamic nature of JavaScript, this is implemented using the same columns property but you include an array instead of an object where necessary. Rather than using a single array of objects that have field/name properties, you can nest column arrays to get the layout you need:

[
    { field: 'name', title: 'Name' },
    { title: 'Details',
      columns: [
        { title: 'Basic Info',
            columns: [
              { field: 'height', title: 'Height' },
              { field: 'mass', title: 'Mass' }
            ]
         },
        { title: 'Detailed Info',
            columns: [
              { field: 'hair_color', title: 'Hair Color' },
              { field: 'eye_color', title: 'Eye Color' }
            ]},
        { field: 'birth_year', title: 'Birth Year' }
      ]
    }
]

Which, using our handy Star Wars API gives us this:

StarWarsMultiHeaderGrid

From here, you can apply some CSS if you want to center and shade any headers that span more than 1 column (if the colspan attribute exists in the th element, we know it is a multi-column header):

.k-grid-header table thead th[colspan] {
  text-align: center;
  background-color: #e5e5e5;
}

You can see the full code in this JSFiddle. I am using declarative syntax, so the grid definition is in the HTML tab.

The post Kendo Grid Multi-Level Headers appeared first on Falafel Software Blog.

Kendo UI Window with Custom Styling

$
0
0

I use the Kendo UI Window widget quite often. It works nicely with MVVM initialization, it matches my theme, and almost everything I need is configurable. I said almost. If you have ever tried to custom style the wrapper for the window, you may have had to do this fun workaround:

$('#myWindow').parent().addClass("myWindow");

 

So, we can’t do anything to the window wrapper until after the window is created, which is OK but I think we can do better. Let’s do this with an extended widget. After all, I can use that extended widget to add other ‘extras’ later. Anything you find yourself configuring repeatedly, you can add to this widget plugin and get a default Kendo UI Window that is exactly the same every time you use it. Or, different, if that suits your needs.

I recommend taking a look at the widget extension documentation, and then starting with this simple example:

function(){

var customwindow = kendo.ui.Window.extend({
                options: {
            name: "customwindow"
        },
        init: function (element, options) {
          var that = this;
      var customClass = $(element).data('customClass');
          kendo.ui.Window.fn.init.call(that, element, options);
          if (customClass) {
                         that.wrapper.addClass(customClass);
                      }
        }
        });
kendo.ui.plugin(customwindow);

})();

 

All this does is create our widget plugin CustomWindow and let us add a custom class, which we read from a custom data attribute on the element. Then the class is added to the wrapper in the initialization. How does that look in use? Like this:

<div id="win2"  data-role="customwindow" data-custom-class="customWindowClass" data-title="Window 2" data-width="300" style="display:none" data-height="160"  >
<span>my other window content</span>
</div>

 

And now I can apply custom styling to my new class, which will let me easily change the wrapper, including the title bar. Sure, who wouldn’t want a pink title bar? We can compare it to the normal window:
window
Here is the complete example for you to try on your own, in case you don’t like the pink:

The post Kendo UI Window with Custom Styling appeared first on Falafel Software Blog.

Kendo Grid Custom Column Sort

$
0
0

In my previous post, I demonstrated a simple natural sort that could be used to generate a sortable string that has mixed letters and numbers by padding the numeric portions of the string. As demonstrated in that post, alphanumeric strings may not sort the way users expect if it uses the standard ASCII sort instead of a natural sort:

// ASCII sort
    a1
    a11
    a2
    b100
    b19
 
    // natural sort
    a1
    a2
    a11
    b19
    b100

While it is true that we could implement our own IComparer in C#, by using a string extension method, we can generate a sort-friendly string that can easily be used outside of your C# service. In this post, I’ll show you how to use that sortable string to sort data in a Kendo grid with JavaScript.

First, let’s take a look at what the default sort would look like on an alphanumeric column:

Kendo UI Grid with Natural Sort

As you can see, our Product Code column is sorted ascending, but A2X comes after A11X, which is a valid ASCII sort, but our users may expect A2 to come before A11. To use a natural sort, we can use our natural sort string extension to include the sortable string in the service call. Yes, this does involve returning additional data, but it allows you to have a single method that could be used application-wide for this scenario. If you are working with long strings or large sets of data, you may want to consider using a natural sort function within JavaScript rather than relying on a pre-built sortable string. Using the server-side sortable string, our returned JSON data would look something like this:

[
   {
      productCode:"A11X",
      sortableCode:"A0011X",
      name:"Stapler",
      unitPrice:11.99
   },
   {
      productCode:"A2X",
      sortableCode:"A0002X",
      name:"Calculator",
      unitPrice:15.49
   },
   {
      productCode:"B31",
      sortableCode:"B0031",
      name:"Power Strip",
      unitPrice:13.29
   },
   {
      productCode:"B300",
      sortableCode:"B0300",
      name:"Deluxe Power Strip",
      unitPrice:25.49
   },
   {
      productCode:"B7CB",
      sortableCode:"B0007CB",
      name:"Clipboard",
      unitPrice:2.49
   },
   {
      productCode:"B71A",
      sortableCode:"B0071A",
      name:"Bulk Paper",
      unitPrice:14.49
   }
]

You’ll notice the leading zeros in the sortableCode field on the numeric groups of digits which will allow us to sort our data easily in the Kendo grid.

Next, we need to have the grid display the productCode value, but use the sortableCode property for sorting. To do this, we’ll use the sortable compare function in our grid declaration:

$('#grid').kendoGrid({
    dataSource: viewModel.gridData,
    sortable: true,
    fitlerable: true,
    columnMenu: true,
    columns: [
      { field: 'productCode', title: 'Product Code', sortable: { compare: viewModel.productCodeNaturalSortCompare } },
      { field: 'name', title: 'Product Name' },
      { field: 'unitPrice', title: 'Unit Price', format: '{0:c}' },
    ]
  });

productCodeNaturalSortCompare is a function that uses JavaScript’s localeCompare() method to compare a different property than what we are displaying in the grid: the sortableCode property.

productCodeNaturalSortCompare: function(a, b) {
        return a.sortableCode.localeCompare(b.sortableCode);
    }

With these changes in place, our grid now shows the original Product Code, but the grid now sorts ascending and descending according to our sortableCode string, which looks like this:

Kendo UI Grid with Natural Sort

Now A2 comes before A11 and B31 comes before B300.

View the full source in a JSFiddle here:

The post Kendo Grid Custom Column Sort appeared first on Falafel Software Blog.

Kendo UI Grid Templates with Directional Icons

$
0
0
I use the Kendo UI Grid. I use it a lot, in fact. And one thing I’ve found is that tabular data, in its pure form with columns and rows is, well, boring. But with column templates, you can add visualizations inside the grid itself. Sure, you probably already knew that adding a visualization makes for a better user experience. But did you know it could be so easy?

Using Templates For Easy Visualizations

The Kendo UI templating syntax really opens up possibilities. You can combine data with CSS, include calculations, even combine data properties to create really informational displays.
Let’s look at a small example. Imagine a dataset that includes directional data. Say, wind speeds and headings. Or driving directions with headings. What can you do with those numeric headings? What is more useful, a value of 90, or an arrow pointing East?
 2016-06-29 00_52_26-Kendo UI Grid with Directional Arrows - JSFiddle
Well, we can do that arrow with a super-simple column template and a rotational transform. Here I am using a FontAwesome arrow and rotating it the amount in degrees of the numeric heading value.
<script id="arrowHeadingTemplate" type="text/x-kendo-template">
<span>
<i style="color: green;
                    transform: rotate(#= Heading #deg); 
                    -ms-transform: rotate(#= Heading #ddeg);
                -webkit-transform: rotate(#= Heading #ddeg)" 
          class="fa fa-arrow-circle-up"></i>
</span>
</script>
Notice the use of the #= # syntax, which lets you apply data values into the generated HTML (or CSS, in this case).
Then you just have to apply the template to a column in the Kendo UI Grid. Notice that the arrow column has no field declared. You don’t need to set the field value if you are accessing the values inside a template instead.
columns : [
                          {field: 'HeadingText',
                          title: 'Heading Text'},
                          {field: 'Heading',
                          title: 'Heading Numeric'},
                          {title: 'Arrow',
                          template: kendo.template($('#arrowHeadingTemplate').html())}
                          ]
And the result? Rotational arrows within our grid!
2016-06-29 00_45_27-Kendo UI Grid with Directional Arrows - JSFiddle

Here’s the full example you can try out for yourself:

The post Kendo UI Grid Templates with Directional Icons appeared first on Falafel Software Blog.

Kendo UI Customization with CSS

$
0
0

kendo css heart

I’ve been using Kendo UI for many years now and I continue to be impressed with the many ways it can be extended and customized. Recently, I had a client request a Kendo Grid customization that at first glance seemed like it might require some code changes, but in the end, it could all be done purely with CSS. Here is the scenario as a CSS customization example: by default, the Kendo Grid shows the sort icon immediately after the header text and when using the column menu with a filter applied, it highlights the border around the column menu chevron, like this:

kendo css 01

This works great, but the client wanted a consistent look with other applications where a filtered state should show a filter icon in the header and the sort indicator should show above the header text so it isn’t hidden when the column is sized smaller than the header text. This is what they wanted:

kendo css 02

Fortunately, we can do this entirely with CSS! First, the filter icon.

Custom Filter Icon

When I’m customizing a Kendo element with CSS, I’ll start by running an example in the Kendo UI Dojo and use Chrome Dev Tools to inspect the element and see which elements and CSS classes are involved.

kendo css filter dev tools

I’ve highlighted the element of interest: the <a> tag for the column menu. We ONLY want this customization for grids that are using the column menu and we only want our custom filter icon to show if the filter is active, so our CSS selector needs to have both .k-header-column-menu and .k-state-active. Our CSS will remove the active background color and instead, insert our custom filter icon before the column menu icon using the ::before CSS pseudo-element selector. I am already using FontAwesome in my project, so I’ll be using their filter icon.

<aside>Telerik, please consider providing optional sprite images with a transparent icon so we can apply any background color.</aside>

And here is the full CSS for our custom filter icon:

/* Custom funnel icon when filtering */
      .k-grid-header a.k-header-column-menu.k-state-active {
        background-color: transparent;
        text-decoration: none;
      }
      .k-grid-header a.k-header-column-menu.k-state-active::before {
          content: '\f0b0'; /* funnel icon */
          font-family: FontAwesome;
          color: orange;
          padding-right: 2px;
      }

Now, let’s customize the grid sort indicator.

Custom Sort Icon

We will follow the same steps and first inspect the existing filter icon to see how it works:

kendo css sort dev tools

As you can see in the above image, Kendo uses a sprite background-image along with background-position to show the standard sort icon. Since we want to apply a custom color to the sort indicator (and because Kendo’s sprite images use a specific color rather than inverted transparency to allow us to set our own background-color) we will need to remove the standard sprite background-image and add our own, again using FontAwesome. We also need to specify separate images/icons for both ascending and descending sort. Fortunately, Kendo applies a different style for each, so we can use .k-i-arrow-n and .k-i-arrow-s as selectors for the up and down icons. And last, we want to position the indicator above the header text rather than beside it, so we will set the .k-link container element to use relative position and the .k-icon element to use absolute position. This will float it above the header text.

/* Custom sort icon above header text */
      .k-grid-header .k-header .k-link {
          position: relative;
      }      
      .k-grid-header .k-header .k-link > .k-icon {
          position: absolute;
          top: 0px;
          left: 0px;
          width: 100%;
          background-image: none; /* remove Kendo's sprite image */
          font-family: FontAwesome;
          color: #0177bf;
          font-size: 16px;
          line-height: 10px;
      }      
      .k-grid-header .k-header .k-link > .k-icon.k-i-arrow-s::before {
          content: '\f0d7'; /* FontAwesome down arrow */
      }
      .k-grid-header .k-header .k-link > .k-icon.k-i-arrow-n::before {
          content: '\f0d8'; /* FontAwesome up arrow */
      }

Here is a link to a Kendo UI Dojo with these changes that you can try out for yourself. If you use FontAwesome, be sure to include their library as I have done in this example.

Custom Grid Sort and Filter – Kendo UI Dojo

Note: NEVER make changes to the original kendo CSS files. You will lose all of your customizations the next time you update to the latest version of Kendo. Instead, put your custom styles in either your main CSS file or create something like a custom-kendo.css (or sass, less) file. There still may be breaking changes with new Kendo releases, but at least your changes won’t be completely wiped out when you update.

The post Kendo UI Customization with CSS appeared first on Falafel Software Blog.


KendoUI Grid Excel Export Automatic DateTime Formatting

$
0
0

If you are working with tabular data, then chances are your users want to see it in a grid. And in my experience if you put the data in a grid, those users are going to export that data to Excel! Of course. Well, if you were lucky enough to be using a Kendo UI Grid, then the pain of exporting that grid data to an Excel spreadsheet is a lot less….painful.

If you haven’t already tried out the Kendo UI Grid Excel Export support, you really should.

So, how about a specific example, with my all-time favorite grid data type: dates. Just kidding, date columns are never my favorite, but they do end up being pretty darn important to users who have time-related data.

Exporting DateTime Columns

So, take your everyday date column in a Kendo grid. You’ve added some special formatting, because not only do we have a date, we also have time data. In fact, let’s say two columns, different formats.

$("#grid").kendoGrid({
                        dataSource: {
                        schema: {
                          model: {
                            fields: {
                              dateSample1: {type: 'date'},
                              stringSample1: {type: 'string'},
                              stringSample2: {type: 'string'},
                              dateSample2: {type: 'date'}
                              }
                           }
                        },
                            data: [
                                                    {
                              dateSample1:kendo.parseDate("12/22/2000 9:00 AM"), 
                              stringSample1: "cat", 
                              stringSample2: "fluffy", 
                              dateSample2:kendo.parseDate("12/22/2000 10:00 AM")
                              }, 
                              {
                              dateSample1:kendo.parseDate("12/23/2000 11:00 AM"), 
                              stringSample1: "cat", 
                              stringSample2: "fluffy", 
                              dateSample2:kendo.parseDate("12/22/2000 12:00 AM")
                              }
                              ]
                          },
                          columns : [
                          {field: 'dateSample1',
                          format: "{0: yyyy-MM-dd HH:mm}"},
                          {field: 'stringSample1'},
                          {field: 'stringSample2'},
                          {field: 'dateSample2',
                          format: "{0: MM/dd/yyyy h:mm:ss tt}"}
                          ],
...

And we can see our formats are working correctly:

2016-07-15 16_27_26-Kendo UI Grid Excel Export With Dates - JSFiddle

Great, so we wire up our handy Export to Excel functionality, which takes all of about 2 minutes, and export.

$("#exportButton").kendoButton({
 click: function(e){
 var grid = $("#grid").data("kendoGrid");
            grid.saveAsExcel();
 }
 });

2016-07-15 16_13_50-Export.xlsx - Excel

What happened? That default column formatting in Excel just clobbered our time-inclusive formatting! Not cool!

Formatting using the Model Types

Ok, but in the link above, we can see how to format certain cells, and even add a date/time format. Perfect. Except I don’t want to handle it by column index, because what if users re-order the columns? Let’s add detection of the column type using the model of the Kendo DataSource. And, since we are referencing the column, we can include our own property for the export format. In case you are wondering, no, the same format string won’t work for both display and export – notice the difference in the AM/PM format specifiers.

$("#grid").kendoGrid({
                        dataSource: {
                        schema: {
                          model: {
                            fields: {
                              dateSample1: {type: 'date'},
                              stringSample1: {type: 'string'},
                              stringSample2: {type: 'string'},
                              dateSample2: {type: 'date'}
                              }
                           }
                        },
                            data: [
                                                    {
                              dateSample1:kendo.parseDate("12/22/2000 9:00 AM"), 
                              stringSample1: "cat", 
                              stringSample2: "fluffy", 
                              dateSample2:kendo.parseDate("12/22/2000 10:00 AM")
                              }, 
                              {
                              dateSample1:kendo.parseDate("12/23/2000 11:00 AM"), 
                              stringSample1: "cat", 
                              stringSample2: "fluffy", 
                              dateSample2:kendo.parseDate("12/22/2000 12:00 AM")
                              }
                              ]
                          },
                          columns : [
                          {field: 'dateSample1',
                          format: "{0: yyyy-MM-dd HH:mm}",
                          exportFormat: "yyyy-MM-dd HH:mm"},
                          {field: 'stringSample1'},
                          {field: 'stringSample2'},
                          {field: 'dateSample2',
                          format: "{0: MM/dd/yyyy h:mm:ss tt}",
                          exportFormat: "MM/dd/yyyy h:mm:ss AM/PM"}
                          ],
                          excelExport: function(e) {
                    var grid = $("#grid").data("kendoGrid");
                            if (grid) {
                              // get the date columns from the datasource
                              var dateColumnList = [];
                              var fields = grid.dataSource.options.schema.model.fields;
                              // only check visible columns
                              var visibleColumns = grid.columns.filter(function(col) { return col.hidden !== true });
                              visibleColumns.forEach(function (col, index) {
                                var fieldName = col.field;
                                // find matching model
                                var match = fields[fieldName];
                                // determine if this is a date column that will need a date/time format
                                if (match && match.type === 'date') {
                                  // give each column a format from the grid settings or a default format
                                  dateColumnList.push(
                                  {
                                  i: index, 
                                  format: col.exportFormat ? col.exportFormat : "MM/dd/yyyy HH:mm:ss "
                                  });
                                }
                              });
                              var sheet = e.workbook.sheets[0];
                              for (var rowIndex = 1; rowIndex < sheet.rows.length; rowIndex++) {
                                var row = sheet.rows[rowIndex];
                                // apply the format to the columns found
                                for (var cellIndex = 0; cellIndex < dateColumnList.length; cellIndex++) {
                                  var index = dateColumnList[cellIndex].i;
                                  row.cells[index].format = dateColumnList[cellIndex].format;
                                }
                              }
                            }
                                                }
                           });

Now, we can export the grid and use either the column-specified date/time format, or let a default format apply that will at least show the time when opened in Excel. No hard-coded column index needed.

2016-07-15 16_13_34-Export (2).xlsx - Excel
Here’s the full example you can try out for yourself:

The post KendoUI Grid Excel Export Automatic DateTime Formatting appeared first on Falafel Software Blog.

Kendo UI Grid with Two Kinds of Checkboxes

$
0
0

In my last blog, I showed some examples of dates in a Kendo UI Grid. This time, let’s look at checkboxes. True/False data in the Kendo UI Grid is always interesting to work with. A checkbox makes sense for boolean values, but checkboxes come with some expectations from the user’s point of view. They see a checkbox, and they want to be able to click it! Entering an Edit mode and then clicking the checkbox just doesn’t cut it.

But getting the checkbox just right inside the grid isn’t as easy as it sounds.

Editing without Edit Mode

Ideally, we want the user to be able to click the checkbox, see the checkmark appear, and have the underlying data item property value reflect the new value. No edit mode, no additional clicks, just the same basic operation as a checkbox appearing anywhere else in the page.

Telerik’s documentation provides a starting point for a simple editable checkbox. We know we can create a checkbox column using a template, which in this case I have put inline in the grid declaration.

$("#grid").kendoGrid({
                        dataSource: {
                        schema: {
                          model: {
                            fields: {
                                name: {type: 'string'},
                              checkSample1: {type: 'bool'},
                              checkSample2: {type: 'bool'}
                              }
                           }
                        },
                            data: [
                                                    {
                              name: "California",
                              checkSample1: true, 
                              checkSample2: true
                              },
                              {
                              name: "Colorado",
                              checkSample1: false, 
                              checkSample2: true
                              }, 
                              {
                              name: "Florida",
                              checkSample1: true, 
                              checkSample2: false
                              }, 
                              {
                              name: "Texas",
                              checkSample1: false, 
                              checkSample2: false
                              }
                              ]
                          },
                          columns : [
                          {field: 'name'},
                          {field: 'checkSample1',  template: '<input type="checkbox" #= checkSample1 ? \'checked="checked"\' : "" # class="chkbx" />', width: 130 }]
...

Next we need a handler to translate those checkbox events to the data items

// from Kendo documentation for checkbox editing, 
                          // modified for multiple checkbox columns
                          $("#grid .k-grid-content").on("change", "input.chkbx", function(e) {
                            var grid = $("#grid").data("kendoGrid");
                            var checked = $(this).is(':checked');
                            var col = $(this).closest('td');
                            dataItem = grid.dataItem($(e.target).closest("tr"));
                            dataItem.set(grid.columns[col.index()].field, checked);
                            log(dataItem);
                          });

Ok, that’s great, now we have clickable checkboxes.

2016-07-31 17_31_58-Kendo UI Grid with Custom Checkbox - JSFiddle

Taking it Further

But the visual experience of a default checkbox leaves a little to be desired. I think we can do better. Our own Falafel blog archive includes an example for a custom Kendo checkbox widget. Can we combine all of these bits and pieces to get custom checkboxes in our Kendo UI Grid?

Yes, yes we can.

2016-07-31 17_41_04-Kendo UI Grid with Custom Checkbox - JSFiddle

To generate the widget checkboxes, we can use the databound event, and we can also handle the change event of the generated widget to set the value of the underlying data item.

dataBound:  function(e) {
                          // create the custom checkboxes from the template
                              e.sender.tbody.find('.grid-check-cell').each(function (cell) {
                                 var cb = $('input.select-checkbox', cell).kendoCustomCheckbox({
                                         checked: cell.value,
                                     label: '',
                                     change: function(e) {
                                        var grid = $("#grid").data("kendoGrid");
                                        var checked = $(this.element).is(':checked'),
                                                                    row = $(this.element).closest("tr"),
                                                                    dataItem = grid.dataItem(row);
                                        var col = $(this.element).closest('td');
                                        var field = grid.columns[col.index()].field;
                                        dataItem[field] = checked;
                                        log(dataItem);
                                      }
                                 });
                              });
                          }

We will use a separate template this time, but the concept is the same.

<script id="checkColumnTemplate" type="text/x-kendo-template">
    <span class="grid-check-cell">
        <input class="select-checkbox" #= checkSample2 ? 'checked="checked"' : "" # />
</span>

Here’s the full example you can try out for yourself:

The post Kendo UI Grid with Two Kinds of Checkboxes appeared first on Falafel Software Blog.

Kendo UI Grid with Custom Checkboxes: Take 2

$
0
0

In my last post, Kendo UI Grid with Two Kinds of Checkboxes, I showed how to use a custom Kendo widget ‘Checkbox’ in a boolean grid column. Since then, I’ve been working with a few similar grids and I found that my original approach works well, but only if you don’t have edit mode enabled for the rest of the grid.

Well, nothing like a little problem to inspire a new post!

The Problem

So, what’s going on? Well, edit mode in the Kendo UI Grid overwrites the Kendo Widget that we created using our databound event. So, when the user clicks not on the checkbox, but next to it (or tabs to the cell in edit mode), the default editor is displayed instead of our lovely checkbox:

2016-08-17 22_14_09-Kendo UI Grid with Custom Checkbox - JSFiddle

Not good! Time to update our approach.

The Solution

I tried several workarounds for this, but what I finally settled on is to move the creation of the checkboxes out of the databound event altogether, and into a template function. Then we can use this function to create both the normal and edit mode elements, and ensure that to the user, they look the same.

If this looks familiar, it’s because it is basically a modified version of the widget creation code used for the widget version of the checkbox. We create the elements that will result in the input-plus-label setup that will display nicely with our svgs.

createCheckboxWrapper : function(options, field){
                           // generate the checkboxes in a function
                            var isChecked = options[field];
                            var input = $("<input type='checkbox' class='select-checkbox' />");
                            input.attr('checked', isChecked);
                            input.bind('change', function (e) {
                                var checked = $(this).is(':checked');
                                var grid = $(this).closest(".k-grid").data("kendoGrid");
                                var dataItem = grid.dataItem($(this).closest('tr'));
                                var col = $(this).closest('td');
                                dataItem.set(grid.columns[col.index()].field, checked);
                            });

                            var wrapper = input.wrap('<span class="custom-k-checkbox"></span>').parent();
                            input.after('<label class="cblabel">' + '</label>');
                            input.siblings('label').click(function (e) {
                                input.click();
                            });

                            return wrapper;
                          },

Then, we call this function as part of our column initialization:

{field: 'checkSample2',  
                          editor: function (container, options) {
                             var wrapper = viewModel.createCheckboxWrapper(options.model, 'checkSample2');
                              wrapper.wrapAll('<div>').parent().appendTo(container);
                          },
                          template:function(options){
                            var wrapper = viewModel.createCheckboxWrapper(options, 'checkSample2');
                            return wrapper.wrapAll('<div>').parent().html();
                          }}

And now, there’s no delay for the visuals, and no amount of clicking or entering/exiting edit mode will show anything but the ‘pretty’ checkboxes.

Here is our edit mode now:

2016-08-17 23_01_38-Kendo UI Grid with More Custom Checkbox - JSFiddle

Much, much better.

Here’s the full example you can try out for yourself:

If you like this post, check out some of Falafel’s other blogs for KendoUI tips and problem-solving.

The post Kendo UI Grid with Custom Checkboxes: Take 2 appeared first on Falafel Software Blog.

Kendo UI Tabstrip with Slanted Tabs

$
0
0
Here’s another fun tip for Kendo UI developers – a tabstrip with slanted tabs. Did you know you can put additional elements into a Kendo UI tab? Let’s look at how to use an additional element to add a slant to your tabs.
First, start with a basic tabstrip:
<div data-role="tabstrip" data-animation="false">
                            <ul>
                                <li class="k-state-active">
                                    First Tab
                                </li>
                                <li>
                                    Second Tab
                                </li>
                            </ul>
                            <div>
                                <span>Tab 1 Content</span>
                            </div>
                            <div>
                                <span>Tab 2 Content</span>
                            </div>
                        </div>

Nothing fancy here, just tabs with some dummy content.

2016-09-10-14_29_21-kendo-ui-slanted-tabs-jsfiddle

Then add an additional element inside the tab itself (not the content). This will be the “arrow” or the slanted side of the tab. Since the tabstrip is created using  the <li> element, we can add our arrow inside.

<li class="k-state-active">
    First Tab
    <div class="arrow"></div>
</li>

Now, to make it a slant, we will use some CSS magic using borders to create triangles.  Really, it is a box with 0 width and height, and thick borders visible only on two sides, which gives us the triangle look. Neat!

2016-09-10-14_36_47-kendo-ui-slanted-tabs-jsfiddle

border-color: transparent transparent #ccc #ccc;
 border-style: solid;
 border-width: 18px 15px 17px 15px;
 height: 0;
 width: 0;

We just need position the arrow correctly so that it extends the tab container with a bit of overlap. And now we have slanted tabs.

.arrow {
    border-color: transparent transparent #fff #fff;
    border-style: solid;
    border-width: 18px 15px 17px 15px;
    height: 0;
    width: 0;
    position: absolute;
    bottom: 0px;
    right: -29px;
}

2016-09-10-14_30_27-kendo-ui-slanted-tabs-jsfiddle

And the best part is that the Kendo UI styling takes care of the style changes and z-index changes needed to make the tab selection look nice.

Here’s the full example you can try out for yourself:

If you like this post, check out some of Falafel’s other blogs for KendoUI tips and problem-solving.

The post Kendo UI Tabstrip with Slanted Tabs appeared first on Falafel Software Blog.

Kendo UI Combobox Customized: Previous Value Restore

$
0
0

Today I want to share a little Kendo UI Combobox customization, one that I hope you’ll find useful: restoring the previous value on an invalid entry. First, why would we want to do that?

Often we see validation, especially numeric validation on a range for example, use coercion to simply force the value to either the highest or lowest number. This certainly prevents invalid entry, but the user experience isn’t all that great in every scenario. What may be preferred, for an allowed range of 0-10, and if the user intends to change a 3 to a 4 and instead accidentally enters 44, would be to just restore the value of 3 (not coerce to 10).

In this case we are looking at a combobox, but the concept could be applied to other widgets. We can restore the previous value if we add a Previous field in the widget options on initialization. This field is then used to store only the most recent valid saved entry.

$("#combobox").kendoComboBox({
   dataSource: {
     data: [
       {Text:'Zero', Value: 0}, 
       {Text:'Test One', Value: 1}, 
       {Text:'Test Two', Value: 2}, 
       {Text:'Test Three', Value: 3}, 
       {Text:'Test Four', Value: 4}
     ]
   },
   filter: 'contains',
   dataTextField: 'Text',
   dataValueField: 'Value',
   template: kendo.template($("#template").html()),
   previous: ""
 });

Then, we interrupt the save (here using a button), to check our range and upon failure, restore the previous value. If the change passes, set the Previous field to the new value and repeat.

$("#saveButton").kendoButton({
 click: function(e){
        var cb = $("#combobox").getKendoComboBox();
        // check the combobox value
        var newValue = cb.value();
    var newInt = parseInt(newValue);
    if (!isNaN(newInt) && newInt >= 0 && newInt <= 10){
                cb.value(newInt);
        cb.setOptions({previous: newInt})
     }
     else{
     // restore the previous value
        var prev = cb.options.previous;
        cb.value(prev);
    }

        var ot = $('#savedValue');
        var comboboxValue =  cb.value();
        ot.html(ot.html() + '<br/>' + 'value: ' + comboboxValue );
    
 }
 });

In my example, you can see below the widget the save values, and how on invalid entry (such as abc), the previous value is restored.

2016-10-09-15_08_14-kendo-combobox-for-headings-jsfiddle

One thing to note: if your page utilizes Kendo Validation, you wouldn’t be saving the invalid value anyway, and the user would be notified by a validation error message instead of coercion. But not every page nor every entry scenario involves validation errors and messages. This is an alternative for cases where full-blown validation is not desired, or can be applied as part of a custom validation rule operation.

Here’s the full Kendo UI Combobox example you can try out for yourself:

The post Kendo UI Combobox Customized: Previous Value Restore appeared first on Falafel Software Blog.

Viewing all 43 articles
Browse latest View live