您的位置:首页 > 产品设计 > UI/UE

Telerik Kendo UI 那点事【3】GridView MVVM前后台联动服务端分页、分组、过滤(查询)、排序

2014-09-25 18:43 627 查看
中文化之后,我们开始具体使用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" : true
4、注意对日期字段的描述设置。通过如下代码的设置,日期字段的过滤器设置,将变为日历控件。便于操作!

{
"field" : "bdate",
"title" : "出生日期",
"filterable" : {
"ui" : "datetimepicker"
},
"format" : "{0:yyyy-MM-dd HH:mm:ss}"
}


5、注意对最后一列显示的按钮的设置。其中name字段的属性值是固定的,只有3种。text为外观显示的中文,如果不设置的话,会是英文。

{
"title" : " ",
"command" : [ {
"text" : "编辑",
"name" : "edit"
}, {
"text" : "删除",
"name" : "destroy"
} ]
}


6、代码最后的部分。指定了是直接在行编辑,还是弹出窗口编辑。如果是弹出窗口将inline改成popup即可,个人提倡用popup,因为popup模式下,弹出窗口里的属性是所有属性,列表hidden设置为true的属性也会显示在弹出窗口中。这样可以达到列表显示简洁,编辑时又不缺字段属性的目的。

"editable" : {
"mode" : "inline"
}


7、如果你希望列宽能够被调整,那么要设置resizeable为true,但是如果你设置它为真,则必须给所有的列指定width属性,否则,没有指定width的列会有无法显示的情形。从hidden变为显示时,也会无法呈现!

8、GridView顶部的Toolbar工具条,设置与最后一列两个按钮的配置类似,需要自行指定中文。

"toolbar" : [ {
"text" : "新增记录",
"name" : "create"
} ],


至于后台的两个关键类,它山之石可以攻玉,取来直接用即可!很好的模型封装,无需再改动什么。要动的是我们的后台设计实现。也许又有人说,我的查询是复杂查询,多表联合的,肯定必须写复杂的sql。我只想说,你是否能考虑把它做成一个通用的sql形成视图,将视图映射为hibernate实体,然后将视图实体直接绑定给GridView,不就可以省下很多事!事在人为!设计的力量就是可以让我们去多思考,用极少量的功耗,实现极美的效果!

未完待续……
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