KendoUI tips and tricks on Dates in a Grid


Have you ever tried to display a date in a Kendo UI Grid widget? It is possible but there are questions that you should keep in mind.

IMPORTANT: Next post explains how to display Time and Date Time Kendo UI widgets in a grid. Stay tuned!

Displaying a Date

Lets assume that we have the following JSON array (either I receive it from the server or it has been computed and stored in the array).

var entries = [
    { "name":"Dave Packard", "born":new Date(1912, 8,  7) },
    { "name":"Bill Hewlett", "born":new Date(1913, 4, 20) },
    { "name":"Larry Ellison","born":new Date(1944, 7, 17) },
    { "name":"Steve Jobs",   "born":new Date(1955, 1, 24) },
    { "name":"Bill Gates",   "born":new Date(1955, 9, 28) },
    { "name":"Jeff Bezos",   "born":new Date(1964, 0, 12) },
    { "name":"Larry Page",   "born":new Date(1973, 5, 26) },
    { "name":"Sergey Brin",  "born":new Date(1973, 7, 21) }
];

And I use the following grid for displaying the data:

var grid = $("#auth_tbl").kendoGrid({
    dataSource: { data: entries },
    editable: true,
    navigatable: true
});

I will get something like this:

And if I click on the date cell for editing it, I see:

Really easy. Well done!, Kendo UI team

KendoUI Formatting Dates on a Grid

Lets start doing some customizations. Something simple: set titles and choose the date representation as year-month-day, I.e. yyyy-MM-dd.

This is my new JavaScript code:

$("#auth_tbl").kendoGrid({
    dataSource: { data: entries },
    columns: [
        { field: "name", title: "Name" },
        { field: "born", title: "Born", format:"{0:yyyy-MM-dd}" }
    ],
    editable: true,
    navigatable: true
});

Now we can see that the format is the same when it is not in edit mode:

Than when it is:

Kendo UI folks: Thanks, again for making it simple!

KendoUI Grid dates are not Date Objects

Not everything is going to be that simple! Lets consider that I saved the dates as ISO 8601 (yyyy-MM-ddTHH:mm:ss.fffZ) and once I retrieve them I want to display them back.

Now, my entries are something like this:

var entries = [
    { "name":"Dave Packard", "born":"1912-09-06T22:00:00.000Z" },
    { "name":"Bill Hewlett", "born":"1913-05-19T22:00:00.000Z" },
    { "name":"Larry Ellison","born":"1944-08-16T22:00:00.000Z" },
    { "name":"Steve Jobs",   "born":"1955-02-23T23:00:00.000Z" },
    { "name":"Bill Gates",   "born":"1955-10-27T22:00:00.000Z" },
    { "name":"Jeff Bezos",   "born":"1964-01-11T23:00:00.000Z" },
    { "name":"Larry Page",   "born":"1973-06-25T23:00:00.000Z" },
    { "name":"Sergey Brin",  "born":"1973-08-20T23:00:00.000Z" }
];

and when I display it:

That is displayed as a string and not a date.

I need to convert it to a JavaScript Date Object that might be done in multiples ways:

  1. Do the conversion in model.transport.read.
  2. Do it in model.schema.data.
  3. Do it in model.schema.parse.

I opt for the last one since parse the response of my server, is exactly what I want to do.

Now, my Grid definition in JavaScript is:

$("#auth_tbl").kendoGrid({
    dataSource:{
        data:entries,
        schema:{
            parse:function (response) {
                $.each(response, function (idx, elem) {
                    if (elem.born && typeof elem.born === "string") {
                        elem.born = kendo.parseDate(elem.born, "yyyy-MM-ddTHH:mm:ss.fffZ");
                    }
                });
                return response;
            }
        }
    },
    columns:[
        { field:"name", title:"Name" },
        { field:"born", title:"Born", format:"{0:yyyy-MM-dd}" }
    ],
    editable:true,
    navigatable:true
});

What parse function does is receive the data from transport (in my case JSON entries array) and iterate on the elements and do the conversions.

