Knockot JS 数字输入插件

我将它放在了在GitHub上面,希望有高手一起来帮助完善它,这里附上网址:

 之前写过XML技术在界面生成中的简单应用的文章,后来由于一直很忙,就没有再写。但是东西已经可以用了, 附上截图给大家预览下,等自己不那么紧张的时候,再接着跟大家分享,这里先行道歉!

Knockot JS 数字输入插件_第1张图片

再附上一TabPanel为例子的XML代码:

复制代码
1 <Tab ID="tab1" Cols="12" Offset="0">
2   <TabItem Key="tabitem1" LabelText="选项卡一">
3     <TextBox ID="tabitemtextbox1" LabelWidth="10" Cols="3" Offset="0" LabelText ="文字输入一:" Value="$root.text"/>
4     <TextBox ID="tabitemtextbox2" LabelWidth="100" Cols="3" Offset="0" LabelText ="文字输入二:" Value="$root.text" NotWripRow="true"/>
5     <TextBox ID="tabitemtextbox3" LabelWidth="100" Cols="3" Offset="0" LabelText ="文字输入三:" Value="$root.text" NotWripRow="true"/>
6     <TextBox ID="tabitemtextbox4" LabelWidth="100" Cols="3" Offset="0" LabelText ="文字输入四:" Value="$root.text" NotWripRow="true"/>
7   </TabItem>
8 </Tab>
复制代码

然后这里是关于操作的定义方式,分为服务器端和客户端操作,其中客户端操作不需要额外手写代码,服务器端也是ajax交互的。也贴出来给大家探讨下(注释部分表示可以那样嵌套定义操作):

复制代码
 1 <Operations>
 2   <Operation ID="saveData">
 3     <ServerOperation Handler="SaveChanges" FinishAlert="'保存数据'">
 4       <!--<ServerOperation Handler="SaveChanges" FinishAlert="'Second'"></ServerOperation>
 5         <ClientOperation Handler="Confirm" Para="'From client'"></ClientOperation>
 6         <ClientOperation Handler="Information" Para="'From client'"></ClientOperation>
 7         <FailbackOperation>
 8           <ClientOperation Handler="Error" Para="'From client'"></ClientOperation>
 9         </FailbackOperation>-->
10     </ServerOperation>
11   </Operation>
12   <Operation ID="changeValue">
13     <ClientOperation Handler="ChangeValue" Para="'$root.mCourseList[0].Name', $root.mCourseList()[0].Desc" />
14   </Operation>
15   <Operation ID="changeValue1">
16     <ClientOperation Handler="ChangeValue" Para="'$root.mCourseList[*].Name', $root.mCourseList()[1].Desc" />
17   </Operation>
18   <Operation ID="openWindow">
19     <ClientOperation Handler="OpenWindow" Para="'window1'" />
20   </Operation>
21 </Operations>
复制代码

当然,OpenWindow有关于Window的定义,如下:

复制代码
1 <PopWindow Height="100" Width="100" IsEnable="true" IsVisible="true" ID="window1" Title="这是个窗口" >
2   <TableContainer>
3     <DataGrid Rows="6" SelectColumnName ="isSelected" SelectMode="multi" ID="datagrid1" DataSource="$root.mCourseList">
4       <Column BindFieldKey="Name" Header="名称" Tip="This is tip"/>
5       <Column BindFieldKey="Desc" Header="描述" Tip="This is tip"/>
6     </DataGrid>
7   </TableContainer>
8 </PopWindow>
复制代码

看看Rhyme定义的客户端操作类库, 例如ChangeValue,解析'$root.mCourseList[*].Name'这个表达式的代码还算有些价值, 当然,还不太完善:

复制代码
 1 ChangeValue: function (args, successCallBack, failureCallBack) {
 2     var _$root, _$data, _event, _target, _assignTo, _from, _dataContext, _statements, _assignmentInContextByStatementsWithValue;
 3 
 4     // Context params.
 5     _$root = args.$root;    // Root context of view model.
 6     _$data = args.$data;    // Current context of view model.
 7     _event = args.event;    // Event from dom or jQuery .
 8     _target = args.target;    // Source element of event.
 9 
10     // The change where target.
11     _assignTo = args.params[0];
12     // The new value.
13     _from = ko.utils.unwrapObservable(args.params[1]);
14 
15     // When the target was observable object, Assignment new value to it.
16     if (typeof (_assignTo) === 'function') {
17         _assignTo(_from);
18     } else if (typeof (_assignTo) === 'string') {
19         //  The list of statements need to parse.
20         _statements = _assignTo.replace(/\[\*\]/g, '.*').replace(/\[\d\]/g, function (s, t) { return '.' + s.replace(/[^\d]/g, ''); }).split('.');
21         _assignmentInContextByStatementsWithValue = function (dataContext, statements, newValue, currentStatementIndex) {
22             var _currentContext = dataContext,
23                 _currentContextLength,
24                 _currentStatementIndex = currentStatementIndex,
25                 _currentStatement,
26                 _indexStatement,
27                 _totalStatements = statements.length;
28 
29             // Syntactic analysis.
30             for (var i = _currentStatementIndex; i < _totalStatements; i++) {
31                 _currentStatement = statements[i];
32 
33                 if (_currentStatement === '$root' || _currentStatement === '$data') {
34                     continue;
35                 } else if (/^\d+$/.test(_currentStatement)) { // Match index statement.
36                     _indexStatement = parseInt(statements[i]);
37 
38                     if (i === _totalStatements - 1) {
39                         _currentContext[_indexStatement](newValue);
40                     } else {
41                         _assignmentInContextByStatementsWithValue(_currentContext[_indexStatement], statements, newValue, i + 1);
42                     }
43                 } else if (_currentStatement === '*') { // Match every item.
44                     _currentContextLength = ko.utils.unwrapObservable(_currentContext).length;
45                     for (var j = 0; j < _currentContextLength; j++) {
46                         debugger;
47                         if (i === _totalStatements - 1) {
48                             _currentContext[j](newValue);
49                         } else {
50                             _assignmentInContextByStatementsWithValue(_currentContext[j], statements, newValue, i + 1);
51                         }
52                     }
53                 } else { // In normal case.
54                     if (i === _totalStatements - 1) {
55                         dataContext[_currentStatement.replace(/\(\)/g, '')](newValue);
56                     } else {
57                         _currentContext = ko.utils.unwrapObservable(dataContext[_currentStatement.replace(/\(\)/g, '')]);
58                     }
59                 }
60             }
61         };
62 
63         if (_statements[0].search("\\u0024data") != -1) {
64             _dataContext = _$data;
65         } else {
66             _dataContext = _$root;
67         }
68 
69         _assignmentInContextByStatementsWithValue(_dataContext, _statements, _from, 0);
70     }
71 }
复制代码

还有Rhyme 为操作的定义自动生成了相应的操作代码:

复制代码
 1 (function(){ $(function(){initViewModel('PageLoaded', 'Evaluation');}); })();
 2 
 3 var saveData = function ($root, $data, event, target){window.appViewModel.doAction('SaveChanges','保存数据' , function (viewModel, response) {  }, function (xhr) {  });}
 4 
 5 var changeValue = function ($root, $data, event, target){
 6     ClientEventHandlers.ChangeValue({ $root: $root, $data: $data, event: event, target: target, params: ['$root.mCourseList[0].Name', $root.mCourseList()[0].Desc] }, function(){  }, function(){  });
 7 }
 8 
 9 var changeValue1 = function ($root, $data, event, target){
10     ClientEventHandlers.ChangeValue({ $root: $root, $data: $data, event: event, target: target, params: ['$root.mCourseList[*].Name', $root.mCourseList()[1].Desc] }, function(){  }, function(){  });
11 }
12 
13 var openWindow = function ($root, $data, event, target){
14     ClientEventHandlers.OpenWindow({ $root: $root, $data: $data, event: event, target: target, params: ['window1'] }, function(){  }, function(){  });
15 }
复制代码

由于Rhyme比较懒惰,所以ViewModel定义了个通用的,当然,如果你不懒惰,定义N个慢慢玩玩后再决定是否也会学Rhyme偷懒吧。(:以后会慢慢完善:

复制代码
 1 var ViewModel = function (url, pageCode, initAction) {
 2 
 3     var self = this;
 4 
 5     self.url = url;
 6     self.pageCode = ko.observable(pageCode);
 7 
 8     self.loadData = function (dataHandler, successMsg, successCallBack, failureCallBack) {
 9         var _data = {};
10         debugger;
11         
12         if (typeof (dataHandler) === 'function') {
13             _data = dataHandler(ko.toJS(self));
14         }
15 
16         $.ajax({
17             url: self.url,
18             data: { pageCode: _data.pageCode, actionName: _data.actionName, "datasource": ko.toJSON(_data) },
19             type: "POST",
20             dataType: 'json',
21             error: function (xhr) {
22                 if (typeof (failureCallBack) === 'function') {
23                     try {
24                         failureCallBack(self, xhr);
25                         MessageCenter.fromXhr(e);
26                     } catch (e) {
27                         MessageCenter.fromException(e);
28                     }
29                 }
30             },
31             success: function (response) {
32                 if (successMsg) {
33                     MessageCenter.success(successMsg);
34                 }
35 
36                 if (typeof (successCallBack) === 'function') {
37                     try {
38                         successCallBack(self, response);
39                     } catch (e) {
40                         MessageCenter.fromException(e);
41                     }
42                 }
43             }
44         });
45         
46         return self;
47     };
48 
49     // Call service.
50     self.doAction = function (actionName, successMsg, successCallBack, failureCallBack) {
51         debugger;
52         self.loadData(function (model) {
53             model.actionName = actionName;
54 
55             return model;
56         }, successMsg, successCallBack, failureCallBack);
57     };
58 
59     //self.saveChanges = function () {
60     //    self.doAction("SaveChanges", successMsg, function (viewModel, response) {
61     //        debugger;
62     //        ko.mapping.fromJS(response, {}, viewModel);
63     //    });
64     //};
65 
66     self.bind = function(element) {
67         if(element) {
68             ko.applyBindings(self);
69         } else {
70             ko.applyBindings(self);
71         }
72     };
73     
74     (function(){
75         if(typeof(initAction) === 'function') {
76             try {
77                 initAction(self);
78             } catch(ex) {
79                 // init error
80             }
81         }
82     })();
83 };
复制代码

还有为了兼容万恶的IE, 不得不用JS来调整组件大小。Rhyme的布局是支持响应式布局的。当然,也是基于Bootstrap的。

复制代码
 1 (function () {
 2     $(function () {
 3         var resizeControls = function (forVisible, containerSelector) {
 4             var selectorTemplate = 'input[type="text"]{forVisible},input[type="password"]{forVisible},select{forVisible},textarea{forVisible}';
 5             var selector = '';
 6 
 7             if (forVisible === true) {
 8                 selector = selectorTemplate.replace(/{forVisible}/g, ':visible');
 9             } else {
10                 selector = selectorTemplate.replace(/{forVisible}/g, '');
11             }
12 
13             var $resizeControl = $(selector, containerSelector);
14             var totalControlForResize = $resizeControl.length;
15             for (var i = 0; i < totalControlForResize; i++) {
16 
17                 var currentControl = $resizeControl.get(i),
18                     $currentControl = $(currentControl),
19                     $parent = $currentControl.parents('.control-group'),
20                     $label = $parent.children('.control-label');
21                 if ($label && $label.get(0)) { $currentControl.width($parent.get(0).offsetWidth - $label.get(0).offsetWidth - 15); }
22                 else {
23                     $currentControl.width($parent.get(0).offsetWidth - 15);
24                 }
25             }
26         };
27 
28         resizeControls(true, '.controls');
29 
30         window.onresize = function () {
31             resizeControls(true, '.controls');
32         };
33 
34         $('[data-toggle="tab"]').live('click', function () {
35             var container = $(this).attr('href');
36             resizeControls(false, container);
37         });
38 
39         $('[data-toggle="details"]').live('click', function () {
40             var container = $(this).attr('data-toggle-target');
41             window.setTimeout(function () { resizeControls(false, container); }, 10)
42         });
43     });
44 })();
复制代码

Rhyme 也为布局提供了模版,类似母版页,当然,用的是HTML,生成后的也是纯HTML,由于服务于企业内部系统,所以没有对SEO做任何考虑。

View Code

Rhyme由于工作比较忙,所以难得抽出时间整理东西,接下来要是再有时间,会把那个语法解析的完善下,争取让Knockout中让人头疼的括号语法消失。Rhyme初步的想法是在绑定数据前对原有表达式语法重新解析,这样需要括号的地方就可以解析的时候在用代码插入了。有哪位有更好的想法请求分享一下。(:

Rhyme接下来的源代码会托管到GitHub,也会在博客园放下载,大家共同学习,也多帮我找找Bug,提点不足,在这里先行谢过了。

 
 
 

你可能感兴趣的:(JavaScript,JavaScript,mvc,XSL,代码生成,MVVM,XSLT,语法分析,数字输入)