Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Editable jQuery Grid Recommendations with REST API

First, i already read the question "jQuery Grid Recommendations" but it doesn't answer to my question.

I have a small REST API with MongoDB Backend just :

Get all equipements :

GET /equipements HTTP/1.1
{{_id:key1, name:Test Document 1, plateforme:prod}, {_id:key2, name:Test Document 2, plateforme:prod}, ...}

Get equipement with the key : key1

GET /equipements/key1 HTTP/1.1
{"_id": "key1", "name": "Test Document 1", "plateforme": "prod"}

Add new equipement

PUT /equipements HTTP/1.1  {"_id": "key8", "name": "Test Document 3", "plateforme": "prod"}
HTTP/1.0 200 OK

Now, I need to find an easy way to allow lambda user to add/view/del equipments. So i think a web interface with a jQuery like UI is the best. I tried with Sencha Rest Proxy but I do not know javascript and I fails to adapt the example.

How fix my javascript for my REST backend?

AND/OR

Can you recommend a simpler alternative to Sencha Rest Proxy? (which works with my REST backend)

Answer : jqGrid

AND/OR

What jQuery Grid would you recommend me? (which works with my REST backend)

Answer : jqGrid

Final question : Why my cells are not editable with double click ?

Appendices

Server Side (EDIT : Add methode POST)

#!/usr/bin/python
import json
import bottle
from bottle import static_file, route, run, request, abort, response
import simplejson
import pymongo
from pymongo import Connection
import datetime



class MongoEncoder(simplejson.JSONEncoder):
    def default(self, obj):
                # convert all iterables to lists
        if hasattr(obj, '__iter__'):
            return list(obj)
        # convert cursors to lists
        elif isinstance(obj, pymongo.cursor.Cursor):
            return list(obj)
        # convert ObjectId to string
        elif isinstance(obj, pymongo.objectid.ObjectId):
            return unicode(obj)
        # dereference DBRef
        elif isinstance(obj, pymongo.dbref.DBRef):
            return db.dereference(obj)
        # convert dates to strings
        elif isinstance(obj, datetime.datetime) or isinstance(obj, datetime.date) or isinstance(obj, datetime.time):
            return unicode(obj)
        return simplejson.JSONEncoder.default(self, obj)



connection = Connection('localhost', 27017)
db = connection.mydatabase

@route('/static/<filename:path>')
def send_static(filename):
    return static_file(filename, root='/home/igs/restlite/static')

@route('/')
def send_static():
    return static_file('index.html',root='/home/igs/restlite/static/')

@route('/equipements', method='PUT')
def put_equipement():
    data = request.body.readline()
    if not data:
        abort(400, 'No data received')
    entity = json.loads(data)
    if not entity.has_key('_id'):
        abort(400,'No _id specified')
    try:
        db['equipements'].save(entity)
    except ValidationError as ve:
        abort(400, str(ve))

@route('/equipements', method='POST')
def post_equipement():
    data = request.forms

    if not data:
        abort(400, 'No data received')
    entity = {}
    for k,v  in data.items():
        entity[k]=v

    if not entity.has_key('_id'):
        abort(400,'No _id specified')
    try:
        db['equipements'].save(entity)
    except ValidationError as ve:
        abort(400, str(ve))


@route('/equipements/:id', methodd='GET')
def get_equipement(id):
    entity = db['equipements'].find_one({'_id':id})
    if not entity:
        abort(404, 'No equipement with id %s' % id)
    return entity

@route('/equipements', methodd='GET')
def get_equipements():
    entity = db['equipements'].find({})
    if not entity:
        abort(404, 'No equipement')
    response.content_type = 'application/json'
    entries = [entry for entry in entity]
    return MongoEncoder().encode(entries)

run(host='0.0.0.0', port=80)

