Telerik blogs

I am writing today in an effort to explain and provide a comprehensive example of a DataSource instance to alleviate any obstacles developers might encounter when configuring a DataSource instance for the first time.

The scope and number of configurable options (i.e. 80+) associated with a Kendo UI DataSource instance reflects the fact that it can be used with just about any sort of data and remote endpoint/service imaginable. In other words, its agility to adapt to whatever data/service situation you throw at it constitutes the complexity of the abstraction itself. Now, given the ground that the abstraction can cover, it’s possible you might find configuration of a DataSource with CRUD operations an involved task, especially in light of the simplicity associated with configuring other Kendo UI abstractions.

In this article, I am going to offer a complete demonstration of a DataSource instance set up to perform CRUD operations on a remote RESTful JSON API via CORS. And, when I say “complete,” I mean a complete demo you can run locally containing both the code and the RESTful API.

If you are new to Node, what follows now is a short set of instructions to help you get Node installed and set up the server and the API used by the demo.

If you have experience with Node and npm, you can go now and get the entire demo from Github and skip the proceeding explanation. Once you have the code running locally you can use the remainder of this article to gain insight into the DataSource implementation showcased by the demo.

Serving a Local Web Page and API

The DataSource demonstration I will be discussing in this article will rely on Node and two npm packages to serve a web page and a RESTful JSON API. If you don’t have Node installed, install it now.

With Node installed, create a new directory (for example, datasource-demo) on your system, and from within this directory, run the following npm command (Note: NPM was install with Node). This will ​facilitate the process of creating a package.json  file.

$ npm init

Just hit the enter key to get through the initial setup, or feel free to answer all the questions.
 
Next, we need to install two packages from npm. In the same directory, run the following install commands:

$ npm install -g browser-sync –save-dev

and:

$ npm install -g json-server –save-dev

With those two packages installed, open up the package.json file that calling the init command created, and edit the scripts property so it contains the JSON below:

"scripts": {
    "www":"browser-sync –files 'index.html' start –server –port 3002 –no-ghost-mode",
    "api":"json-server –watch ./json-api/db.json"
},

Next, in the same directory that is housing the package.json file, create a new directory called json-api and a new file called index.html.

Inside the new json-api directory create, a JSON file called db.json. Inside of this file, place the following JSON.

{
  "users": [
    {
      "name": "John",
      "id": 1
    }
  ]
}

This file will act as a simple database. For more details about how this works, have a quick glance at​ lowdb

Inside the index.html​, ​ one directory up from the db.json file, place the following HTML:

<!DOCTYPE html>
<html>
 
<head>
    <title>Kendo UI DataSource CRUD Example</title>
    <meta charset="utf-8">
</head>
 
<body style="margin:100px">
 
 
</body>
</html>

With all that done you are now ready to server a web page (i.e. index.html) and a RESTful JSON API. Note the index.html page is ready to rip with Kendo UI, Bootstrap and jQuery.

To server the index.html file and start the API, you need to be in the same directory as the package.json file and run the following commands. Enter the first command below in the terminal you’ve been using, then open another terminal prompt/tab/instance and run the other command:

$ npm run api

then:

$ npm run www

When this is done, you should have an empty web page being served to localhost:3002 and an API available at localhost:3000.

Screenshot 2015-08-17 13.23.12

The RESTful JSON API interface will be identical to the one found here: http://jsonplaceholder.typicode.com/. Of course, the custom installation you just installed will only have the "users" route.

Adding a GUI

The graphical user interface for this demonstration will orchestrate adding (in other words, saving to the database) a named user. Obviously, this example is trivial on purpose so we can focus on the mechanics of the CRUD operations.​

I will be relying on Bootstrap’s and Kendo UI framework’s ability to work together to create the UI.

Open the index.html page and right after the opening <body> element add the following HTML.

<div class="panel panel-default">
    <div class="panel-body">
        <label for="name">Enter Users Name Click Add:</label>
        <div class="input-group">
            <input type="text" id="name" class="form-control" placeholder="name">
            <span class="input-group-btn">
        <button class="btn btn-primary" id="add" type="button">Add</button>
      </span>
        </div>
        <table class="table">
            <thead>
                <tr>
                    <th>id</th>
                    <th>name</th>
                </tr>
            </thead>
            <tbody>
            </tbody>
        </table>
    </div>
</div>

You should now see the following GUI at localhost:3003.  

Screenshot 2015-08-17 11.26.02


Reading Data

If you look closely at the HTML, you’ll see we intend to list out all of the users in the database in an HTML table. To do this, we will need to create a Kendo UI template that for creating the table rows from the data in the database.

Add the following Kendo UI HTML template to the index.html page right after the kendo.all.min.js script include.

