标签:spring mvc mvvm kendo ui json date
中文化之后,我们开始具体使用kendo ui组件。经常开发系统的我,通常从最常用的控件用起,那就是表格控件GridView!现在的软件系统基本上就是标签框、文本框、选择框、树、表格堆砌而成。因此接触任何一种UI组件的时候,我通常都最为关注GridView,表格控件。小小的表格控件,做的好,能够给前端呈现和使用带来很好的感受的同时,还能够大幅度降低开发的工作量。
Kendo UI在这方面让我十分满意,甚至是震惊。因为它的GridView不仅仅是能够简单的在呈现层进行数据的过滤、排序、分组,关键是还能通过简单的配置,让界面的过滤、排序、分组与后台直接联动。相当于只要是呈现的数据列,如果要查询,根本不必再在表格的上方做一堆标签选择区。当然这也是对软件开发模型的一大挑战,习惯了通过标签框、选择框等一堆控件指定条件,生成where条件,然后在点查询的时候,进行后台查询再呈现的开发方式,已经完全过时,不仅不够高效,而且开发起来浪费大量时间,用户使用时,也并不直观,对于条件对查询后果的影响,用户根本不清楚,有时因为设置大量查询条件,而导致没有查询结果时,还很恼火。
二、直接联动,无sql,无拼装,查询呈现。
kendo ui 的GridView控件,通过MVVM绑定数据源,通过REST-Full风格的URL来与spring mvc的后台进行通信,就使用简单的GridView,就能直接实现对后台数据的实时查询、修改、增加、删除。并且无需写什么sql或者hql,也不需要拼装什么nosql的条件。一切都是完美联动。界面的操作会生成条件对象,整个框架体系浑然一体,后端hibernate通过对前端对象反馈的条件数据进行自动拼装,查询得到结果。
说了这么多,很多人看的可能觉得头晕,不如代码来的爽。那么就让我们领教一下kendo UI的GridView之美吧!
页面的头部就忽略了,需要head的到前一篇文章看一下吧。body的部分,如下写,就可以使用GridView。
头部首先要增加REST-Full风格的CURD连接定义(个人习惯,便于工程管理,你也可以直接写到js里)
<c:url value="/kendo/test6.json" var="transportReadUrl" /> <c:url value="/kendo/test6c.json" var="transportCreateUrl" /> <c:url value="/kendo/test6u.json" var="transportUpdateUrl" /> <c:url value="/kendo/test6d.json" var="transportDestroyUrl" />
然后是body部分:
<div> <div id="grid" style="height:580px"></div> </div> <script> $(document).ready(function() { $("#grid").kendoGrid({ "columnMenu" : true, "dataSource" : { "schema" : { "total" : "total", "model" : { "id" : "objectId", "fields" : { "objectId" : { "type" : "number" }, "age" : { "type" : "number" }, "name" : { "type" : "string" }, "bdate" : { "type" : "date" } } }, "data" : "data", "groups" : "data" }, "serverFiltering" : true, "transport" : { "destroy" : { "dataType" : "json", "contentType" : "application/json", "type" : "POST", "url" : "${transportDestroyUrl}" }, "update" : { "dataType" : "json", "contentType" : "application/json", "type" : "POST", "url" : "${transportUpdateUrl}" }, "read" : { "dataType" : "json", "contentType" : "application/json", "type" : "POST", "url" : "${transportReadUrl}" }, "create" : { "dataType" : "json", "contentType" : "application/json", "type" : "POST", "url" : "${transportCreateUrl}" }, "parameterMap" : function(options) { return JSON.stringify(options); } }, "batch" : false, "serverSorting" : true, "pageSize" : 10.0, "serverPaging" : true, "serverGrouping" : true }, "toolbar" : [ { "text" : "新增记录", "name" : "create" } ], "reorderable" : true, "filterable" : true, "pageable" : { "input" : true, "buttonCount" : 5.0, "pageSize" : 10.0, "pageSizes" : [ 5, 10, 15, 20, 30 ], "refresh" : true }, "sortable" : true, "columns" : [ { "field" : "objectId", "title" : "编号", "hidden" : true }, { "field" : "name", "title" : "姓名" }, { "field" : "age", "title" : "年龄" }, { "field" : "bdate", "title" : "出生日期", "filterable" : { "ui" : "datetimepicker" }, "format" : "{0:yyyy-MM-dd HH:mm:ss}" }, { "title" : " ", "command" : [ { "text" : "编辑", "name" : "edit" }, { "text" : "删除", "name" : "destroy" } ] } ], "groupable" : true, "editable" : { "mode" : "inline" } }); }); </script>
接下来,后台spring MVC控制层如下:
@Controller public class KendoControl { @Autowired private SessionFactory sessionFactory; @RequestMapping(value = "/kendo/test6d", method = RequestMethod.POST) public TestObject delete(@RequestBody TestObject model) { System.out.println("delete Object-->" + model); sessionFactory.getCurrentSession().delete(model); return model; } @RequestMapping(value = "/kendo/test6u", method = RequestMethod.POST) public TestObject update(@RequestBody TestObject model) { System.out.println("update mapObject-->" + model); Session session = sessionFactory.getCurrentSession(); session.saveOrUpdate(model); session.flush(); return model; } @RequestMapping(value = "/kendo/test6c", method = RequestMethod.POST) public TestObject create(@RequestBody TestObject model) { System.out.println("create mapObject-->" + model); Session session = sessionFactory.getCurrentSession(); session.saveOrUpdate(model); session.flush(); return model; } @RequestMapping(value = "/kendo/test6", method = RequestMethod.POST) public DataSourceResult kendoTest6( @RequestBody com.kendoui.spring.models.DataSourceRequest request) { System.out.println("RequestBody-->" + request.toString()); return request.toDataSourceResult(sessionFactory.getCurrentSession(), TestObject.class); } }各位肯定发现了,里面有个DataSourceRequest类,还有个DataSourceResult类,所有的查询都和Request相关,核心的实现已经形成了模式,并且通过模型完成了界面查询与后端查询的映射,因此界面选择过滤、分组、排序等等操作时,read url会被调用,参数会被传到spring的mvc控制层,并且反序列化为DataSourceRequest对象,对象即实现了所有的操作,不需要我们再去拼装什么sql!伟大而美丽吧!这两个类是从kendo ui for JSP的DEMO工程中直接cp过来使用的,其中由于日期格式化的问题,修改了DataSourceRequest类。下面给出他们的源代码!
实体类TestObject:
import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; /** * TestObject entity. @author MyEclipse Persistence Tools */ @Entity @Table(name="test_object") public class TestObject implements java.io.Serializable { // Fields private Long objectId; private String name; private Integer age; private Date bdate; // Constructors /** default constructor */ public TestObject() { } /** minimal constructor */ public TestObject(Long id) { this.objectId = id; } /** full constructor */ public TestObject(Long id, String name, Integer age) { this.objectId = id; this.name = name; this.age = age; } // Property accessors @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name="id") public Long getObjectId() { return this.objectId; } public void setObjectId(Long id) { this.objectId = id; } @Column(name="name") public String getName() { return this.name; } public void setName(String name) { this.name = name; } @Column(name="age") public Integer getAge() { return this.age; } public void setAge(Integer age) { this.age = age; } @Temporal(TemporalType.TIMESTAMP) @Column(name="bdate", length = 11) public Date getBdate() { return bdate; } public void setBdate(Date bdate) { this.bdate = bdate; } @Override public String toString() { return "TestObject [objectId=" + objectId + ", name=" + name + ", age=" + age + ", bdate=" + bdate + "]"; } }
DataSourceRequest:
package com.kendoui.spring.models; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.codehaus.jackson.annotate.JsonAnySetter; import org.hibernate.Criteria; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.criterion.Criterion; import org.hibernate.criterion.Junction; import org.hibernate.criterion.MatchMode; import org.hibernate.criterion.Order; import org.hibernate.criterion.ProjectionList; import org.hibernate.criterion.Projections; import org.hibernate.criterion.Restrictions; import org.hibernate.criterion.SimpleExpression; import org.hibernate.transform.ResultTransformer; public class DataSourceRequest { private int page; private int pageSize; private int take; private int skip; private List<SortDescriptor> sort; private List<GroupDescriptor> group; private List<AggregateDescriptor> aggregate; private HashMap<String, Object> data; private FilterDescriptor filter; public DataSourceRequest() { filter = new FilterDescriptor(); data = new HashMap<String, Object>(); } public HashMap<String, Object> getData() { return data; } @JsonAnySetter public void handleUnknown(String key, Object value) { data.put(key, value); } public int getPage() { return page; } public void setPage(int page) { this.page = page; } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } public int getTake() { return take; } public void setTake(int take) { this.take = take; } public int getSkip() { return skip; } public void setSkip(int skip) { this.skip = skip; } public List<SortDescriptor> getSort() { return sort; } public void setSort(List<SortDescriptor> sort) { this.sort = sort; } public FilterDescriptor getFilter() { return filter; } public void setFilter(FilterDescriptor filter) { this.filter = filter; } private static void restrict(Junction junction, FilterDescriptor filter, Class<?> clazz) { String operator = filter.getOperator(); String field = filter.getField(); Object value = filter.getValue(); boolean ignoreCase = filter.isIgnoreCase(); try { Class<?> type = new PropertyDescriptor(field, clazz) .getPropertyType(); if (type == double.class || type == Double.class) { value = Double.parseDouble(value.toString()); } else if (type == float.class || type == Float.class) { value = Float.parseFloat(value.toString()); } else if (type == long.class || type == Long.class) { value = Long.parseLong(value.toString()); } else if (type == int.class || type == Integer.class) { value = Integer.parseInt(value.toString()); } else if (type == short.class || type == Short.class) { value = Short.parseShort(value.toString()); } else if (type == boolean.class || type == Boolean.class) { value = Boolean.parseBoolean(value.toString()); } else if (type == Date.class) { SimpleDateFormat df = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); String input = value.toString(); value = df.parse(input); } } catch (IntrospectionException e) { } catch (NumberFormatException nfe) { } catch (ParseException e) { } switch (operator) { case "eq": if (value instanceof String) { junction.add(Restrictions.ilike(field, value.toString(), MatchMode.EXACT)); } else { junction.add(Restrictions.eq(field, value)); } break; case "neq": if (value instanceof String) { junction.add(Restrictions.not(Restrictions.ilike(field, value.toString(), MatchMode.EXACT))); } else { junction.add(Restrictions.ne(field, value)); } break; case "gt": junction.add(Restrictions.gt(field, value)); break; case "gte": junction.add(Restrictions.ge(field, value)); break; case "lt": junction.add(Restrictions.lt(field, value)); break; case "lte": junction.add(Restrictions.le(field, value)); break; case "startswith": junction.add(getLikeExpression(field, value.toString(), MatchMode.START, ignoreCase)); break; case "endswith": junction.add(getLikeExpression(field, value.toString(), MatchMode.END, ignoreCase)); break; case "contains": junction.add(getLikeExpression(field, value.toString(), MatchMode.ANYWHERE, ignoreCase)); break; case "doesnotcontain": junction.add(Restrictions.not(Restrictions.ilike(field, value.toString(), MatchMode.ANYWHERE))); break; } } private static Criterion getLikeExpression(String field, String value, MatchMode mode, boolean ignoreCase) { SimpleExpression expression = Restrictions.like(field, value, mode); if (ignoreCase == true) { expression = expression.ignoreCase(); } return expression; } private static void filter(Criteria criteria, FilterDescriptor filter, Class<?> clazz) { if (filter != null) { List<FilterDescriptor> filters = filter.filters; if (!filters.isEmpty()) { Junction junction = Restrictions.conjunction(); if (!filter.getFilters().isEmpty() && filter.getLogic().toString().equals("or")) { junction = Restrictions.disjunction(); } for (FilterDescriptor entry : filters) { if (!entry.getFilters().isEmpty()) { filter(criteria, entry, clazz); } else { restrict(junction, entry, clazz); } } criteria.add(junction); } } } private static void sort(Criteria criteria, List<SortDescriptor> sort) { if (sort != null && !sort.isEmpty()) { for (SortDescriptor entry : sort) { String field = entry.getField(); String dir = entry.getDir(); if (dir.equals("asc")) { criteria.addOrder(Order.asc(field)); } else if (dir.equals("desc")) { criteria.addOrder(Order.desc(field)); } } } } private List<?> groupBy(List<?> items, List<GroupDescriptor> group, Class<?> clazz, final Session session, List<SimpleExpression> parentRestrictions) throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { ArrayList<Map<String, Object>> result = new ArrayList<Map<String, Object>>(); if (!items.isEmpty() && group != null && !group.isEmpty()) { List<List<SimpleExpression>> restrictions = new ArrayList<List<SimpleExpression>>(); GroupDescriptor descriptor = group.get(0); List<AggregateDescriptor> aggregates = descriptor.getAggregates(); final String field = descriptor.getField(); Method accessor = new PropertyDescriptor(field, clazz) .getReadMethod(); Object groupValue = accessor.invoke(items.get(0)); List<Object> groupItems = createGroupItem(group.size() > 1, clazz, session, result, aggregates, field, groupValue, parentRestrictions); List<SimpleExpression> groupRestriction = new ArrayList<SimpleExpression>( parentRestrictions); groupRestriction.add(Restrictions.eq(field, groupValue)); restrictions.add(groupRestriction); for (Object item : items) { Object currentValue = accessor.invoke(item); if (!groupValue.equals(currentValue)) { groupValue = currentValue; groupItems = createGroupItem(group.size() > 1, clazz, session, result, aggregates, field, groupValue, parentRestrictions); groupRestriction = new ArrayList<SimpleExpression>( parentRestrictions); groupRestriction.add(Restrictions.eq(field, groupValue)); restrictions.add(groupRestriction); } groupItems.add(item); } if (group.size() > 1) { Integer counter = 0; for (Map<String, Object> g : result) { g.put("items", groupBy((List<?>) g.get("items"), group.subList(1, group.size()), clazz, session, restrictions.get(counter++))); } } } return result; } private List<Object> createGroupItem(Boolean hasSubgroups, Class<?> clazz, final Session session, ArrayList<Map<String, Object>> result, List<AggregateDescriptor> aggregates, final String field, Object groupValue, List<SimpleExpression> aggregateRestrictions) { Map<String, Object> groupItem = new HashMap<String, Object>(); List<Object> groupItems = new ArrayList<Object>(); result.add(groupItem); if (groupValue instanceof Date) { // format date SimpleDateFormat formatter = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); String formattedDate = formatter.format(((Date) groupValue) .getTime()); groupItem.put("value", formattedDate); } else { groupItem.put("value", groupValue); } groupItem.put("field", field); groupItem.put("hasSubgroups", hasSubgroups); if (aggregates != null && !aggregates.isEmpty()) { Criteria criteria = session.createCriteria(clazz); filter(criteria, getFilter(), clazz); // filter the set by the // selected criteria SimpleExpression currentRestriction = Restrictions.eq(field, groupValue); if (aggregateRestrictions != null && !aggregateRestrictions.isEmpty()) { for (SimpleExpression simpleExpression : aggregateRestrictions) { criteria.add(simpleExpression); } } criteria.add(currentRestriction); groupItem.put("aggregates", calculateAggregates(criteria, aggregates)); } else { groupItem.put("aggregates", new HashMap<String, Object>()); } groupItem.put("items", groupItems); return groupItems; } @SuppressWarnings({ "serial", "unchecked" }) private static Map<String, Object> calculateAggregates(Criteria criteria, List<AggregateDescriptor> aggregates) { return (Map<String, Object>) criteria .setProjection(createAggregatesProjection(aggregates)) .setResultTransformer(new ResultTransformer() { @Override public Object transformTuple(Object[] value, String[] aliases) { Map<String, Object> result = new HashMap<String, Object>(); for (int i = 0; i < aliases.length; i++) { String alias = aliases[i]; Map<String, Object> aggregate; String name = alias.split("_")[0]; if (result.containsKey(name)) { ((Map<String, Object>) result.get(name)).put( alias.split("_")[1], value[i]); } else { aggregate = new HashMap<String, Object>(); aggregate.put(alias.split("_")[1], value[i]); result.put(name, aggregate); } } return result; } @SuppressWarnings("rawtypes") @Override public List transformList(List collection) { return collection; } }).list().get(0); } private static ProjectionList createAggregatesProjection( List<AggregateDescriptor> aggregates) { ProjectionList projections = Projections.projectionList(); for (AggregateDescriptor aggregate : aggregates) { String alias = aggregate.getField() + "_" + aggregate.getAggregate(); if (aggregate.getAggregate().equals("count")) { projections.add(Projections.count(aggregate.getField()), alias); } else if (aggregate.getAggregate().equals("sum")) { projections.add(Projections.sum(aggregate.getField()), alias); } else if (aggregate.getAggregate().equals("average")) { projections.add(Projections.avg(aggregate.getField()), alias); } else if (aggregate.getAggregate().equals("min")) { projections.add(Projections.min(aggregate.getField()), alias); } else if (aggregate.getAggregate().equals("max")) { projections.add(Projections.max(aggregate.getField()), alias); } } return projections; } private List<?> group(final Criteria criteria, final Session session, final Class<?> clazz) { List<?> result = new ArrayList<Object>(); List<GroupDescriptor> group = getGroup(); if (group != null && !group.isEmpty()) { try { result = groupBy(criteria.list(), group, clazz, session, new ArrayList<SimpleExpression>()); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | HibernateException | IntrospectionException e) { e.printStackTrace(); } } return result; } private static long total(Criteria criteria) { long total = Long.parseLong(criteria .setProjection(Projections.rowCount()).uniqueResult() .toString()); criteria.setProjection(null); criteria.setResultTransformer(Criteria.ROOT_ENTITY); return total; } private static void page(Criteria criteria, int take, int skip) { criteria.setMaxResults(take); criteria.setFirstResult(skip); } public DataSourceResult toDataSourceResult(Session session, Class<?> clazz) { Criteria criteria = session.createCriteria(clazz); filter(criteria, getFilter(), clazz); long total = total(criteria); sort(criteria, sortDescriptors()); page(criteria, getTake(), getSkip()); DataSourceResult result = new DataSourceResult(); result.setTotal(total); List<GroupDescriptor> groups = getGroup(); if (groups != null && !groups.isEmpty()) { result.setData(group(criteria, session, clazz)); } else { result.setData(criteria.list()); } List<AggregateDescriptor> aggregates = getAggregate(); if (aggregates != null && !aggregates.isEmpty()) { result.setAggregates(aggregate(aggregates, getFilter(), session, clazz)); } return result; } private static Map<String, Object> aggregate( List<AggregateDescriptor> aggregates, FilterDescriptor filters, Session session, Class<?> clazz) { Criteria criteria = session.createCriteria(clazz); filter(criteria, filters, clazz); return calculateAggregates(criteria, aggregates); } private List<SortDescriptor> sortDescriptors() { List<SortDescriptor> sort = new ArrayList<SortDescriptor>(); List<GroupDescriptor> groups = getGroup(); List<SortDescriptor> sorts = getSort(); if (groups != null) { sort.addAll(groups); } if (sorts != null) { sort.addAll(sorts); } return sort; } public List<GroupDescriptor> getGroup() { return group; } public void setGroup(List<GroupDescriptor> group) { this.group = group; } public List<AggregateDescriptor> getAggregate() { return aggregate; } public void setAggregate(List<AggregateDescriptor> aggregate) { this.aggregate = aggregate; } public static class SortDescriptor { private String field; private String dir; public String getField() { return field; } public void setField(String field) { this.field = field; } public String getDir() { return dir; } public void setDir(String dir) { this.dir = dir; } @Override public String toString() { return "SortDescriptor [field=" + field + ", dir=" + dir + "]"; } } public static class GroupDescriptor extends SortDescriptor { private List<AggregateDescriptor> aggregates; public GroupDescriptor() { aggregates = new ArrayList<AggregateDescriptor>(); } public List<AggregateDescriptor> getAggregates() { return aggregates; } @Override public String toString() { return "GroupDescriptor [aggregates=" + aggregates + "]"; } } public static class AggregateDescriptor { private String field; private String aggregate; public String getField() { return field; } public void setField(String field) { this.field = field; } public String getAggregate() { return aggregate; } public void setAggregate(String aggregate) { this.aggregate = aggregate; } @Override public String toString() { return "AggregateDescriptor [field=" + field + ", aggregate=" + aggregate + "]"; } } public static class FilterDescriptor { private String logic; private List<FilterDescriptor> filters; private String field; private Object value; private String operator; private boolean ignoreCase = true; public FilterDescriptor() { filters = new ArrayList<FilterDescriptor>(); } public String getField() { return field; } public void setField(String field) { this.field = field; } public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } public String getOperator() { return operator; } public void setOperator(String operator) { this.operator = operator; } public String getLogic() { return logic; } public void setLogic(String logic) { this.logic = logic; } public boolean isIgnoreCase() { return ignoreCase; } public void setIgnoreCase(boolean ignoreCase) { this.ignoreCase = ignoreCase; } public List<FilterDescriptor> getFilters() { return filters; } @Override public String toString() { return "FilterDescriptor [logic=" + logic + ", filters=" + filters + ", field=" + field + ", value=" + value + ", operator=" + operator + ", ignoreCase=" + ignoreCase + "]"; } } @Override public String toString() { return "DataSourceRequest [page=" + page + ", pageSize=" + pageSize + ", take=" + take + ", skip=" + skip + ", sort=" + sort + ", group=" + group + ", aggregate=" + aggregate + ", data=" + data + ", filter=" + filter + "]"; } }
DataSourceResult:
package com.kendoui.spring.models; import java.util.List; import java.util.Map; public class DataSourceResult { private long total; private List<?> data; private Map<String, Object> aggregates; public long getTotal() { return total; } public void setTotal(long total) { this.total = total; } public List<?> getData() { return data; } public void setData(List<?> data) { this.data = data; } public Map<String, Object> getAggregates() { return aggregates; } public void setAggregates(Map<String, Object> aggregates) { this.aggregates = aggregates; } }
这里要强调一些细节。主要是kendo ui GridView使用的细节,也是CSDN另位博主 引路蜂未说明的:
1、dataSource的transport成员的parameterMap方法,必须进行覆盖重写,这个与后台spring的mvc映射密切相关。重写parameterMap方法,才能对后台提交数据时提交json对象,参数才能正常反序列化!切记!
"parameterMap" : function(options) { return JSON.stringify(options); }
2、dataSource的schema成员的model成员,必须定义id属性,指向实体的ID。否则控件自身的修改、删除、增加操作会混乱,无法正常工作。现象是增加记录会多出2条,修改记录也会多出记录。因为控件无法正常识别修改、增加的操作,到底针对的记录是什么情况。而取消按钮又会被识别成删除操作导致增加或修改过程中取消,记录被删除的问题。因此一定要注意model成员的id属性一定要指定对应的id,否则GridView只能进行查询操作,不能进行增、删、改。
"dataSource" : { "schema" : { "total" : "total", "model" : { "id" : "objectId", "fields" : { "objectId" : { "type" : "number" }, "age" : { "type" : "number" }, "name" : { "type" : "string" }, "bdate" : { "type" : "date" } } },3、注意设置GridView自身的下列属性,以确保开启服务端分页、服务端查询、服务端过滤、服务端分组,也就是我说的界面与后台的直接联动。而不是将数据查询到界面缓冲后,由界面再分页、过滤、分组!
"serverSorting" : true, "serverPaging" : true, "serverGrouping" : true4、注意对日期字段的描述设置。通过如下代码的设置,日期字段的过滤器设置,将变为日历控件。便于操作!
{ "field" : "bdate", "title" : "出生日期", "filterable" : { "ui" : "datetimepicker" }, "format" : "{0:yyyy-MM-dd HH:mm:ss}" }
{ "title" : " ", "command" : [ { "text" : "编辑", "name" : "edit" }, { "text" : "删除", "name" : "destroy" } ] }
"editable" : { "mode" : "inline" }
7、如果你希望列宽能够被调整,那么要设置resizeable为true,但是如果你设置它为真,则必须给所有的列指定width属性,否则,没有指定width的列会有无法显示的情形。从hidden变为显示时,也会无法呈现!
8、GridView顶部的Toolbar工具条,设置与最后一列两个按钮的配置类似,需要自行指定中文。
"toolbar" : [ { "text" : "新增记录", "name" : "create" } ],
未完待续……
标签:spring mvc mvvm kendo ui json date
原文地址:http://blog.csdn.net/supperman_009/article/details/39555029