Editable jQuery Grid Recommendations with REST API
Asked Answered
R

1

7

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>
Radian answered 24/5, 2012 at 7:3 Comment(2)
"I do not know javascript" - That is going to have to change if you are trying to make a frontend for a REST client. Take the time to learn JavaScript; it will payoff in the end and it isn't a very difficult language.Chainsmoke
It's only for the internal team and it's not my job to write web frontend. One day, i will learn javascript. But now, i have lot of more important stuff.Radian
P
7

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.

Paraformaldehyde answered 29/5, 2012 at 12:48 Comment(9)
Thanks for this long answer. Bit it doesn't answer to my question : In fact, it's not possible to use jQGrid with my REST backend. jQGrid use a specific jsojn format : { total: xxx, page: yyy, records: zzz, rows: [ {name1:”Row01″,name2:”Row 11″,name3:”Row 12″,name4:”Row 13″,name5:”Row 14″}, ...Radian
@Yohann: No! jqGrid can read almost any JSON format you should just use the corresponding jsonReader. You don't described your current data format detailed, but I suppose that the following jsonReader will work in your case: 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; }}Paraformaldehyde
@Yohann: The standard format of input data looks a little strange, but if you send from the server array of strings instead of named properties you can reduce the size of data transferred between the server and the client. Moreover it require the user to specify the unique id of every row because the id will be used as id of <tr> - the rowid.Paraformaldehyde
thanks ! Now, i have to just to fix some javascript to have editable cell !Radian
@Yohann: You are welcome! You should just choose the editing mode which the mostly corresponds your requirements.Paraformaldehyde
i try with the exemple "Using event" but it doesn't work at all. (it works with the menu but it's less intuitive). So, can you explain how chose the editing mode ?Radian
@Yohann: I don't understand which example you mean under "Using event". The best choice of editing mode depend on many criteria. For example do you want to display the grid to edit only or you want to display grid to allow user to scroll the data and gives the user additional possibility to edit data. In the last case you can use ondblClickRow for example to start editing. If you beginner with jqGrid you should don't forget to include editable: true in every column which you want to allow to edit.Paraformaldehyde
Sorry, i wanted to say "on select" a row (and no and double click on a row). As you can see, i edit my post yesterday with my jqGrid implementation and onSelectRow. But when a select a row, i can't edit this. (but it works with the menu.)Radian
@Yohann: I see only one clear error in your code is: you have to include grid.locale-fr.js before jquery.jqGrid.min.js. Instead of jQuery('#liste2') inside of any callback like onSelectRow it's better to use jQuery(this). The main problem of your code is that it don't use any RESTfull settings (see the answer) which I referenced in my answer. Is it not important for you now or you want to do this in the next step?Paraformaldehyde

© 2022 - 2024 — McMap. All rights reserved.