EDIT : JQGrid :

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Rest Proxy Example</title>
    <link rel="stylesheet" type="text/css" href="/static/css/ui.jqgrid.css" />
    <link rel="stylesheet" type="text/css" href="/static/css/jquery-ui-1.8.20.custom.css" />

    <script type="text/javascript" src="/static/js/jquery.js"></script>
    <script type="text/javascript" src="/static/js/jquery.jqGrid.min.js"></script>
    <script type="text/javascript" src="/static/js/grid.locale-fr.js"></script>
    <script type="text/javascript">
        jQuery(document).ready(function(){
            var lastsel;

jQuery("#list2").jqGrid({
    url:'equipements',
    datatype: "json",
    colNames:['Id','Name', 'Plateforme'],
    colModel:[
        {name:'_id',index:'_id', width:50, editable:true},
        {name:'name',index:'_id', width:300, editable:true},
        {name:'plateforme',index:'total', width:200,align:"right", editable:true},
    ],
    rowNum:30,
    rowList:[10,20,30],
    pager:'pager2',
    sortname: '_id',
    viewrecords: true,
    width: 600,
    height: "100%",
    sortorder: "desc",
    onSelectRow: function(_id){
        if(_id && _id!==lastsel){
            jQuery('#liste2').jqGrid('restoreRow',lastsel);
            jQuery('#liste2').jqGrid('editRow',_id,true);
            lastsel=_id;
        }
    },
    jsonReader: {
        repeatitems: false,
        id: "_id",
        root: function (obj) { return obj; },
        records: function (obj) { return obj.length; },
        page: function (obj) { return 1; },
        total: function (obj) { return 1; }
    },
    editurl:'equipements',
    caption:"Equipements"
});
jQuery("#list2").jqGrid('navGrid','#pager2',{edit:true,add:true,del:true});
});
    </script>
</head>
<body>
    <table id="list2"></table>
    <div id="pager2"></div>
    <br />

</body>
</html>
like image 811
Yohann Avatar asked May 24 '12 07:05

Yohann


1 Answers

You can use jqGrid to communicate with your RESTfull service. jqGrid supports tree editing modes: cell editing, inline editing and form editing. Moreover the inline editing can be initialized in different ways. For example one can call editRow method inside of onSelectRow or ondblClickRow callback or use navGrid to add "Delete" button in the navigator toolbar and inlineNav to add "Add" and "Edit" buttons. Another way will be to use formatter: "actions" to include "editing" buttons in one column of the grid. You can find all the ways in the official jqGrid demo. More technical implementation details you can find in the answer.

I find personally important that you understand another important aspect of the usage of RESTfull services in the web front-end. The problem is that standard RESTfull API don't any standard interface for sorting, paging and filtering of the data. I try to explain the problem below. After that I hope it will be clear my recommendation to extend the current standard RESTfull API with additional methods which have additional parameters and which provide sorting, paging and filtering functionality.

The problem is very easy to understand if you have large dataset. It has no sense to display in the grid for example 10000 rows of data at once. The user is unable to see the data on the screen without scolling or paging of data. Moreover because of the same reasons it has sense to implement sorting and even filtering of the data. So it is much more practical to display only one page of data at the beginning and give the user some interface for paging the data. In the standard pager of jqGrid which looks like

enter image description here

the user can go to the "Next", "Last", "Previous" or "First" page or choose the page size:

enter image description here

additionally the user can specify the desired page by direct input of the new page and pressing of Enter:

enter image description here

In the same way the user can click on the header of any column to sort the grid data by the column:

enter image description here

Another very important user interface element (important from the users point of view) could be some filtering interface like here for example or searching interface like here.

I give you example from jqGrid, but I find the problem as common. I want to emphasize that RESTfull service gives you no standard interface for sorting, paging and filtering of the data.

In case of usage jqGrid the RESTfull url will get by default additional parameters. It's: page, rows which specify the page number and the page size which will be asked from the service, sidx and sord parameters which specify the sorting column and the sorting direction and _search and filters (the last is in the format) which allows to support filtering. One can rename the parameters if needed using prmNames option of jqGrid.

I recommend you to read the answer on the question which are asked about the encoding of the URL. I think that the part _search=false&rows=20&page=1&sidx=&sord=asc is not belong to resource and so it's better to send the information as parameters and not as the part of URL.

What I mainly wanted to express in my answer is that the usage of pure classical RESTfull API is not sufficient to implement good user interface. You will have to extend the interface with additional parameters used for paging, sorting and filtering or you will be not able to create performant and user friendly web GUI.

like image 77
Oleg Avatar answered Sep 18 '22 01:09

Oleg