分享一個本人寫的一個Knockot JS 數字輸入插件


我將它放在了在GitHub上面,希望有高手一起來幫助完善它,這里附上網址:

https://github.com/dragonrhyme/KnockotNumeric

這里附上下載連接:

  

之前寫過XML技術在界面生成中的簡單應用的文章,后來由於一直很忙,就沒有再寫。但是東西已經可以用了, 附上截圖給大家預覽下,等自己不那么緊張的時候,再接着跟大家分享,這里先行道歉!

再附上一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
 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="utf-8" />
 5     <title></title>
 6         <!--<link href="assets/css/bootstrap-responsive.css" rel="stylesheet">-->
 7         <link href="assets/jqueryui/jquery.ui.css" rel="stylesheet">
 8         <link href="assets/jqueryui/jquery.ui.ie.css" rel="stylesheet">
 9         <link href="assets/timepicker/css/jquery-ui-timepicker-addon.css" rel="stylesheet">
10         <link href="assets/css/bootstrap.css" rel="stylesheet">
11         <link href="assets/css/application.css" rel="stylesheet">
12 </head>
13 <body>
14     <article id="layout-wrapper" class="container-fluid">
15         <header>
16             <hgroup>
17                 <h1 class="page-title">{pagetitle}</h1>
18             </hgroup>
19         </header>
20         <div id="message-center" class="container-fluid"></div>
21         <div class="form-horizontal">
22             {placeholder1}
23         </div>
24         {common}
25     </article>
26         <script src="assets/js/jquery.js"></script>
27         <script src="assets/js/resizecontrols.js"></script>
28         <script src="assets/js/jquery.ui.js"></script>
29         <script src="assets/timepicker/js/jquery-ui-timepicker-addon.js"></script>
30         <script src="assets/js/bootstrap.js"></script>
31         <script src="assets/js/jquery.maskedinput.js"></script>
32         <script src="assets/js/knockout.js"></script>
33         <script src="assets/js/knockout.mapping.js"></script>
34         <script src="assets/js/knockout.handlers.date.js"></script>
35         <script src="assets/js/knockout.handlers.datetime.js"></script>
36         <script src="assets/js/knockout.handlers.datepicker.js"></script>
37         <script src="assets/js/knockout.handlers.datetimepicker.js"></script>
38         <script src="assets/js/knockout.handlers.numeric.js"></script>
39         <script src="assets/js/lib.date.js"></script>
40         <script src="assets/js/lib.viewmodel.js"></script>
41         <script src="assets/js/lib.eventcaller.js"></script>
42         <script src="assets/js/lib.messagecenter.js"></script>
43         <script src="assets/js/lib.clientevent.handlers.js"></script>
44         <script src="assets/js/application.js"></script>
45         <script src="{pagecode}.js"></script>
46 </body>
47 </html>

Rhyme由於工作比較忙,所以難得抽出時間整理東西,接下來要是再有時間,會把那個語法解析的完善下,爭取讓Knockout中讓人頭疼的括號語法消失。Rhyme初步的想法是在綁定數據前對原有表達式語法重新解析,這樣需要括號的地方就可以解析的時候在用代碼插入了。有哪位有更好的想法請求分享一下。(:

Rhyme接下來的源代碼會托管到GitHub,也會在博客園放下載,大家共同學習,也多幫我找找Bug,提點不足,在這里先行謝過了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM