关于 Widget 的事件

呵呵,先祝贺发布了 1.0.1 新版本 :)

我看到 widget 目前只是封装了视图的渲染,并没有对事件进行处理,但是 CComponent 基础类中是提供了事件接口的。

不知道你对这一块的事件机制有没有什么考虑?因为我也在做这部分。

我目前的思路是这样的:

1、在渲染视图时既然调用了 widget,那么就等同于注册了 widget。此时 widget 就自动向视图注册自己需要响应的事件。

2、视图渲染完成后,根据注册的事件自动生成 json 字符串之类的,添加到视图中。

3、视图在浏览器显示时,事件 json 字符串会被自动调用,从而让一个公共 js 库知道应该拦截哪些事件。

4、事件发生后,js 库通过 ajax 将事件发送给服务端,服务端处理后返回处理结果。

困难的地方就是下面几点:

1、是不是所有事件都通过 ajax 来触发?

2、触发后的事件交给一个公共控制器处理还是一种特定的机制来自动调用 widget 的事件响应函数?

3、事件响应函数反馈的结果如何反应到页面上,同时又保持页面中其他 widget 的状态?

4、在一个 widget 是否可以访问另一个 widget 的属性呢?比如在 tree 的 onclick 事件中更新 list 的内容。

不知道 qiang 有没有什么思路,大家可以多多交流。

这是个好问题,也是MVC框架的一个"通病",其本质在于HTTP的stateless。在Prado里不存在这个问题,但是代价就是性能以及control开发的复杂性。

新发布的1.0.1版本采用了一个称之为action provider的折中解决办法(目前只有API文档)。一个widget可以拥有自己的action。如果在一个视图里使用了该widget,那么必须在controller的action map里声明这个widget。这样,如果该widget所属的action被触发了,controller就可以知道调用什么action。通常这个action是通过ajax触发的,这样可以保持页面的客户端状态。至于widget间的互相通讯主要是widget的设计问题,与框架无关。例如tree widget可以有target属性,用来指定onclick所应该更新的目标widget的id。

不知你有什么想法,欢迎一起讨论!

widget 通过返回 target 来更新其他 widget 数据是可行,但要取得其他 widget 的属性就不行了吧。而且在服务端实例化 widget 对象还有一个问题,就是 widget 对事件的处理很可能和 widget 当前的状态有关。

还是那个 tree 的例子:

1、点击一个 node,产生一个 click 事件;

2、执行服务端的事件处理函数;

3、事件处理函数根据引发 click 事件的 node 来返回不同的数据。这时就需要取得 widget 的当前状态了。甚至还可能需要根据 tree 的其他属性来作出不同的响应。

这种情况下,肯定要把 widget 的状态和事件一起发送到服务端才行。

再者,如果 tree 对事件的响应需要检查另一个 widget 的属性或状态,此时也不好处理。

例如一个 autocomplete 的输入框,需要根据 search type 单选框来决定是搜索标题还是正文。

这个问题我以前做过一些初步的验证,和 asp.net 以及 prado 的机制一样。

页面上产生了什么事件,所有控件(也就是 widget)的属性和状态信息都通过一个表单隐藏域提交。

制器处理收到这个表单后,先将所有控件的属性和状态还原,然后根据 event 字段的信息来调用不同的事件处理函数。

这样做服务端编写事件处理函数很自由,问题就是要实现这个效果,必须在每一次请求中把所有 widget 的属性和状态信息都提交回去,数据量稍微多一点就成问题。

现在我设想的是通过类似 REST (有状态的传输)的机制来解决。

1、所有客户端事件引发的 ajax 请求,都加入一个特定的 header(这个就是请求的特定状态);

2、框架接收到这个 ajax 请求后,通过检查 header,就可以确定是否是客户端事件;

3、确认是客户端事件时,直接构造需要处理该事件的 widget 对象(因此 widget 不依赖于控制器和视图);

4、widget 对象中如果要取得其他 widget 对象的属性和状态,必须预先声明。而声明则在 widget 渲染时进行。因此视图可以知道 widget 的特定事件需要哪些 widget 参与。从而在引发该 widget 的事件时,把其他 widget 的属性和状态信息打包提交到服务端。

我想这种方式,可以取得一个平衡。一是不需要时,不用把所有 widget 的信息都提交上去,保证了性能。需要时,可以通过简单的声明实现效果。虽然多写几行代码,我想还是值得的。

此外如果一个 widget 要更新另一个 widget,我想可以参考 xajax 的原理,通过返回一组特定的 js 代码来让客户端完成数据的更新。这样整个流程对开发人员是透明的,不需要开发人员编写大量的 js 代码。

呵呵,这个是我的初步设想,还没有去验证可行性。

如果我理解正确的话,你的方法是让widget也开放对用户的终端接口,而传统的MVC里只有Controller才对终端用户开放接口。我曾考虑过类似解决方案,因为它和Prado里的ActiveControl实现比较象。这个方法有一些复杂点:

  1. 如何根据请求来创建对应的widget对象?从安全性出发我们不能任意根据用户的请求来创建对象。所以需要有一套访问规则。

  2. widget区别于controller的一个重要方面是widget往往需要很多属性配置(或称为状态)。不同的配置会导致不同的行为表现。有的属性配置可以直接根据客户端请求来恢复,而有的则不允许。所以需要有一套机制来初始化widget。

是的,我就是这个意思。

嗯,安全问题确实没考虑仔细,我再想想,呵呵。

第二点也是,可能会导致一个非常复杂的 widget 初始化机制出现,结果让 widget 开发变得更困难。

难度不小,最难的在于怎么指导用户。