本帖最后由 棕榈 于 2020-2-13 16:54 编辑
FineUI IFrame页面间交互 - 学生CRUD示例
上面的示例是一个学生CRUD示例,也是在项目中经常会出现的场景。在这里我们并不会去讨论CRUD的实现,而是探讨在这个过程中IFrame间页面的通讯。
我先说一下这个示例的构成,它一共有4个页面,StudentCrud是主页,它包含School(学校Tree)及StudentList(学生列表)这两个页面,在StudentList页面中又包含一个F.Window控件,它用于显示Student页面。它的操作也比较简单,当School页面中学校被选择时,StudentList页面会加载相应学校的学生,这里并不是重新加载StudentList页面,而是通过Ajax加载学生数据,StudentList页面中的按钮也就是CRUD相应的操作。
首先,我要先说一下如果采用常规的方式,它之间的交互是怎样的。School页面与StudentList页面都是在StudentCrud页面中,它们俩之间的交互需要通过StudentCrud页面来完成,也就是说StudentCrud页面中要含有操作StudentList页面代码,School页面再去调用StudentCrud页面中的代码。关于School页面,由于它是被F.Window控件显示出来,它需要在后台或前台去操作ActiveWindow,在添加或编辑学生时需要重新加载学生,隐藏ActiveWindow时,还需要拼接一个调用SchoolList页面加载学生方法。这里只是一个简单的嵌套,如果因业务需求,需要更加复杂的嵌套,那上面的操作会更加的复杂,同时相互交结在一起的代码也会增加,维护难度及Bug查找也会更加困难。
下面我说一个当前示例采用的方法,这也是一个全新的IFrame页面间的交互方式,当然当前示例只是一个演示,示例中的代码可能还需要进一步的优化及封装(这里指的是IFrame页面间的交互代码,业务代码只是演示)。
在这之前,请大家看一下之前的关于IFrame页面间通讯的帖子(https://fineui.com/bbs/forum.php?mod=viewthread&tid=22357&extra=)。
先看一下下面的这行代码:
- F.eventBus = top.F.eventBus ? top.F.eventBus : $('<fineui-event-bus>');
复制代码
这行代码需要放到每一个页面中,它的意思是每个页面中的F.eventBus都是引用最顶层F.eventBus对象,而F.eventBus其实就是一个JQuery对象,这样我们就可以借用JQuery中事件的操作,来完成消息的订阅及发布。
下面代码是一个订阅
- F.eventBus.on('refresh', function(e,data){});
复制代码
这样我无论在哪个页面进行发布消息,这个订阅都会收到消息。
- F.eventBus.trigger('refresh');
复制代码
这样也就实现了页面间通讯的基础,是不是很简单。当然我们这里借用的是JQuery事件机制,你也可以采用其它的EventBus。
到这里并没有结束,这样虽然实现了页面间的通讯,但大家有没有发现,所有发布及订阅都是全局,当你页面很多时,又没有很好的组织,那将是很混乱的。
我这里就说一下对F.Window控件的封装,由于F.Window展示方式与WinForm中的对话框基本一致,而对话框基本上都有确认(ok)及取消(cancel)按钮,我以这个为基础来对F.Window进行封装。
首先看一下代码:
- var query = document.location.search
- .replace(/(^\?)/, '')
- .split('&')
- .map(
- function (p) {
- return p = p.split('='), this[p[0]] = p[1], this;
- }.bind({})
- )[0];
- if (query._page_id_) {
- $(document).data('page-id', query._page_id_);
- }
- F.Window.prototype.getPageId = function () {
- if (this.options) {
- if (this.options.attrs) {
- return this.attrs['data-page-id'];
- }
- }
- return null;
- }
- var showWindow = F.Window.prototype.show;
- F.Window.prototype.show = function () {
- var url = arguments[0];
- if (typeof url === 'string') {
- var pageId = this.getPageId();
- if (pageId) {
- url += (url.indexOf('?') >= 0 ? '&' : '?') + '_page_id_=' + pageId;
- arguments[0] = url;
- }
- }
- showWindow.apply(this, arguments);
- }
- $('.f-window[data-page-id]').each(function (index, ele) {
- var win = F.ui[$(ele).attr('id')];
- if (win) {
- var pageId = win.getPageId();
- if (pageId) {
- F.eventBus.on('ok', function (e, data) {
- if (win.ok && data && data.PageId === pageId) {
- win.ok(data);
- }
- });
- F.eventBus.on('cancel', function (e, data) {
- if (win.cancel && data && data.PageId === pageId) {
- win.cancel(data);
- }
- });
- }
- }
- });
- $.ajaxPrefilter(function (options) {
- var pageId = $(document).data('page-id');
- if (pageId) {
- options.data = options.data ? options.data + '&' : '';
- options.data += '_page_id_=' + pageId;
- }
- });
复制代码
这段代码也是要出现在每一个页面中,这里说一个代码所做的操作,在这个之前需要在F.Window控件中添加一个Attribute,代码
- <f:Window ID="winStudent" Title="学生" Hidden="true" EnableIFrame="true" Target="Top" IsModal="true" Width="500" Height="400">
- <Attributes>
- <f:Attribute Key="data-page-id" Value="@Guid.NewGuid()"></f:Attribute>
- </Attributes>
- </f:Window>
复制代码
这里的pageId是用于标识每一个页面的,用于在服务器端触发客户端事件时识别是哪一个F.Window。
在上面的代码中,对pageId进行了封装,在使用时感知不到它的存在。
下面是添加学生按钮的操作代码:
View:
- <f:Button Text="添加" OnClientClick="addStudent()"></f:Button>
复制代码
Script:
- function addStudent() {
- F.ui.winStudent.show("@Url.Content("~/Student")?schoolId=" + schoolId);
- }
- F.ui.winStudent.ok = function () {
- this.close();
- loadStudents();
- }
复制代码
F.ui.winStudent.ok 是在单击确定按钮需要执行的操作,取消按钮的操作代码如下:
- F.ui.winStudent.cancel = function () {
- this.close();
- }
复制代码
就这几行代码,就完成了,显示、确定、取消的操作,甚至也可以添加其它的操作,与WinForm中的操作基本保持一致,当然WinForm是同步的,这里是异步的。
这里只是客户端的封装,服务器端也需要做一些代码的包装,我就不在一一举例了,代码也很简单,一看就明白,大家可以通过示例代码来查看。
示例代码请到知识星球下载。
|