Formatting JSON object as string.


Just a little trick… when you need to format a JSON object for displaying it you might use JSON.stringify and there is a third argument that is the space. If you write JSON.stringify(obj, null, ‘\t’) if prints each element preceded by a tabulator.

The space might also be a number and then the text is indented with the specified number of white spaces at each level.

Example:

var obj = {
    field1: 123.5,
    field2: true,
    field3: "Hello, OnaBai!",
    field4: [ "Working", "in", "a", "cloud", "of", "documents" ],
    field5: {
        field51: "I'm nested"
    },
    field6: function() {
        alert("Hello, OnaBai");
    }
};
console.log(JSON.stringify(obj, null, 2));

Shows:

{
  "field1": 123.5,
  "field2": true,
  "field3": "Hello, OnaBai!",
  "field4": [
    "Working",
    "in",
    "a",
    "cloud",
    "of",
    "documents"
  ],
  "field5": {
    "field51": "I'm nested"
  }
} 

NOTE: As you can see functions are not displayed!!!

More information here and here.

Advertisements

KendoUI: Selectable Widget


Continuing a series of KendoUI widgets (this and this)… This one allows you to mark as selected HTML objects and then ask for which one are actually selected.

As per se, this is not a lot but this is actually used by another widgets and you can find it useful as well.

KendoUI: selectable widget

Selectable widget: source code

/*
 * Copyright (c) 2012. OnaBai Software Solutions.  All rights reserved.
 * If you do not own a commercial license, this file shall be governed by the
 * GNU General Public License (GPL) version 3.
 * For GPL requirements, please review: http://www.gnu.org/copyleft/gpl.html
 */

(function ($) {
    var kendo = window.kendo,
        ui = kendo.ui,
        Widget = ui.Widget,
        CLICK = "click";

    var OBSelectable = Widget.extend({
        init       :function (element, options) {
            var that = this;

            Widget.fn.init.call(that, element, options);
            element = that.element;
            options = that.options;
            var items;
            if (options.selector) {
                items = $(options.selector, that.element);
            } else {
                items = element.children();
            }
            items.bind(CLICK, $.proxy(that._click, that));
        },
        getSelected:function () {
            return $(".ob-selected", this.element);
        },
        options    :{
            name    :"OBSelectable",
            style   :"ob-status-selected",
            multiple:true,
            selector:null
        },
        events     :[
            CLICK
        ],
        _click     :function (ev) {
            if (!this.options.multiple) {
                this.getSelected().removeClass(this.options.style + " ob-selected");
            }
            $(ev.currentTarget).toggleClass(this.options.style + " ob-selected");
        }
    });
    ui.plugin(OBSelectable);
})(jQuery);

Selectable widget: usage

One element is selected by clicking on it, if you click on a selected element, then it is de-selected.

Selectable widget: configuration

style (string, default: ob-status-selected): Style (CSS class) applied to selected elements (clicked).

multiple (boolean, default: true): When true, you might select more than one item, otherwise selecting an item will cause the de-selection of any selected one.

selector: (string, default first level child nodes). If configured, selector allows you to mark as selectable nodes that are not the direct children of this.

Selectable widget: methods

getSelected: Returns list containing the elements that are marked as selected.

Selectable widget: usage example

The HTML:

<ol id="selectable">
    <li class="ob-item">Element 1</li>
    <li class="ob-item">Element 2</li>
    <li class="ob-item">Element 3</li>
    <li class="ob-item">Element 4</li>
    <li class="ob-item">Element 5</li>
</ol>

The JavaScript:

$("#selectable").kendoOBSelectable({
    selector:"[class='ob-item']",
    style:"selected",
    multiple:true
});