NOTE: By default kendo.parseDate tries a series of formats depending on your culture and prebuilt ones BUT ISO 8601 as yyyy-MM-ddTHH:mm:ss.fffZ is not included. You can include them following my instruction posted here.

The result is (almost) the expected both in edit and not edit mode.

And I say almost since visually is correct BUT dates are not correct (one day less). The reason is a bug in KendoUI parseDate and ISO 8601 when it uses Zulu timezone where when building the Date object, it does not consider it as UTC. The fix is posted in here.

KendoUI Grid Dates in popup edit mode

What about if the edit is in popup mode?

The code would be like this:

$("#auth_tbl").kendoGrid({
    dataSource:{
        data:entries,
        schema:{
            parse:function (response) {
                $.each(response, function (idx, elem) {
                    if (elem.born && typeof elem.born === "string") {
                        elem.born = kendo.parseDate(elem.born, "yyyy-MM-ddTHH:mm:ss.fffZ");
                    }
                });
                return response;
            }
        }
    },
    columns:[
        { command:[ "edit" ] },
        { field:"name", title:"Name" },
        { field:"born", title:"Born", format:"{0:yyyy-MM-dd}" }
    ],
    editable:"popup",
    navigatable:true
});

And displayed I get:

Conclussions

  1. Define titles in KendoUI Grid columns options.
  2. Define date format in KendoUI Grid columns options.
  3. Data should be Objects since there is no conversion (guess) depending on the output format.
  4. Use dataSource.schema.parse function for converting data formats between transport format and and KendoUI Grid data formats.

Acknowledgements

Congratulations, Kendo UI team for making my life easier!

Advertisements

KendoUI supporting ISO 8601 dates


As I explained in this other post there are some bugs on KendoUI core in the function that parses dates. KendoUI folks (thank, you!) have fixed them BUT this is not open source available and will not be until november. In the meantime fix it as I’ve explained in that post.

But related with this there is a missing functionality that is being able to parse (by default) ISO 8601 dates represented as yyyy-MM-ddTHH:mm:ss.fffZ

Default supported formats

In addition to date formats defined in your culture file, KendoUI defines some other patterns:

  • yyyy-MM-ddTHH:mm:ss.fffZ
  • ddd MMM dd yyyy HH:mm:ss
  • yyyy-MM-ddTHH:mm:ss.fffzzz
  • yyyy-MM-ddTHH:mm:sszzz
  • yyyy-MM-ddTHH:mmzzz
  • yyyy-MM-ddTHH:mmzz
  • yyyy-MM-dd

So any date with one of this formats is recognized.

But these formats do not include the one that we get with Date.getISOString and used for sorting date alphabetically.

So, you have to use (being str the string that contains the date being parsed):

kendo.parseDate(str, "yyyy-MM-ddTHH:mm:ss.fffZ");

In order to correctly parse an ISO date.

Adding yyyy-MM-ddTHH:mm:ss.fffZ format

I would like to be able to add formats to culture files and get them automatically recognized by KendoUI parsing date function but KendoUI did prefer to add only formats in culture that have the following codes:

formatsSequence = ["G", "g", "d", "F", "D", "y", "m", "T", "t"];

Likely because they want to control the precedence of the patterns.

Despite I don’t like to change to code that is open source but without a public mechanism for contributions (because each time who is controlling the code releases a new version you have to do it again) I do it when something is very located and hence easy to do it back

So, the only way is going to the source code and add the format. Edit kendo.web.js or (kendo.core.js) file and look for a function called parseDate.

formats[idx] = "ddd MMM dd yyyy HH:mm:ss";<br />
formats[++idx] = "yyyy-MM-ddTHH:mm:ss.fffZ";
formats[++idx] = "yyyy-MM-ddTHH:mm:ss.fffzzz";
formats[++idx] = "yyyy-MM-ddTHH:mm:sszzz";
formats[++idx] = "yyyy-MM-ddTHH:mmzzz";
formats[++idx] = "yyyy-MM-ddTHH:mmzz";
formats[++idx] = "yyyy-MM-dd";

