基于TypeScript的FineUIMvc组件式开发(概述)

WebForm与Mvc

    我简单说一下WebForm与Mvc,WebForm是微软很早就推出的一种WEB开发架构,微软对其进行了大量的封装,使开发人员可以像开发桌面程序一样去开发WEB程序,虽然开发效率得到了提高,但可控性会一差。而MVC框架的出现,使WEB开发又回到了请求与响应的开发模式下,可控性增强了,但开发难度也增加了,所以说在架构的选择上没有好与坏,只有合自己的才是最好的。

FineUIMvc与IFrame

    虽然FineUIMvc基于MVC架构,但是也沿用了WebForm页面开发的方式,所以一开始刚接触FineUIMvc时还有一点疑惑。在FineUIMvc中的控制器与视图之间存在着一定的耦合,但这对FineUIMvc来说应该不是什么问题,因为FineUIMvc本身并不是一个通用型的架构,FineUIMvc更适合拥有大量控件操作的项目,耦合的存在可以降低控件的操作难度。在官网的示例中,我们经常能看到在一个控制器中有多个可以返回视图的Action,特别是AppBoxMvc更是达到极致,一个控制器几乎包含了整个项目的全部分代码,这是我所不推荐的做法,所以我在后面对这方面做一些约束。

    在FineUIMvc中有很多容器控件都可以启用IFrame,IFrame之间的交互,在官网示例(http://fineui.com/demo_mvc/)的内联框架中也给出了示例,在这些示例中,大家经常会看到在很多地方都在调用ActiveWindow中的方法,这个也是FineUIMvc对活动窗体的封装。那你在调用ActiveWindow中的方法时,你就已明确的知道,当前的页面是在一个窗体中显示的,并且还是一个活动窗体,如果我们想重用这个页面,并将其放到其它非窗体的容器中,我们要么需要在页面中判断,要么再重新创建一个新的页面,这样必然会对页面的重用带来一些麻烦,同时如果需要在页面间传递一些数据,页面还需要事先知道父页面或同级页面中的方法,这也无形中增加了页面与页面之间的耦合。

FineUIMvc组件开发的基本概念

    如果基于组件的方式去开发FineUIMvc项目,这样就可以解决上面所遇到的问题。首先组件是封闭性的,你只需要暴露你想暴露的事件与方法,同时组件不需要知道调用者的信息,调用者只需要监控组件所提供的事件,调用组件所提供的方法,就可以完成组件的操作。我们这里所指的组件,主要是在FineUIMvc中通过IFrame显示的页面,IFrame本身可以起到上下文隔离的作用,再加上在IFrame中显示的页面以组件的方式提供服务,这样页面的开发可以独立出来,而不用去考虑调用者所处在的上下文。

FineUIMvc组件的构成

    通过上面的介绍大家应该都知道,我们这里的组件其实就是页面,组件与组件之间的交互,其实就是页面与页面之间的交互。一个页面一般要包含HTML文件、CSS文件及JS文件,由于我们是基于TS开发,所以这里的JS文件就是TS文件,HTML文件对应到MVC中就是CSHTML视图文件。如果我们要创建一个UserInfoComponent组件,那它对应到MVC中应该包含哪些文件及是什么样的结构呢,请看下图

    图中用箭头所指向的文件就是我们UserInfoComponent组件所需要的文件,有人可能会问,开发一个组件需要这么文件吗,那组件的开发是不是很复杂。为了使前后端的开发更顺畅,我们需要一些辅助文件,所以这些文件都是必须的,但大家也不用担心,因为这些文件是通过FineUIMvcTools插件自动生成的,并且会在你更改视图及控制器代码时,会自动更新相应的文件。下面是这些文件说明:

  1. UserInfoController.cs:这是MVC中的控制器文件,与普通控制器文件一样没有什么区别
  2. UserInfoController.g.cs:控件引用定义文件,由工具生成,方便直接操作视图中的控件
  3. UserInfoModels.cs:模型文件,这个文件中包含了与当前控制器相关的模型,其中有些模型是自动生成的
  4. Index.cshtml:视图文件,对应控制器中的Index方法
  5. Index.css:视图文件的样式文件
  6. Main.cs:组件引导文件,由工具生成,一般不用修改此文件
  7. UserInfoComponent.ts:组件的TS逻辑文件,组件在客户端的逻辑代码
  8. UserInfoComponent.g.ts:组件的TS辅助文件,由工具生成,包含了控件TS引用定义、模型TS引用定义 及TS的控制器中Action的调用方法

UserInfoComponent组件的开发过程

下面为UserInfoComponent组件的内容,4个文本框及2个按钮,4个文本框的数据来源于外部调用者的设置,“确定”及“取消”按钮触发事件通知调用者

当我们创建完视图时,工具会自动帮助我们生成代码

UserInfoController.g.cs中的代码:

  1 //
  2 // 此代码由工具生成
  3 // 对此文件的修改,将会在下次生成时丢失
  4 //
  5 
  6 using FineUIMvc;
  7 using System.Collections.Generic;
  8 
  9 namespace FineUIMvc.EmptyProject.Controllers
 10 {
 11     public partial class UserInfoController
 12     {
 13         private Dictionary<string, IControlBaseAjaxHelper<ControlBase>> _FINE_UI_MVC_CONTROLS_ = new Dictionary<string, IControlBaseAjaxHelper<ControlBase>>();
 14 
 15         private TextBoxAjaxHelper txtName
 16         {
 17             get
 18             {
 19                 var controlId = "txtName";
 20 
 21                 if (!this._FINE_UI_MVC_CONTROLS_.ContainsKey(controlId))
 22                 {
 23                     this._FINE_UI_MVC_CONTROLS_[controlId] = UIHelper.TextBox(controlId);
 24                 }
 25 
 26                 return (TextBoxAjaxHelper)this._FINE_UI_MVC_CONTROLS_[controlId];
 27             }
 28         }
 29 
 30         private TextBoxAjaxHelper txtEmail
 31         {
 32             get
 33             {
 34                 var controlId = "txtEmail";
 35 
 36                 if (!this._FINE_UI_MVC_CONTROLS_.ContainsKey(controlId))
 37                 {
 38                     this._FINE_UI_MVC_CONTROLS_[controlId] = UIHelper.TextBox(controlId);
 39                 }
 40 
 41                 return (TextBoxAjaxHelper)this._FINE_UI_MVC_CONTROLS_[controlId];
 42             }
 43         }
 44 
 45         private TextBoxAjaxHelper txtTelphone
 46         {
 47             get
 48             {
 49                 var controlId = "txtTelphone";
 50 
 51                 if (!this._FINE_UI_MVC_CONTROLS_.ContainsKey(controlId))
 52                 {
 53                     this._FINE_UI_MVC_CONTROLS_[controlId] = UIHelper.TextBox(controlId);
 54                 }
 55 
 56                 return (TextBoxAjaxHelper)this._FINE_UI_MVC_CONTROLS_[controlId];
 57             }
 58         }
 59 
 60         private TextBoxAjaxHelper txtAddress
 61         {
 62             get
 63             {
 64                 var controlId = "txtAddress";
 65 
 66                 if (!this._FINE_UI_MVC_CONTROLS_.ContainsKey(controlId))
 67                 {
 68                     this._FINE_UI_MVC_CONTROLS_[controlId] = UIHelper.TextBox(controlId);
 69                 }
 70 
 71                 return (TextBoxAjaxHelper)this._FINE_UI_MVC_CONTROLS_[controlId];
 72             }
 73         }
 74 
 75         private ButtonAjaxHelper btnOK
 76         {
 77             get
 78             {
 79                 var controlId = "btnOK";
 80 
 81                 if (!this._FINE_UI_MVC_CONTROLS_.ContainsKey(controlId))
 82                 {
 83                     this._FINE_UI_MVC_CONTROLS_[controlId] = UIHelper.Button(controlId);
 84                 }
 85 
 86                 return (ButtonAjaxHelper)this._FINE_UI_MVC_CONTROLS_[controlId];
 87             }
 88         }
 89 
 90         private ButtonAjaxHelper btnCancel
 91         {
 92             get
 93             {
 94                 var controlId = "btnCancel";
 95 
 96                 if (!this._FINE_UI_MVC_CONTROLS_.ContainsKey(controlId))
 97                 {
 98                     this._FINE_UI_MVC_CONTROLS_[controlId] = UIHelper.Button(controlId);
 99                 }
100 
101                 return (ButtonAjaxHelper)this._FINE_UI_MVC_CONTROLS_[controlId];
102             }
103         }
104     }
105 }
View Code

UserInfoComponent.g.ts中的代码:

 1 //
 2 // 此代码由工具生成
 3 // 对此文件的修改,将会在下次生成时丢失
 4 //
 5 
 6 /*
 7  * UI
 8  */
 9 export module UI {
10     export let txtName = F<F.TextBox>('txtName');
11     export let txtEmail = F<F.TextBox>('txtEmail');
12     export let txtTelphone = F<F.TextBox>('txtTelphone');
13     export let txtAddress = F<F.TextBox>('txtAddress');
14     export let btnOK = F<F.Button>('btnOK');
15     export let btnCancel = F<F.Button>('btnCancel');
16 }
17 
18 /*
19  * Action
20  */
21 export module Action {
22     export function getActionUrl(actionName: string) {
23         return `${F.baseUrl}UserInfo/${actionName}`;
24     }
25 
26     export function index(model?: Model.IndexModel, owner?: Window | GetIFrameWindow) {
27         let url = `${getActionUrl('Index')}${model != null ? '?' + QueryString.stringify(model) : ''}`;
28         let wnd : any = owner || window;
29         wnd = (wnd != null && wnd.getIFrameWindow && wnd.getIFrameWindow()) || wnd;
30         wnd.open(url, '_self');
31     }
32 }
33 
34 /*
35  * Model
36  */
37 export module Model {
38 
39     export class IndexModel {
40 
41         constructor() {
42         }
43     }
44 
45 }
View Code

外部调用者设置组件的信息,我们需要在UserInfoComponent.ts中去实现(23行到53行为自己添加的代码,其它的为创建时自动生成的代码),代码:

 1 import { Component } from '../../Scripts/Component';
 2 import { UI, Action, Model } from './UserInfoComponent.g';
 3 
 4 export class UserInfoComponent extends Component {
 5     public static readonly TYPE = 'FINEUIMVC_EMPTYPROJECT_VIEWS_USERINFOCOMPONENT';
 6 
 7     constructor() {
 8         super(UserInfoComponent.TYPE);
 9         this.init();
10     }
11 
12     public static validType(component: Component) {
13         return component.isType(UserInfoComponent.TYPE);
14     }
15 
16     public static open(model?: Model.IndexModel, owner?: Window | GetIFrameWindow) {
17         Action.index(model, owner);
18     }
19 
20     private init() {
21     }
22 
23     public set userName(name: string) {
24         UI.txtName.setText(name, false);
25     }
26 
27     public get userName() {
28         return UI.txtName.getText();
29     }
30 
31     public set email(email: string) {
32         UI.txtEmail.setText(email, false);
33     }
34 
35     public get email() {
36         return UI.txtEmail.getText();
37     }
38 
39     public set telphone(telphone: string) {
40         UI.txtTelphone.setText(telphone, false);
41     }
42 
43     public get telphone() {
44         return UI.txtTelphone.getText();
45     }
46 
47     public set address(address: string) {
48         UI.txtAddress.setText(address, false);
49     }
50 
51     public get address() {
52         return UI.txtAddress.getText();
53     }
54 }
View Code

外部调用者监听组件事件,我们还是需要在UserInfoComponent.ts中去实现(20行到36行为自己添加的代码),代码:

 1 import { Component } from '../../Scripts/Component';
 2 import { UI, Action, Model } from './UserInfoComponent.g';
 3 
 4 export class UserInfoComponent extends Component {
 5     public static readonly TYPE = 'FINEUIMVC_EMPTYPROJECT_VIEWS_USERINFOCOMPONENT';
 6 
 7     constructor() {
 8         super(UserInfoComponent.TYPE);
 9         this.init();
10     }
11 
12     public static validType(component: Component) {
13         return component.isType(UserInfoComponent.TYPE);
14     }
15 
16     public static open(model?: Model.IndexModel, owner?: Window | GetIFrameWindow) {
17         Action.index(model, owner);
18     }
19 
20     private init() {
21         UI.btnOK.onClick(() => {
22             $(document).trigger('onOKClick');
23         });
24 
25         UI.btnCancel.onClick(() => {
26             $(document).trigger('onCancelClick');
27         });
28     }
29 
30     public onOKClick(handler: (e, m) => any) {
31         $(document).on('onOKClick', handler);
32     }
33 
34     public onCancelClick(handler: (e, m) => any) {
35         $(document).on('onCancelClick', handler);
36     }
37 
38     public set userName(name: string) {
39         UI.txtName.setText(name, false);
40     }
41 
42     public get userName() {
43         return UI.txtName.getText();
44     }
45 
46     public set email(email: string) {
47         UI.txtEmail.setText(email, false);
48     }
49 
50     public get email() {
51         return UI.txtEmail.getText();
52     }
53 
54     public set telphone(telphone: string) {
55         UI.txtTelphone.setText(telphone, false);
56     }
57 
58     public get telphone() {
59         return UI.txtTelphone.getText();
60     }
61 
62     public set address(address: string) {
63         UI.txtAddress.setText(address, false);
64     }
65 
66     public get address() {
67         return UI.txtAddress.getText();
68     }
69 }
View Code

调用者本身也是一个组件,组件中含有一个Window控件,用于加载UserInfoComponent组件(22行到45行为调用UserInfoComponent,第3行为导入UserInfoComponent),代码: 

 1 import { Component } from '../../Scripts/Component';
 2 import { UI, Action, Model } from './DefaultComponent.g';
 3 import { UserInfoComponent } from '../UserInfo/UserInfoComponent';
 4 
 5 export class DefaultComponent extends Component {
 6     public static readonly TYPE = 'FINEUIMVC_EMPTYPROJECT_VIEWS_DEFAULTCOMPONENT';
 7 
 8     constructor() {
 9         super(DefaultComponent.TYPE);
10         this.init();
11     }
12 
13     public static validType(component: Component) {
14         return component.isType(DefaultComponent.TYPE);
15     }
16 
17     public static open(model?: Model.IndexModel, owner?: Window | GetIFrameWindow) {
18         Action.index(model, owner);
19     }
20 
21     private init() {
22         UserInfoComponent.open(null, UI.window1);
23         UI.window1.onReady(() => {
24             let userInfoComponent = UI.window1.getComponent<UserInfoComponent>();
25             if (userInfoComponent && UserInfoComponent.validType(userInfoComponent)) {
26                 userInfoComponent.userName = '张三';
27                 userInfoComponent.email = 'test@test.com';
28                 userInfoComponent.telphone = '123456789';
29                 userInfoComponent.address = 'xxxxxxxxxxx';
30 
31                 userInfoComponent.onOKClick(() => {
32                     F.notify({
33                         message: `姓名:${userInfoComponent.userName};邮箱:${userInfoComponent.email}`,
34                         messageIcon: F.MessageBoxIcon.$Success
35                     });
36                 });
37 
38                 userInfoComponent.onCancelClick(() => {
39                     F.notify({
40                         message: '单击了取消按钮',
41                         messageIcon: F.MessageBoxIcon.$Warning
42                     });
43                 });
44             }
45         });
46     }
47 }
View Code

运行后的效果(修改完姓名及邮箱后,单击“确定”按钮),如图:

 

以上就是组件开发的过程,基于组件方式的开发,可以最大程度的提高页面重复利用次数,降低页面与页面之间的耦合。

在后续的文章中,我会介绍组件开发的每一个细节,FineUIMvcTools工具还在完善中,我想等到这个系列文章写完后,再提供给大家。

 

示例代码:

链接: http://pan.baidu.com/s/1o7AgH2y 密码: 6b33

转载于:https://www.cnblogs.com/haoxj/p/6892508.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值