I define as selectable those node under the node with id selectable and with class ob-item. This would have been equivalent to say selector:”li” or not defining selector since all li elements area actually first level children of $(“#selectable”).

If I define the selected CSS class as:

.ob-selected {
    background-color: red !important;
    color: #d3d3d3 !important;
}

I get something like this when Element 1 and Element 3 are selected:

KendoUI selectable widget
KendoUI selectable widget

KendoUI: Sortable Widget


My friend kept challenging me about jQuery UI Sortable and my implementation of KendoUI Sortable Lists. He asked me to go a step further and implement a Sortable Grid that would allow to move cells in horizontal and vertical.

KendoUI Sortable Grid

KendoUI Sortable Grid: See it in action

I think that the very best is see what it can do:

But, it does not actually to be a regular grid (all cell with the same width):

Not even just static content elements, they might be other KendoUI widgets:

KendoUI Sortable Grid: Description

Basically you define an HTML element that contains group of HTML elements (rows). Each row contains a list of HTML elements (cells).

So, this widget is able to:

  • Move a cells inside a row or between rows.
  • If a row gets empty (no cells on it after a move), then the row is removed.
  • If you drop a cell in the space between rows (in the videos it has been styled as box in slightly darker border), then a new row is inserted with the element being dragged.

 

KendoUI: Sortable Lists


Few days ago a colleague of mine was complaining about KendoUI arguing that it does not provide sortable list as jQueryUI does (check this if you want to see it). I told him that it could easily be implemented using KendoUI Tree Views that might not look exactly the same but the functionality exists.

He challenged me to do it in a few lines of code. Wow! I love challenges…

Here is the result:

KendoUI Sortable Lists

First of all let’s define a list that I will display as a flat tree (one level) and I want to customize how to display each element so, I’ll be using KendoUI templates.

The HTML, the minimum HTML, just some place where to insert the list.

<div id="sortable"></div>

The JavaScript where I have my list:

var dataSource = [
    { id:1, text:"Element 1" },
    { id:2, text:"Element 2" },
    { id:3, text:"Element 3" },
    { id:4, text:"Element 4" },
    { id:5, text:"Element 5" },
    { id:6, text:"Element 6" }
];

And now the initialization of the list:

$("#sortable").kendoTreeView({
    dataSource :dataSource,
    template   :"<div class='ob-item'> #= item.text # </div>",
    dragAndDrop:true
});

I’m using template in order to provide a little styling to each item in the list (border, background and text color):

.ob-item {
    background-color: #e9e9e9;
    border: 1px solid #a99f9a;
    color: #2e2e2e;
    padding: 5px;
    border-radius: 4px;
}

With this, we already hava a list that is sortable BUT -and here is where my colleague was complaining about- is that we can drop one element into another and then having a multilevel tree (not flat).

Forcing flat tree

I need to do some little checking in order to guarantee that one item it is not dropped over an element. The easiest way (and I think that is not a bad solution) is inserting a node before (or after) the node where you drop it.

So, I write a drop event handler that will manage where to insert instead of delegating it in kendoTreeView.

drop       :function (e) {
    $(e.sourceNode).insertBefore(e.destinationNode);
    e.setValid(false);
}

What I do is insert sourceNode before destination node and invoke setValid with false in order to prevent default handler of doing the insertion.

Final tuning

But this is not perfect:

  • It is possible to drop a node on itself that would cause invoking insertBefore (and the node would get lost)
  • The visual feedback about where the element is going to be inserted is wrong.
drag       :function (e) {
    var dst = $($(e.dropTarget).closest(".k-item")[0]).data("uid");
    var src = $(e.sourceNode).data("uid");
    if ($(e.dropTarget).hasClass("ob-item") && dst != src) {
        e.setStatusClass("k-insert-top");
    } else {
        e.setStatusClass("k-denied");
    }
},
drop       :function (e) {
    if ($(e.sourceNode).data("uid") !== $(e.destinationNode).data("uid")) {
        $(e.sourceNode).insertBefore(e.destinationNode);
    }
    e.setValid(false);
}

For the visual feedback what I do is:

  1. Check that I’m dropping the element in top of another element ($(e.dropTarget).hasClass(“ob-item”))
  2. Check that source (node being moved) and destination (the node on which I’m dropping the source) are different (have different uid).

If both are true then I set status class (visual feedback) to k-insert-top that displays that it is going to be inserted before. Otherwise, I set the status class to k-denied to show that it is not a valid movement.

The complete final code is:

$("#sortable").kendoTreeView({
    dataSource :dataSource,
    template   :"<div class='ob-item'> #= item.text # </div>",
    dragAndDrop:true,
    drag       :function (e) {
        var dst = $($(e.dropTarget).closest(".k-item")[0]).data("uid");
        var src = $(e.sourceNode).data("uid");
        if ($(e.dropTarget).hasClass("ob-item") && dst != src) {
            e.setStatusClass("k-insert-top");
        } else {
            e.setStatusClass("k-denied");
        }
    },
    drop       :function (e) {
        if ($(e.sourceNode).data("uid") !== $(e.destinationNode).data("uid")) {
            $(e.sourceNode).insertBefore(e.destinationNode);
        }
        e.setValid(false);
    }
});

Conclusion

You can do it, it is not that hard. The functionality is the same but you might find jQueryUI Sortable more appealing.

KendoUI: ComboBoxes avoiding non-documented-features (bugs?)


I’ve already published some posts on how to use KendoUI ComboBox and how delighted I was (and I am) about it.
But, this morning I did a little change in the UI (just cosmetic and it stop working). Here I share with you the problem and how to avoid it but not solving it: this is KendoUI engineers job and they will not be happy if I do it for free 😀

First problem: List area wrong positioning

If the body of your HTML page has a border, then the position of the open list is wrong (read about this in the KendoUI forum).

NOTE: This is actually the second one that I found looking for the solution in reading KendoUI forums.

This is the code:

<script type="text/javascript">
    $(document).ready(function () {
        var numbers = [
            "One",
            "Two",
            "Three"
        ];

        $("#numbers").kendoComboBox({
            dataSource:numbers
        }).data("kendoComboBox");
    });
</script>

and this the HTML:

<body style="border: 50px solid red;">
    <div>
        <input id="numbers"/>
    </div>
</body>

This is how it looks like:

KendoUI ComboBox list when body has border
KendoUI ComboBox list when body has border

Solution

Don’t use borders in your body. Insert a div that contains that border… Sorry if you were waiting for something much smarter!

<body>
<div style="border: 50px solid red;">
    <div>
        <input id="numbers"/>
    </div>
</div>
</body>

And this is how it looks like.

KendoUI ComboBox list moving style from body to a div
KendoUI ComboBox list moving style from body to a div

Second problem: List area wrong positioning

The topic is the same since this is what I trying to solve when I found problem number 1.

Here, I was trying to set a div as container and looking similar to KendoUI popups. Something like this:

Styling a div to look like a KendoUI popup
Styling a div to look like a KendoUI popup

So I have defined the following HTML:

<div class="k-popup k-group" style="min-height: 200px; padding: 8px;">
    <div>
        <input id="numbers"/>
    </div>
</div>

And when I open the ComboBox:

Misplace ComboBox list
Misplace ComboBox list

As you can see it is not correctly positioned (a little left and up).

Solution

Despite of this information about KendoUI styling, you cannot use k-popup and k-group around ComboBoxes. You have to copy the style and define your own.

<div class="ob-popup" style="min-height: 200px; padding: 8px">
    <div>
        <input id="numbers"/>
    </div>
</div>

where ob-group is:

.ob-group {
    -webkit-box-shadow: rgba(0, 0, 0, 0.298039) 0px 2px 2px 0px;
    box-shadow: rgba(0, 0, 0, 0.298039) 0px 2px 2px 0px;
    background-color: #ebebeb;
    border: 1px solid #c5c5c5;
}

This is not a very nice solution since you need to check what k-popup and k-group are in your theme but at least it works.

Third Problem: (very) Wrong positioning

As I’ve already said, I’m curious and I like to do extensive testing before shipping a product / a solution / a patch / a piece of code… it saves me a looooot of time.

So, I prepare a test where I mixed two ComboBoxes, the first with the solution to problem 2 (use ob-group) and another without the solution (use k-group and k-popup).

This is the HTML code:

<div class="ob-group" style="min-height: 100px; padding: 8px">
    <div>
        <input id="numbers1"/>
    </div>
</div>
<div class="k-group k-popup" style="min-height: 100px; padding: 8px">
    <div>
        <input id="numbers2"/>
    </div>
</div>

this the JavaScript:

$(document).ready(function () {
    var numbers1 = [
        "One",
        "Two",
        "Three"
    ];
    var numbers2 = [
        "1",
        "2",
        "3"
    ];
    $("#numbers1").kendoComboBox({
        dataSource :numbers1,
        placeholder:"number1"
    }).data("kendoComboBox");
    $("#numbers2").kendoComboBox({
        dataSource :numbers2,
        placeholder:"number2"
    }).data("kendoComboBox");
});

The first ComboBox has a list with: “One”, “Two” and “Three” while the second with: 1, 2, 3.

and this the capture…

ComboBoxes not using and using  k-popup and k-group in a container div
ComboBoxes not using and using k-popup and k-group in a container div

Looks fine! Lets try opening the second one…

Oops! It open just bellow the first but shows the values of the second!!!

Actually, the problem is not that it opens near the first but in that absolute position, if we remove the ComboBox but leave the DIV we see that it actually opens in the same place.

So, it doesn’t look that bad. Lets try it not using k-group and k-popup

Conclusion

Actually, there is only one bug: the one about the border and body. The second is a side effect of a common dangerous practice used in JavaScript and HTML (and I must recognize that I use it too) based on using class names as node selectors: this is a huge source of errors since there are side effect when someone uses that class name where is not expected leading to wrong node selection.

KendoUI: kendoButton widget


How many times did you have to include buttons in your HTML/KendoUI pages? Many!!! But there is no such kendoButton widget (afaik) -even that it is some-sort-of-pretty-easy getting one-.

Here you will find a kendoButton widget for using in your web-pages. Enjoy it!

Source code for kendoButton

This is the JavaScript!!! I’ve tried to be as KendoUI compliant as possible.

/*
 * Copyright (c) 2012. OnaBai Software Solutions.  All rights reserved.
 * If you do not own a commercial license, this file shall be governed by the
 * GNU General Public License (GPL) version 3.
 * For GPL requirements, please review: http://www.gnu.org/copyleft/gpl.html
 */

(function ($) {
    var kendo = window.kendo,
        ui = kendo.ui,
        Widget = ui.Widget,
        CLICK = "click";

    var OBButton = Widget.extend({
        init:function (element, options) {
            var that = this;

            Widget.fn.init.call(that, element, options);
            element = that.element;
            options = that.options;

            var span = $("<span class='k-icon " + options.imageClass + "'></span>");
            if (options.showImage && options.showText) {
                $(element)
                    .addClass("k-button k-button-icontext")
                    .prepend(span);
            } else if (options.showImage) {
                $(element)
                    .addClass("k-button k-button-icon")
                    .html(span);
            } else {
                $(element).addClass("k-button k-button-icontext")

            }
            element.bind(CLICK, $.proxy(that._click, that));
        },
        options:{
            name:"OBButton",
            imageClass:"k-i-custom",
            showImage:true,
            showText:true,
            text:""
        },
        events:[
            CLICK
        ],
        _click:function () {
            this.trigger(CLICK);
        }
    });
    ui.plugin(OBButton);
})(jQuery);

Usage

You just need to invoke kendoButton with the options object. Valid options are:

  • iconClass: CSS class to add to the button in order to include an image (default value is k-icon-custom).
  • showIcon: Where the icon should be displayed or not (default value is true).
  • showText: Where the text should be displayed or not (default value is true).

There is only one event:

  • click: callback function to execute when button is clicked.

Example: explicit HTML anchor <a> element

The HTML anchor:

<a id="button1" href="#">One</a>

And the JavaScript:

$("#button1").kendoOBButton({
    imageClass:"k-i-pencil",
    showIcon:true,
    showText:true,
    click:function () {
        alert("button1 clicked");
    }
});

Where I create a button with icon and text. The icon is the one defined in k-i-pencil (this is a standard KendoUI icon included in their sprites). The text is the one defined in the anchor (One).

Example: explicit HTML button element

The HTML button:

<button id="button2">Second</button>

And the JavaScript:

$("#button2").kendoOBButton({
    imageClass:"k-i-plus",
    showIcon:true,
    showText:false
});

Where I create a button with icon only. The icon is the one defined in k-i-plus (this is a standard KendoUI icon included in their sprites). The text (Two) is actually not displayed.

Example: explicit HTML input of type button element

The HTML input:

<input id="button3" type="button" value="Three"/>

And the JavaScript:

$("#button3").kendoOBButton({
    showIcon:false,
    showText:true
});

Where I create a button with text only. The text (Three) is defined in the value attribute.

NOTE: If both showIcon and showText are false, I actually show text anyway.

Example: Declarative

But you can also use declarative. Wow!!! Thanks and congratulations: KendoUI/Telerik!!!

<a id="button4"
   data-role="button"
   data-image-class="k-i-pencil"
   data-show-text="true"
   href="#">Four</a>

Where I set data-role to button. Then I can configure showing text by setting data-show-text to true and show image by setting data-show-image to true. Configuration of the CSS class containing the image can be done by setting data-image-class to the class name.

This four example look like this:

KendoUI kendoOBButton widgets
KendoUI kendoOBButton widgets

Example: More customization

User your own icons in the buttons:

kendoOBButton with custom icon
kendoOBButton with custom icon

Use complex buttons:

kendoOBButton with complex body
kendoOBButton with complex body

Zafu: CouchDB and KendoUI (part 4)


In recent posts I’ve shown how to use KendoUI cascading ComboBox and map/reduce in CouchApps, Lets combine the two of them.

Populating KendoUI ComboBox

NOTE: I’m going to be using the database used in here, refer to that post for details.

Lets start defining the map and reduce JavaScript code using in this view:

// Map function
function(doc) {
    if (doc.type==="cities") emit(doc.state, doc.city );
}
// Reduce function
function(keys, values) {
    return true;
}

The map function emits pairs of records with a key that is the state and a value that is the city. This allows me to use the same map/reduce for getting the states (setting reduce and group to true) and for getting the cities in a state (by setting as key the name of the state).

Then I will start populating the first ComboBox with the name of the states:

$("#state").kendoComboBox({
    autoBind      :true,
    ignoreCase    :true,
    highlightFirst:true,
    delay         :100,
    placeholder   :"State",
    suggest       :true,
    dataSource    :{
        serverFiltering:true,
        transport      :{
            read:function (operation) {
                db.view("post20/getCities", {
                    success:function (data) {
                        var states = [];
                        var rows = data.rows;
                        $.each(rows, function (idx, value) {
                            states.push(value.key);
                        });
                        operation.success(states);
                    },
                    error  :function (status) {
                        console.log(status);
                    },
                    group  :true,
                    reduce :true
                });
            }
        }
    }
}).data("kendoComboBox");

In the success function what I do is extracting the keys retrieved that are the name of the states.

Populating the cities uses the same view but disables reduce by setting it to false, In addition I sort the results in order to get the cities order alphabetically.

$("#city").kendoComboBox({
    autoBind      :false,
    cascadeFrom   :"state",
    highlightFirst:true,
    delay         :100,
    index         :0,
    placeholder   :"City",
    suggest       :true,
    dataSource    :{
        serverFiltering:true,
        transport      :{
            read:function (operation) {
                db.view("post20/getCities", {
                    success:function (data) {
                        var cities = [];
                        var rows = data.rows;
                        $.each(rows, function (idx, value) {
                            cities.push(value.value);
                        });
                        operation.success(cities.sort());
                    },
                    error  :function (status) {
                        console.log(status);
                    },
                    key    :$("#state").val(),
                    reduce :false
                });
            }
        }
    }
}).data("kendoComboBox");

Optimizations

Both the data organization in the documents and views are not optimal neither in time nor in space but designed for illustrating map/reduce and KendoUI Cascading ComboBoxes.