extjs表格展开更多内容

事情是从官方的一个表格的例子开始的。

就是其中第一个名为“Expander Rows in a Collapsible Grid”的表格吸引了飞尘。

通过这个可以展开和折叠的小功能,我们就可以直接在表格中展现更多的内容,而不需要新窗口或者额外的页面。

这么好的方法自然要用到项目中了,找来先有的表格做测试,对比了一下,与官方例子主要的差别就在于这个plugins属性了。

于是直接copy来,修改为自己的数据字段,并加入到原有的grid创建代码中。

?View Code JAVASCRIPT
plugins: [{
		ptype: 'rowexpander',
		rowBodyTpl : [
				'<p><b>name:</b> {name}</p><br>',
				'<p><b>age:</b> {age}</p>'
		]
}],

意料之中的页面出现错误,直接就不显示表格了,看来这个plugins并不简单啊,呵呵。

又去观察了一下官方例子,发现引用了另外的js:

examples/ux/RowExpander.js?_dc=1323855646488

原来这个不在extjs中,而是extjs的扩展插件。

在自己的extjs文件夹找了下,的确是有这个文件的,那就引用一下好了。

但是问题并没有彻底解决,这次报脚本错误了:

this.view is undefined

官方查到了这个bug,已经在新版本中进行了修正。

http://www.sencha.com/forum/showthread.php?141590-New-bug-in-RowExpander-plugin

http://www.sencha.com/forum/showthread.php?139187-rowexpander-does-not-work

项目启动时使用的extjs是4.0.2a版本,于是去看了下最新的版本,升级到了4.0.7。小版本的升级应该不会有太多变化,于是下载最新版本,替换现有的4.0.2a。把已有的功能过了一遍,没有问题,平滑升级。

关于之前发现的中文本地化一个问题,依然存在,于是手工修改了一下,详细内容就不多说了。

RowExpander的终于正常工作了。

赶快体验一下展开表格的酷炫感受吧。不幸的是又出现了新问题,当表格设置了高度,且加载的数据条目较少,表格不会有滚动条,当有多个记录被展开后,表格中内容的高度就超过了表格设定的高度,但是表格并未出现意料中的滚动条,如图所示:

而对于开始数据就比较多,表格已经有滚动条的情况,展开是没有问题的。

查看了RowExpander.js的源码,其中toggleRow就是展开、折叠时做的一些处理,方法的最后一行

this.view.up(‘gridpanel’).invalidateScroller();

似乎已经对滚动条进行了考虑,没有再继续追查,通过表格对象的getHeight()方法对比了折叠前后的表格高度,发现一样都是创建表格时设置的高度。

借鉴之前的经验,在这个时候再对表格执行一下doLayout(),使其重新渲染,但是没有效果,滚动条依旧没有出现。

经过查阅,refresh可以搞定,增加下面一句:

this.view.up(‘gridpanel’).view.refresh();

再来查看表格,展开时久违的滚动条终于露脸了。

提醒一下,最好保留原始的extjs所有文件,自己修改过的文件,复制一份到项目路径,修改最好添加简单的注释说明。这样以后升级版本时会方便很多。

webpy搭配wsgi使用session的问题

项目使用webpy作为python的web框架,同时借助于mod_wsgi搭建在apache服务器之上。

随着项目的不断推进,用户相关的需求被引入,自然就用到了session。

在webpy官方也给出了session及用户认证相关的例子,可以参考:

http://webpy.org/cookbook/sessions

http://webpy.org/cookbook/userauthpgsql

在结合自身项目时,遇到了点小问题,总是报这样的错误:

AttributeError: ‘function’ object has no attribute ‘add_processor’

经过一番折腾,在google讨论组找到了原因:

http://groups.google.com/group/webpy/browse_thread/thread/5a41ad7a10dca68f

对于wsgi是这样的

application = web.application(urls, globals(), autoreload=True).wsgifunc()

注意这里是globals()

而webpy官方给出的例子中是locals()

app = web.application(urls, locals())
session = web.session.Session(app, web.session.DiskStore(‘sessions’), initializer={‘count’: 0})

难怪会出错呢

原有的保持不变,新增加一个app用来为session服务就好了

app = web.application(urls, locals())

但是又遇到了新的问题:

AttributeError: ‘ThreadedDict’ object has no attribute

费了九牛二虎之力还是没有搞定,都准备放弃webpy了,看了一下django,重构的成本有点大,再说webpy不可能不行啊,终于还是在webpy官方找到了原因:

Note: mod_wsgi + sessions

If you use sessions with mod_wsgi, you should change you code like below:

app = web.application(urls, globals())

curdir = os.path.dirname(__file__)
session = web.session.Session(app, web.session.DiskStore(os.path.join(curdir,’sessions’)),)

application = app.wsgifunc()

现在来输出一下session_id就不再报错了

[Thu Dec 08 10:59:38 2011] [error]
[Thu Dec 08 10:59:38 2011] [error] da397a83fc1f92f4d4ed1fa919b6f50d105392a5

哎,真是的,当初只看了一半,看来有时候还是应该静下心来仔细的阅读一下文档,似乎做项目太浮躁了,只管实现就好,真是不应该啊。

解决extjs使用chart设置category未定义

本文主要是解决extjs使用chart设置category未定义的问题。

定义一个json对象,注意其中的数据类型都是字符串。

var data = [{
name : 'Jan',
value : '20'
}, {
name : 'Feb',
value : '25'
}];

定义store,此处没有指定数据类型,而这样会带来潜在的问题,后面就可以看到。

var store = Ext.create(‘Ext.data.Store’, {
fields : ['name', 'value'],
data : data
});

创建一个chart,此处设置为category并使用line折线类型。注意此处的数据类型为numeric。

?View Code JAVASCRIPT
var chart = Ext.create('Ext.chart.Chart', {
	width : 500,
	height : 300,
	store : store,
	renderTo : Ext.getBody(),
	legend : {
		position : 'bottom'
	},
	axes : [{
		type : 'Category',
		position : 'bottom',
		fields : 'name'
	}, {
		type : 'Numeric',
		position : 'left',
		fields : 'value',
		decimals : 0,
		grid : true,
		minimum : 0
	}],
	series : [{
		type : 'line',
		axis : 'left',
		xField : 'name',
		yField : 'value',
		markerCfg : {
			size : 1,
			radius : 1
		},
		tips : {
			width : 170,
			height : 60,
			renderer : function(storeItem, item) {
				this.setTitle(storeItem.get('name') + '<br />' + storeItem.get('value'));
			}
		}
	}]
});

放到Ext.onReady(function() {});里就可以看到效果了。

从图中可以看到,虽然图表画出来了,但是y轴的坐标是错误的。这就是前面data以及store中数据类型与chart中要求的数据不统一造成的。

不是说js若类型吗,但那也是有类型的。所以需要对data或者store中的数据类型进行一下预处理,以满足chart的需要。

store在初始化的时候就提供了数据类型的设置,可以自动完成类型的转换。

var store = Ext.create(‘Ext.data.Store’, {
fields : [{
name : 'name',
type : 'string'
}, {
name : 'value',
type : 'int'
}],
data : data
});

经过这样修改后,如图显示正常了。但是下面的问题才是本文的重点。

假如现在data中只有一条数据:

var data = [{
name : 'Jan',
value : '20'
}];

来看看extjs给我们的效果吧:

天哪,怎么这个样子啊,只有一个点折线自然是没有了,就这一个点也不知道跑到哪里了,似乎在左上角的地方。而且还有一个undefined,真是丑陋无比,在注重用户体验的今天这是无法容忍的。

好吧,那我们自己增加两个端点,让烦人的undefined见鬼去吧。

if (data.length === 1) {
data.push({
name : ‘after’
});
data.splice(0, 0, {
name : ‘before’
});
}

来看看效果,有了点进步,但是依然不理想。端点应该只是体现在x轴的标签上,折线就免了,因为两边不一定也不应该是0,理想的效果是,只显示原来的一个点,不需要有线。

经过了一番摸索,终于找到了解决的办法。对于data中的数据进行处理,将字符串类型的数据转换为数值型。

for (i in data) {
data[i].value = parseInt(data[i].value);
}

对于store依然保持原始的定义方式

var store = Ext.create(‘Ext.data.Store’, {
fields : ['name', 'value'],
data : data
});

最终的效果,这下看上去舒服多了。

mysql按天分组支持时区

时区问题总是个比较麻烦的问题,客户端与服务器的时区不一致自然是理所当然的事情,而对于多台服务器或者分布式再或者炙手可热的云,时区不统一也很正常,而且也不需要统一,还好有个时间戳的概念,通过时间戳就可以保证交互的过程中始终讨论的是同一个时间。

但是有些时候时间戳并不能满足要求,比如对于一个按天统计的报表,日期范围的选择来自于客户端,通过时间戳我们可以保证服务端返回的数据是属于客户端选择的日期范围中的,但是按天做统计就出现问题了。

服务器端是以mysql作为数据库,其中一张表名为session,其中记录时间戳的字段为ts,在做按日期分组查询时,主要的依据就是ts。

通过很简单的分组GROUP BY date(from_unixtime(ts))就可以统计到每天的记录数。

而这里from_unixtime得到的日期对象是以格林尼治时间为准的,也就是时区为+00:00

例如客户端需要展现2011年1月1日零时至2011年1月10日零时(不包含)的按天统计报表,客户端时区为+08:00,服务器处理此请求时,查询的结果可能会多出2011-01-10这个分组,原因是此时的分组时区不是客户端所期望的。

mysql提供了时区转换的函数CONVERT_TZ,通过它将from_unixtime得到的日期转换为客户端指定的时区就可以解决上面的问题了。

下面通过对比可以看出,统计出的结果是有很大区别的,而后者才是正确的。

SELECT from_unixtime(ts), count(*) AS num FROM session GROUP BY date(from_unixtime(ts))

2011-11-25 06:01:35 2011-11-25 14:01:35 105
2011-11-26 01:42:27 2011-11-26 09:42:27 28
2011-11-27 00:12:32 2011-11-27 08:12:32 39
2011-11-28 00:43:40 2011-11-28 08:43:40 70

SELECT from_unixtime(ts), count(*) AS num FROM session GROUP BY date(CONVERT_TZ(from_unixtime(ts),’+00:00′,’+08:00′))

2011-11-25 06:01:35 2011-11-25 14:01:35 95
2011-11-25 16:54:23 2011-11-26 00:54:23 22
2011-11-26 16:26:29 2011-11-27 00:26:29 33
2011-11-27 16:50:08 2011-11-28 00:50:08 92

对于extjs而言,客户端的时区信息可以这样获取:

Ext.Date.getGMTOffset(new Date());

返回’+0800′,使用时需要处理一下。

对于纯js:

new Date().getTimezoneOffset()/60;

返回-8,为什么呢?有空去查一下看看。

js秒数转换为时间形式

业务中有这样的需求,统计某个行为的时间长度,例如在google统计中的用户在页面停留时间,对于如何考量用户停留时间不是本文的讨论范围,有兴趣的朋友可以自行了解。

本文关注的是如何将业务中记录下来的使用时间长度,以一种更为友好直观的方式展现出来。统计时间通常是以两个时间戳的差作为基础记录下来,一般一毫秒为单位。js从服务器获取到相应数据后,需要对这样的毫秒数据进行处理,使其表现形式更佳。举例说明:

原始数据为3601000毫秒,为了计算方便此处假定数据已经初步处理为整型的以秒为单位的数据–3601,希望通过一个方法,得到以天、小时、分钟、秒为单位的时间形式,例如3天2小时15分钟32秒

本例中,经过转换的时间为1小时1秒

下面给出代码示例:

?View Code JAVASCRIPT
function secondToDate(second) {
	if (!second) {
		return 0;
	}
	var time = '';
	if (second >= 24 * 3600) {
		time += parseInt(second / (24 * 3600)) + '天';
		second %= 24 * 3600;
	}
	if (second >= 3600) {
		time += parseInt(second / 3600) + '小时';
		second %= 3600;
	}
	if (second >= 60) {
		time += parseInt(second / 60) + '分钟';
		second %= 60;
	}
	if (second > 0) {
		time += second + '秒';
	}
	return time;
}

没有对年做处理,业务本身不会用到,除非数据出现非法情况。当然如果你有需要,也可以添加哦。

————-2012-04-12更新————-

感谢loveunk,发现了一个bug,已经修正。

godaddy2012年2月最新30%优惠码

貌似很久没有收到godaddy发来的邮件了,似乎是由于很久没有在godaddy购买产品或者服务的缘故,莫非偶已经不是其的客户了?呵呵

从春季回家过年到现在,发现自己有一个月多没有写什么东西了,昨天晚上赶紧小补了一篇,希望以后自己还是要多多坚持啊。

说了写闲话,来看看今天的主角吧–godaddy优惠码,域名可以优惠30%,有兴趣的可以去围观一下。

优惠码gdx222bg

有效期2012-02-29

这里有godaddy快速通道