<script type="text/x-kendo-template" id="template">
    <tr>
        <td>#:id#</td>
        <td>
            <input value="#:name#">
            <button type="button" data-id="#:id#" id="update" class="btn btn-default btn-xs">update</button>
            <button type="button" data-id="#:id#" id="delete" class="btn btn-danger btn-xs">delete</button>
        </td>
    </tr>
</script>

Looking at the template, you should notice I am intending to pull the id and name data from the RESTful API.

With the template now available, let’s go ahead and create a DataSource instance ​to get data from the database.

var dataSource = new kendo.data.DataSource({
    transport: {
        read: {
            url: 'http://localhost:3000/users',
            dataType: 'json', //not needed, shown anyway, jQuery sniffs it
            type: 'GET' //defined but, this is the default
        }
    }
});

The code above will create a new instance of DataSource and configure the data source to read data from the URL, “http://localhost:3000/users." The next two properties passed in the read configuration object are not strictly necessary but I’ve added them so that I can call out the fact that all CRUD transport operations default to “GET” requests and the dataType will be intelligently guessed if it is not defined. What you need to remember is Kendo UI framework simply passes these values off to the jQuery.ajax()method. Thus, the options are determined by the jQuery API. My advice, when configuring CRUD transports, would be to make sure you are referencing the jQuery Ajax API for directions.

To actually “GET” the data we will have to tell the DataSource instance with the read() method to read data from the API.

Go ahead and add the following javascript right after the construction of the DataSource instance.

dataSource.read();

If you view the index.html in the browser and look at the network interactions you will now see that an XHR request is made, returning the one ​record in the database.

Screenshot 2015-08-17 11.56.38

The 
read() method will populate our data source with data, but it won’t yet display the data. To display the data, we will add an event handler that will fire when the data source instance changes (or, when it reads).

To do this, we will configure the data source instance with a change event that will take the data from the DataSource instance, create a string of HTML containing our data using our template, then place that HTML string in the tables <tbody> element.

Add the following change configuration property to the object passed to the DataSource constructor.

change: function() {
    $('tbody').html(
        kendo.render(
            kendo.template(
                $('#template').html()
            ), this.view()
        )
    );
}

The code that creates the 
DataSource instance should now look like the following:

var dataSource = new kendo.data.DataSource({
 
    transport: {
        read: {
            url: 'http://localhost:3000/users',
            dataType: 'json', //not needed jQuery figures it out, shown to be verbose
            type: 'GET' //defined but, this is the default
        }
    },
 
    change: function() {
        $('tbody').html(kendo.render(kendo.template($('#template').html()), this.view()));
    }
 
});

Once you have a change event defined, you should now see the one record from the database displaying in the browser.

Screenshot 2015-08-17 12.14.33

Of course, the interface is only reading data at this point. We’ll need to add additional transport configuration values and code so that the interface will perform create, update and destroy functionality.

Creating Data

To create data (i.e. POST), we add the following create transport configuration values to the transport object.

create: {
    url: 'http://localhost:3000/users',
    type: 'POST'
}

Note that the type property is set to 'POST.'

With the create transport logic in place, we now have to provide an event handler to respond when the add button is clicked. Add the following jQuery code directly after the dataSource.read(); code.

$('#add').on('click', function() {
    dataSource.add({
        name: $('#name').val()
    });
    dataSource.sync();
}); 

When the add button is clicked, we take the value from the HTML input and add that value to the data source by invoking the add() method and passing it an object with a property of name containing the value from the input.

Now, to complete the create routine, we have to tell the data source to sync with the API. To do this we will have to call dataSource.sync().

At this point you might think we are done. However, one last step is required. The schema configuration will have to be set so the data source knows how to sort out the id of the record we are adding. Note we did not pass an id property and value with the add() method, just a value for name.

Think of the schema configuration option as an object that informs the data source how the data should be parsed. Here, we are literally telling the data source which field in the database contains the id. In our case that field is called id.

Add the following schema configuration value to the object passed to the DataSource constructor during instantiation.

schema: {
    model: {
        id: 'id'
    }
} 

With the schema correctly identified, you should now be able to add users to the database using the UI in the browser.

The JavaScript in its entirety at this point should look like so:

//create DataSource instance
var dataSource = new kendo.data.DataSource({
 
    transport: {
        read: {
            url: 'http://localhost:3000/users',
            dataType: 'json', //not needed jQuery figures it out, shown to be verbose
            type: 'GET' //defined but, this is the default
        },
        create: {
            url: 'http://localhost:3000/users',
            type: 'POST'
        }
    },
 
    schema: {
        model: {
            id: 'id'
        }
    },
 
    change: function() {
        $('tbody').html(
            kendo.render(
                kendo.template(
                    $('#template').html()
                ), this.view()
            )
        );
    }
 
});
 
dataSource.read();
 
$('#add').on('click', function() {
    dataSource.add({
        name: $('#name').val()
    });
    dataSource.sync();
});

Updating and Destroying Data