and add:

formats[++idx] = "yyyy-MM-ddTHH:mm:ss.fffZ";

I do recommend adding it before yyyy-MM-ddTHH:mm:ss.fffzzz which is a very similar format but define a timezone.

Now, you can simple do:

kendo.parseDate(str);

and get the str converted to a Date.

KendoUI parsing dates (fixing the bugs)


There are some bugs in KendoUI date parsing!!!

Don’t worry I already submitted it to KendoUI and they say:

This has been fixed with the Q2’12 service pack version (2012.2.913).

Commercial users can update to this version, while for the others the fix will be available with the Q3’12 release in mid November.

If you are not a commercial user, the good news is that you can fix them in the open-source version.

KendoUI parseDate bugs

The question is that you can parse the following string: 2012-08-26 using a format of hh:mm returning as result: Mon Jan 01 1900 20:12:00 GMT+0100 (CET) Where year 2012 ends up being 20 hours and 12 minutes of 1900, January 1st. Oops!

The second bug is that dates in ISO8601 are not considered as UTC and end up being shifted by your timezone.

KendoUI parseExact and checkLiteral bug

You can reproduce it using the following JavaScript code (also in here).

function traceDate(msg, obj) {
    $("#debug").append(msg + " : " + obj + "\n");
}

var date = new Date(2012, 8, 26, 12, 34, 56, 789);
traceDate("Original date                  ", date);
var str = kendo.toString(date, "yyyy-MM-dd");
traceDate("kendo.toString('yyyy-MM-dd')   ", str);
traceDate("kendo.parseDate('yyyy-MM-dd')  ", kendo.parseDate(str, "yyyy-MM-dd"));
traceDate("kendo.parseDate('yyyy.MM.dd')  ", kendo.parseDate(str, "yyyy.MM.dd"));
traceDate("kendo.parseDate('HH:mm')       ", kendo.parseDate(str, "HH:mm"));​

and as HTML simply:

</pre>
<pre id="#debug">

Producing as result:

Original date                   : Wed Sep 26 2012 12:34:56 GMT+0200 (CEST)
kendo.toString('yyyy-MM-dd')    : 2012-09-26
kendo.parseDate('yyyy-MM-dd')   : Wed Sep 26 2012 00:00:00 GMT+0200 (CEST)
kendo.parseDate('yyyy.MM.dd')   : null
kendo.parseDate('HH:mm')        : Mon Jan 01 1900 20:12:00 GMT+0100 (CET)

This displays:

  • the original date,
  • the date converted to string by kendo.toString and yyyy-MM-dd format
  • 2012-09-26 string parsed by kendo.parseDate and yyyy-MM-dd format
  • 2012-09-26 string parsed by kendo.parseDate and yyyy.MM.dd format (return null that is correct)
  • 2012-09-26 string parsed by kendo.parseDate and HH:mm format (returns 1900-01-01 20:12:00 CET that is incorrect!!!)

Solution

Search for checkLiteral function invocation in kendo.web.js or kendo.core.js and replace where it says:

checkLiteral();

by

if (!checkLiteral()) return;

and where it says:

ISO8601 = checkLiteral();

by

if (!checkLiteral()) return null;
ISO8601 = true;

KendoUI ISO8601 bug

For information on ISO 8601 take a look here. Why do I care about this format? Because is the one that I get when serialize Date objects using JSON.stringify that I commonly use to interface with my DB system (CouchDB) and hence I need to parse them back when I receive it from CouchDB.

If I have the following JavaScript code:

var date = new Date(2012, 8, 26, 12, 34, 56, 789);
var iso = JSON.stringify(date).replace(/"/g, '');
traceDate("Original date     ", date);
traceDate("date as ISO8601   ", iso);
traceDate("kendo.parseDate   ", kendo.parseDate(iso, "yyyy-MM-ddThh:mm:ss.fffZ"));

Where I define a date as 2012 september 26th and time 12 hours 34 minutes 56 seconds and 789 milliseconds. I convert it to ISO using JSON.stringify and then try to parse it back using Kendo.

NOTE: I had to remove extra quotes that were added to the ISO8601 representation of the date. I might have used date.toISOString() instead but I did want to stick to my actual scenario.

I get:

Original date      : Wed Sep 26 2012 12:34:56 GMT+0200 (CEST)
date as ISO8601    : 2012-09-26T10:34:56.789Z
kendo.parseDate    : Wed Sep 26 2012 10:34:56 GMT+0200 (CEST)

If you do not pay close attention to the output it might seem correct BUT it is not. If you realize, the original date was 12:34:56 GMT+0200 so if we convert it to Zulu time, it is: 10:34:56Z (two hours less). So, the conversion that we did using JSON.stringify (2012-09-26T10:34:56.789Z) is actually correct. BUT the result of parsing it back using Kendo UI is 10:34:56 GMT+0200 that is not correct.

Solution

Just have to look for exactParse in kendo.web.js or kendo.core.js and by the end of the function it says:

if (UTC) {
    if (hoursOffset) {
    hours += -hoursOffset;
}

if (minutesOffset) {
    minutes += -minutesOffset;
}

return new Date(Date.UTC(year, month, day, hours, minutes, seconds, milliseconds));
}

and replace it by:

if (UTC || ISO8601) {
    if (hoursOffset) {
        hours += -hoursOffset;
    }

    if (minutesOffset) {
        minutes += -minutesOffset;
    }

    return new Date(Date.UTC(year, month, day, hours, minutes, seconds, milliseconds));
}

If the date is in ISO8601 then we should use Date.UTC for building the date.

Need more help!

If you do not feel confident with the instructions on fixing it, please let me know and I will help you with the modified file.

Why KendoUI (and maybe why not!)


Briefly, I’ve chosen KendoUI because:

  1. The quality of their emitted HTML code (fast and supporting different browsers).
  2. Being able to move to payable version and get support.

I like from KendoUI

  1. GPL v3 (one of the versions)
  2. The widgets:
    1. Are visually nice (to me)
    2. And are visually consistent (most of the times).
  3. Responsiveness of the HTML emitted code.
  4. Documentation (Ok! there are a lot of complains about the lack of documentation not having every function, event, option documented and I totally agree BUT if we compare with most freely available software there is a lot of documentation.
  5. Have support (if you pay for it!)
  6. Their forum for free-version users where some people is pretty active (like myself in Grid section).
  7. Being able to move to paying version as soon as I have the product finished and take full support and updates.
  8. Have access to their source code and being able to fix their bugs if they do not do it for me.
  9. Pretty fast start using it (take a look into this).

and I don’t like from KendoUI

  1. Documentation (I told you, it might be much better BUT it is much better that other equivalent UI).
  2. Lack of orthogonality and features that work for some widgets don’t work for others.
  3. Their forum for free-version users where admins are not as active as I would expect.
  4. Have access to their source code and have to fix their bugs if they do not do it for me. This is an advantage and a disadvantage because I have to check for changes every version and maybe have to redo the fixing (and I have a few done).

KendoUI setting background for specific dates


In Kendo UI demos it is explained how to format a specific date by adding an icon but many times you just need to change the background.

Kendo UI calendar with formatted dates

First of all lets define the structure used for choosing which dates to format and how:

var tasks = [];
tasks[+new Date(2012, 8 - 2, 22)] = "ob-done-date";
tasks[+new Date(2012, 8 - 2, 27)] = "ob-done-date";
tasks[+new Date(2012, 8 - 1, 3)] = "ob-done-date";
tasks[+new Date(2012, 8 - 1, 5)] = "ob-not-done-date";
tasks[+new Date(2012, 8, 8)] = "ob-done-date";
tasks[+new Date(2012, 8, 12)] = "ob-done-date";
tasks[+new Date(2012, 8, 24)] = "ob-not-done-date";
tasks[+new Date(2012, 8 + 1, 6)] = "ob-done-date";
tasks[+new Date(2012, 8 + 1, 7)] = "ob-not-done-date";
tasks[+new Date(2012, 8 + 1, 25)] = "ob-done-date";
tasks[+new Date(2012, 8 + 1, 27)] = "ob-not-done-date";

What I did is defining a vector tasks and for each (desired) date I define a mark that will help me deciding its formatting.
Now I create a Kendo UI Calendar as:

$("#calendar").kendoCalendar({
    value:today,
    dates:tasks
}).data("kendoCalendar");

And a style definition:

.ob-done-date {
    background-color: #85d633;
    background-image: url(../styles/textures/highlight.png);
}
.ob-not-done-date {
    background-color: #EBB500;
    background-image: url(../styles/textures/highlight.png);
}

Next is defining the logic for deciding the formatting…

if (typeof data.dates[+data.date] === "string") {
    // insert the HTML: <div class="data.dates[+data.date]">
    // insert the date: data.value
    // insert the HTML: </div>
} else {
    // insert the date: data.value
}

And this logic as a template gets transformed into:

$("#calendar").kendoCalendar({
    value:today,
    dates:tasks,
    month:{
        content:'# if (typeof data.dates[+data.date] === "string") { #' +
                '<div class="#= data.dates[+data.date] #">' +
                '#= data.value #' +
                '</div>' +
                '# } else { #' +
                '#= data.value #' +
                '# } #'
    }
}).data("kendoCalendar");

Producing…

Kendo UI Calendar with wrong formatting
Kendo UI Calendar with wrong formatting

Where you can see it is almost what I wanted to do but formatting is not correct. The reason is that I’ve applied the formatting to the wrong HTML node. I should have applied the formatting to the parent of the DIVnode that I have defined in the template.

Fixing formatting

1. Rename CSS style

Lets append -style suffix to each of the marks used in tasks array.

.ob-done-date-style {
    background-color: #85d633;
    background-image: url(../styles/textures/highlight.png);
}
.ob-not-done-date-style {
    background-color: #EBB500;
    background-image: url(../styles/textures/highlight.png);
}

2. Bind a function to navigate for applying the style to the parent node

$("#calendar").kendoCalendar({
    value:today,
    dates:tasks,
    month:{
        content:'# if (typeof data.dates[+data.date] === "string") { #' +
                '<div class="#= data.dates[+data.date] #">' +
                '#= data.value #' +
                '</div>' +
                '# } else { #' +
                '#= data.value #' +
                '# } #'
    },
    navigate:function () {
        $(".ob-done-date", "#calendar").parent().addClass("ob-done-date-style k-state-hover");
        $(".ob-not-done-date", "#calendar").parent().addClass("ob-not-done-date-style k-state-hover");
    }
}).data("kendoCalendar");

And I finally get what I was looking for:

Kendo UI calendar with colored dates
Kendo UI calendar with colored dates

Zafu: CouchDB and KendoUI (part 3)


In the previous posts I’ve explained how to deploy Kendo UI in CouchDB (here) and the four CRUD operations using CouchDB’s Standard JavaScript API (here).
On this post I will explain how to implement KendoUI DataSource transport commands using CouchDB.

Kendo UI DataSource Transport Read

1. Define map function for the view

Create in the root of your project a views and views/getTicks folders:

mkdir views
mkdir views/getTicks

and create a file named map.js as follow:

function(doc) {
  if (doc.type && doc.type === "tick") {
    emit(doc._id, doc);
  }
}

This map function emits each document in this DataBase that has type defined and equals ‘tick’. The key for the map function is the _id of the document and the value is the document itself.

2. Define the HTML for holding the grid

I will create a minimum HTML as follows

<div id="grid"></div>

3. Create Kendo UI Grid

Lets use the following code:

var grid = $("#grid").kendoGrid({
    columns:[
        { field:"tick", title:"Tick" },
        { field:"name", title:"Company" }
    ],
    dataSource:ticksDataSource
}).data("kendoGrid");

Where I define a grid showing two columns: Tick and Company and using a ticksDataSource as Data Source.

4. Create Ticks Model for using in Kendo UI DataSource

This is the Model for holding the information.

// Ticks Model
var ticksModel = {
    id:"_id",
    fields:{
        type:{ defaultValue:"tick", editable:false },
        tick:{ editable:true},
        name:{ editable:true }
    }
};

Where id define that the id is the same that CouchDB uses, plus three additional fields:

  1. type: The type of record (here it is always tick).
    This is a trick quite commonly used in CouchDB for knowing the type of information saved on each record. This is not like other DataBase Systems where I have one separate table for each type of record.
  2. tick: The tick of the company
  3. name: The Company name.

5. Create Kendo DataSource

I create ticksDataSource as follow:

// Create Grid for displaying data
var ticksDataSource = new kendo.data.DataSource({
    transport:{
        read:function (operation) {
            // Read data and populate it
            db.view("app1/getTicks", {
                        success:function (data) {
                            if (data.rows) {
                                var res = [];
                                $.each(data.rows, function (i, val) {
                                    res.push(val.value);
                                });
                                operation.success(res);
                            }
                        }
                    }
            );
        }
    },
    schema:{
        model:ticksModel
    }
});

Where I set the schema model to ticksModel that I have defined above and I define a read callback for Kendo Data Source transport. This callback is invoked each time the system needs to populate the grid.

For reading the data, I use view method of jquery.couch.js API that executes the view that I have defined above (app1/getTicks).

On success, I will get a JSON object containing an array with the records. So, I copy them into an auxiliar variable res and the return res as result by invoking operation.success(res).

And I get something like this:

Kendo UI Grid populated using DataSource transport read callback
Kendo UI Grid populated using DataSource transport read callback

Kendo UI DataSource Transport Create

Once I have implemented the callback for filling the grid, lets add the capacity for creating a new record.

1. Add button to the toolbar for creating record

I added a toolbar array containing an element equal to “create” to kendoGrid creation and specified the editable mode as popup:

var grid = $("#grid").kendoGrid({
    toolbar: [ "create" ],
    editable: "popup",
    columns:[
        { field:"tick", title:"Tick" },
        { field:"name", title:"Company" }
    ],
    dataSource:ticksDataSource
}).data("kendoGrid");

And I got:

"Add record" button in Kendo UI Grid toolbar.
“Add new record” button in Kendo UI Grid toolbar.

And if I click on “Add new record” button I get a popup window for introducing a new record:

Popup window for adding new record
Popup window for adding new record

But I did not implemented create callback into ticksDataSource transport so even I fill the form and click on update, the record is not saved in CouchDB.

2. Implement create callback

As well as I defined a create callback, I implement a callback for saving the form in CouchDB.

create:function (operation) {
    var data = operation.data;
    delete data._id;
    db.saveDoc(data,
            {
                success:function () {
                    operation.success();
                    dataSource.read();
                },
                error:function (e) {
                    operation.error("Error " + e + " while creating document.");
                }
            }
    );
},

What I do is invoking saveDoc and if succeed then refresh the grid by invoking dataSource.read() and if it fails I inform the DataSource that it failed by finishing with an operation.error.

What I get is:

Result of adding a record
Result of adding a record

NOTE: that I delete _id before saving the document. This is important since KendoUI created this field as an empty string (“”) and if  I save a document with _id defined, CouchDB believes that this is the actual value for the _id but an empty string is not a valid identifier. Conclusion: I delete it before saving the document.

Kendo UI DataSource Transport Update

What I’m going to do is add an edit button to the toolbar and on click open a popup window with the document that is selected.

1. Add Edit button

Add a button to the toolbar for editing: change toolbar definition in grid to match:

toolbar:[ "create", "edit" ]

Getting:

Kendo UI toolbar with edit button
Kendo UI toolbar with edit button

But if you click on it it actually does nothing since KendoUI does not define a default callback for an edit button in the toolbar but in a row. That would look something like this:

KendoUI in row edit button
KendoUI in row edit button

And this time if I click on edit button I actually get:

Kendo UI grid with in-row edit button
Kendo UI grid with in-row edit button

But I don’t like this edit buttons using space on each row, so lets go back to the original one and add code for making it work.

2. Add callback to toolbar edit button

What I do is bind a click event to the button such as:

$(".k-grid-edit", "#grid").click(function () {
    alert("hello");
})

Clicking on edit button I actually get the alert with a hello message.

3. Make rows selectable.

To make a row selectable I added selectable:”row” but if in addition I want to be able to navigate using the mouse then in addition I include: navigatable:true. Now my grid definition is something like:

var grid = $("#grid").kendoGrid({
    toolbar:[ "create", "edit" ],
    editable:"popup",
    selectable:"row",
    navigatable:true,
    columns:[
        { field:"tick", title:"Tick" },
        { field:"name", title:"Company" }
    ],
    dataSource:ticksDataSource
}).data("kendoGrid");

And if I select a row it looks like:

Kendo UI grid with row selected
Kendo UI grid with row selected

4. Popup edit window for selected row

Previously I just open an alert when clicking on edit. Now, I’m going to find the selected row and open a popup with the actual content of the row.

// Edit implementation
$(".k-grid-edit").click(function (e) {
    var selected = grid.select();
    if (selected && selected.length > 0) {
        $.each(selected, function (idx, elem) {
            grid.editRow(grid.select());
        });
    }
});

And when I click on editbutton I get:

Kendo UI edit popup triggered by edit button in toolbar
Kendo UI edit popup triggered by edit button in toolbar

If I modify the company name from Apple to Apple Incand click on update I will see:

Row edited but marked as not synchronized
Row edited but marked as not synchronized

Where the company name is marked as modified but not saved in the database I can actually verify this by refreshing the page.

4. Implement transport update

I need to implement Kendo UI DataSource transport update for persisting the information in the database.

update:function (operation) {
    db.saveDoc(operation.data,
            {
                success:function () {
                    operation.success();
                    ticksDataSource.read();
                },
                error:function (e) {
                    operation.error("Error " + e + " while updating document.");
                }
            });
}

If I do now edit a record and click on update I will see that the change becomes permanente and refreshing the page will show the information updated.

Kendo UI DataSource Transport Delete

Only one last callback to implement to have a full CRUD KendoUI DataSource implemented on CouchDB.

1. Add delete button

As for edit, Kendo UI library only implements the callback for delete buttons included in a row. But I will do the same that I did for edit.

The toolbar definition is:

toolbar:[ "create", "edit", "destroy" ],

The callback for when pressing the delete button (same code that edit but invoking grid.removeRow instead of grid.editRow):

// Delete implementation

// Delete implementation
$(".k-grid-delete").click(function (e) {
    var selected = grid.select();
    if (selected && selected.length > 0) {
        $.each(selected, function (idx, elem) {
            grid.removeRow(grid.select());
        });
    }
});

And the delete implementation in Kendo UI DataSource transport is:

// Delete implementation
destroy:function (operation) {
    db.removeDoc(operation.data,
            {
                success:function () {
                    operation.success();
                    dataSource.read();
                },
                error:function (e) {
                    operation.error("Error " + e + " while deleting document.");
                }
            });
}

If I select a row and click on delete button I will get a prompt window asking for confirmation and I click ok the record is deleted.

If you liked this post please click on “like” in order to let me know that you are interested and keep writing about Zafu.

If you would like to propose some subject, you are also welcome!

And that’s it!!!

Zafu: CouchDB and KendoUI (part 2)


In my previous post I explained how to deploy KendoUI inside CouchDB using CouchApp.

This was a pretty easy thing and (of course) very little benefit since we were not actually saving / retrieving data from the database.

Step1: Using jQuery CouchDB library

jquery.couch.js is the standard API shipped with CouchDB. This is what I’m going to use for Creating, Reading, Updating and Deleting data from CouchDB.

Finding jquery.couch.js

The easiest way for finding jquery.couch.js is getting the one that is shipped inside CouchDB:

  1. Open http://127.0.0.1:5984/_utils/script/jquery.couch.js in your browser and save the file.
  2. Use curl from the command line.
curl http://127.0.0.1:5984/_utils/script/jquery.couch.js > jquery.couch.js

The very initial idea would be linking your HTML with this version (by using relative paths to it) BUT according to some forums (see this) it does not work in mobile environments. Since it is not large, I do also recommend copying it to your _attachments/js folder.

Very quick tutorial about jQuery CouchDB library

NOTE: This is not really a tutorial but some basic notions.
First of all you start thinking in terms of parallelization: I mean, you should not think about methods that you invoke and execution continues when method finishes. Most (all?) methods within CouchDB JavaScript API should be considered as something that you ask for and it will (eventually) finish and you get notified about it (upon successful -or not- completion of it).
Many of jquery.couch.js methods you provide one JSON object with two fields that are callback functions:

var callbacks = {
    success: function () { alert ("Yeah!"); },
    error: function () { alert ("Oops!"); }
}

1. Including the API

After including jQuery library include jquery.couch.js:

<!-- jQuery CouchDB scripts -->
<script src="js/jquery.min.js" type="text/javascript"></script>
<script src="js/jquery.couch.js" type="text/javascript"></script>

2. Accessing the database

Define a variable that we will use throughout the code:

// Reference to the database
var db = $.couch.db("app1");

NOTE: Remember that you might need to login before start using the database.

3. Create document

Invoke saveDoc method from db with two arguments: JSON document being saved and callbacks for success or error.

var db = $.couch.db("app1");
var data = { type:'tick', tick: 'AAPL', name: 'Apple' };
db.saveDoc (data, {
    success: function (d) { alert ("Yeah!: " + d._id); },
    error: function () { alert ("Oops!"); }
});

IMPORTANT:It is very important to note that data should either not include _id field or define it with a value that does not exist. A value that already exists represents a collision and hence an error. An empty string is not valid either.
success receives as argument an object with the identifier (id) and revision (rev) of the created document.

4. Reading a document

Invoke openDoc with document identifier as first argument and callback functions as second.

db.openDoc("63806bde88c403b5c5aadceba60260e9", {
    success: function (d) { alert ("Yeah!" + d.tick + "->" + d.name); },
    error: function () { alert ("Oops!"); }
});

On success you get the document.

5. Updating a document

Invoke saveDoc with the document update (it must include the identifier _id and revision _rev) and callback functions.

var data = { _id:'63806bde88c403b5c5aadceba60260e9', _rev:'1-f10e140d3debd4cd3c524737c2d0e8a6',
    type:'tick', tick: 'AAPL', name: 'Apple Inc.' };
db.saveDoc(data, {
    success: function (d) { alert ("Yeah!" + d.rev); },
    error: function () { alert ("Oops!"); }
});

6. Delete a document

Invoke removeDoc with one JSON object including the document identifier _id and revision _rev and a second argument for the callbacks.

var data = { _id:'63806bde88c403b5c5aadceba60274ef', _rev:'1-c8af95a9d3c4d9ea3cd720f25415ffe9' };
db.removeDoc(data, {
    success: function (d) { alert ("Yeah! " + d.ok); },
    error: function () { alert ("Oops!"); }
});

7. Views

Invoke view with the name of the view that you want to execute. On return you get a JSON structure with a field rows containing a vector with the information retrieved.

db.view("app1/getTicks", {
    success: function (d) { alert ("Yeah! Retrieved: " + d.ok + " documents"); },
    error: function () { alert ("Oops!"); }
});

And that’s about it. For a more complete description of these interfaces, click here!.
Next post (here) is start using it for populating a Kendo UI grid.