We will now follow what should be a familiar pattern at this point to complete our CRUD interactions. First, update the transport object with the following
update and destroy configuration values.

update: {
    url: function(data) {
        return 'http://localhost:3000/users/' + data.id;
    },
    type: 'PUT'
},
destroy: {
    url: function(data) {
        return 'http://localhost:3000/users/' + data.id;
    },
    type: 'DELETE'
}

Note again that I have changed the type property to the appropriate HTTP verb (“PUT” and “DELETE”). Also note that instead of passing the url property a URL string value, I have to instead use a function to create a URL that matches how the API interface works. To update and delete items, I will need to request a URL that contains the id of the value I would like to update or delete. To do this, we provide the url property with a function that returns the URL with the correct id (i.e. http://localhost:3000/users/2).

Inside of the function, the DataSource instance will pass me the data I am intend to change. Thus, data.id in the scope of the function will be the id of the value needing to be deleted or updated.

Add the following event handlers for the delete and update buttons right after the add event handler we added previously.

$('tbody').on('click', '#delete', function() {
    var $this = $(this);
    dataSource.remove(dataSource.get($this.data('id')));
    dataSource.sync();
});
 
$('tbody').on('click', '#update', function() {
    var $this = $(this);
    dataSource.get($this.data('id')).set('name', $this.prev('input').val());
    dataSource.sync();
});

Inside of both of these event handlers, I first acquire the id representing the value I am updating or deleting from the HTML (i.e. data-id="#:id#").

For deleting, I take this id and pass it to the get() method to get a reference to the record in the data source. Next, I pass that reference to the remove() method. This will, as you might guess, remove data from the data source.

For updating, I take the id and get() a reference to that recored in the data source. Then, using the set() method I set the value of name to the new value found in the input which previously displaying the old value.

Of course, in both handlers, I then have to sync() with my back-end to make changes to the database.

The final JavaScript in the index.html page should look like this:

//create DataSource instance
var dataSource = new kendo.data.DataSource({
 
    transport: {
        read: {
            url: 'http://localhost:3000/users',
            dataType: 'json', //not needed jQuery figures it out, shown to be verbose
            type: 'GET' //defined but, this is the default
        },
        create: {
            url: 'http://localhost:3000/users',
            type: 'POST'
        },
        update: {
            url: function(data) {
                return 'http://localhost:3000/users/' + data.id;
            },
            type: 'PUT'
        },
        destroy: {
            url: function(data) {
                return 'http://localhost:3000/users/' + data.id;
            },
            type: 'DELETE'
        }
    },
 
    schema: {
        model: {
            id: 'id'
        }
    },
 
    change: function() {
        $('tbody').html(
            kendo.render(
                kendo.template(
                    $('#template').html()
                ), this.view()
            )
        );
    }
 
});
 
 dataSource.read();
 
$('#add').on('click', function() {
    dataSource.add({
        name: $('#name').val()
    });
    dataSource.sync();
});
 
$('tbody').on('click', '#delete', function() {
    var $this = $(this);
    dataSource.remove(dataSource.get($this.data('id')));
    dataSource.sync();
});
 
$('tbody').on('click', '#update', function() {
    var $this = $(this);
    dataSource.get($this.data('id')).set('name', $this.prev('input').val());
    dataSource.sync();
});

At this point, the UI should be a fully functional CRUD UI for editing users in the database.

JOhKsXjBOV

Conclusion

I have laid out a complete demo of a functioning CRUD interface using the Kendo UI DataSource abstraction interfacing with a JSON API. However, my implementation is just the tip of the iceberg. The wealth of configuration options runs deep with the DataSource and the numerous ways in which it could be configured to work with your specific API is limitless. Take what you have learned here, dive into the DataSource API documents, and put the DataSource widget to work in your own code. Trust me, it’ll be well worth it, given the heavy lifting that this abstraction provides.

Before you go, I'd like to mention that I also created three variations of this demo (​with the same UI and function, but different code), which you can find in the github repository. These demos continue to showcase the versatility and power of the DataSource abstraction.

1. Using local data instead of a RESTful JSON API:

https://github.com/kendo-labs/datasource-restful-api-CRUD-demo/blob/master/local-data-CRUD-demo.html

2. Using local data that is saved locally using the DataSource offline mode:

https://github.com/kendo-labs/datasource-restful-api-CRUD-demo/blob/master/local-data-CRUD-offline-demo.html

3. Replacing the HTML table with a Grid widget that uses the DataSource instance:

https://github.com/kendo-labs/datasource-restful-api-CRUD-demo/blob/master/grid-widget.html


cody-lindley
About the Author

Cody Lindley

Cody Lindley is a front-end developer working as a developer advocate for Telerik focused on the Kendo UI tools. He lives in Boise, ID with his wife and three children. You can read more about Cody on his site or follow him on Twitter at @codylindley.

Comments

Comments are disabled in preview mode.