舊專案必學技術整理 - AngularJS

Posted by Tim Lin on 2018-11-24

AngularJS 必會語法解說

Two-way Data Binding

在 AngularJS 裡,所有在 View 與 Controller 之間,都是透過 Model 來做資料傳遞,
無論你在 View 這端透過表單元素修改了內容,或是在 Controller 這端修改了 Model 內容,
兩邊都會即時發生變化,因為在 AngularJS 框架中會監控 (watch) Model 物件的任何變化,
並隨時反映到 View 上面。以下是雙向資料繫結的示意圖:

是用來宣告angular的範圍

ng-controller & $scope

https://jsbin.com/nocedibagi/edit?html,js,output

$scope

https://docs.angularjs.org/guide/scope

Scope as Data-Model

Scope is the glue (膠;膠水) between application controller and the view.
•連結 html (view) 與 JS (controller) 的橋樑

•每一個 angular 的應用程式只會有一個 root scope,但是可以有很多子 scope 物件(即可以多個controller)
•當宣告一個 controller 時,就會產生一個 scope 物件(Data-Model)

將 html 上輸入的值綁定到 scope 上
http://jsbin.com/ogecuw/1/edit
所以
當 html 的值變化: scope 上的值變化
當 controller 把 scope 的值改變: html 上顯示的值也會改變
=> two-way binding

angularJS ng-model v.s. javascript && JQuery

javascript

1
2
3
4
5
6
html:
<input id="demo"/>

在 js 要取值:
document.getElementById("demo").value;
此外 每次要做取值 設值 還要自己設定事件... change, click...來做 model binding, 超麻煩

JQuery

1
2
3
4
5
6
html:
<input id="demo"/>

在 js 要取值:
$('#demo').val();
此外 每次要做取值 設值 還要自己設定事件... change, click...來做 model binding, 超麻煩

內嵌繫結 (Interpolation)

{{ 要顯示的 scope 內的變數 }}

ng-bind

ng-click

https://jsbin.com/angularjs-controller/3/edit?html,output

ng-show/ng-hide

https://code.angularjs.org/1.2.6/docs/api/ng.directive:ngShow
http://jsbin.com/opojaq/7/edit

ng-init

http://jsbin.com/ucodup/7/edit

ng-repeat

https://jsbin.com/fahahuseba/edit?html,css,js,output
https://docs.angularjs.org/api/ng/directive/ngRepeat

track by $index

1
2
3
4
5
6
7
Best Practice: If you are working with objects that have a unique
identifier property, you should track by this identifier instead of the
object instance, e.g. item in items track by item.id. Should you reload
your data later, ngRepeat will not have to rebuild the DOM elements for
items it has already rendered, even if the JavaScript objects in the
collection have been substituted for new ones. For large collections, this
significantly improves rendering performance.

input 欄位

https://code.angularjs.org/1.2.6/docs/api/ng.directive:input

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
input 有以下屬性:

name 名稱
ng-model 綁定的資料
required 限制是否必填
ng-required 限制是否必填
ng-minlength 限制最小長度
ng-maxlength 限制最大長度
ng-pattern 限制RegExp格式
ng-change 當input的值發生變化的時候執行

除此之外,input 還包含了以下類型:

type="checkbox"
type="email"
type="number"
type="radio"
type="text"
type="url"

input checkbox

https://jsbin.com/egavik/1/edit?html,output

ng-option

http://blog.miniasp.com/post/2013/05/12/AngularJS-ng-module-select-ngOptions-usage-samples.aspx

https://jsbin.com/gekahapeto/edit?html,js,output

pipe filter

https://code.angularjs.org/1.2.6/docs/api/ng.filter:currency
內建的 filter

1
2
3
4
5
6
7
8
9
currency 加入貨幣符號
date 可以將 Date 物件轉成指定格式的日期字串
filter 可以篩選陣列中的內容
json 可以將javascriop物件轉成json字串
limitTo 可以限制陣列或字串裡面的字元個數
number 可以為數值或指定小數以下位數
lowercase 可以將英文字串轉成英文小寫
uppercase 可以將英文字串轉成英文大寫
orderBy 可以將資料作排序

https://jsbin.com/uPETequ/2/edit?html,output

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
(function () {
var app = angular.module("app", []);

app.controller('appController', function ($scope, $filter) {
var articles = [
{
title: "How to build webapps that scale",
slug: "zp7yqc",
body: "laudantium enim quasi est quidem magnam voluptate ipsam eos\ntempora quo necessitatibus\ndolor quam autem quasi\nreiciendis et nam sapiente accusantium",
createdAt: "2018-05-11T21:58:27.358Z",
updatedAt: "2018-05-11T21:58:27.358Z",
tagList: [],
description: "laudantium enim quasi est quidem magnam voluptate ipsam eos\ntempora quo necessitatibus\ndolor quam autem quasi\nreiciendis et nam sapiente accusantium",
author: {
username: "Eliseo@gardner.biz",
bio: "Eliseo",
image: "/assets/img/N4VcUeJ.jpg",
following: false
},
favorited: false,
favoritesCount: 1
},
{
title: "quo vero reiciendis velit similique earum",
slug: "p3vcsw",
body: "est natus enim nihil est dolore omnis voluptatem numquam\net omnis occaecati quod ullam at\nvoluptatem error expedita pariatur\nnihil sint nostrum voluptatem reiciendis et",
createdAt: "2018-05-11T21:55:00.722Z",
updatedAt: "2018-05-11T21:55:00.722Z",
tagList: [],
description: "est natus enim nihil est dolore omnis voluptatem numquam\net omnis occaecati quod ullam at\nvoluptatem error expedita pariatur\nnihil sint nostrum voluptatem reiciendis et",
author: {
username: "Jayne_Kuhic@sydney.com",
bio: "Jayne_Kuhic",
image: "/assets/img/Qr71crq.jpg",
following: false
},
favorited: false,
favoritesCount: 3
},
{
title: "odio adipisci rerum aut animi",
slug: "3c69lg",
body: "quia molestiae reprehenderit quasi aspernatur\naut expedita occaecati aliquam eveniet laudantium\nomnis quibusdam delectus saepe quia accusamus maiores nam est\ncum et ducimus et vero voluptates excepturi deleniti ratione",
createdAt: "2018-05-11T19:49:35.917Z",
updatedAt: "2018-05-11T19:49:35.917Z",
tagList: [],
description: "quia molestiae reprehenderit quasi aspernatur\naut expedita occaecati aliquam eveniet laudantium\nomnis quibusdam delectus saepe quia accusamus maiores nam est\ncum et ducimus et vero voluptates excepturi deleniti ratione",
author: {
username: "Lew@alysha.tv",
bio: "Lew",
image: "/assets/img/cat.jpg",
following: false
},
favorited: false,
favoritesCount: 5
},
{
title: "dolore omnis vol odio adipisci rerum aut animi",
slug: "3c69lg",
body: "quia molestiae reprehenderit quasi aspernatur\naut expedita occaecati aliquam eveniet laudantium\nomnis quibusdam delectus saepe quia accusamus maiores nam est\ncum et ducimus et vero voluptates excepturi deleniti ratione",
createdAt: "2018-05-11T19:49:35.917Z",
updatedAt: "2018-05-11T19:49:35.917Z",
tagList: [],
description: "quia molestiae reprehenderit quasi aspernatur\naut expedita occaecati aliquam eveniet laudantium\nomnis quibusdam delectus saepe quia accusamus maiores nam est\ncum et ducimus et vero voluptates excepturi deleniti ratione",
author: {
username: "Lew@alysha.tv",
bio: "Lew",
image: "/assets/img/cat.jpg",
following: false
},
favorited: false,
favoritesCount: 5
}
];
$scope.list = angular.copy(articles);

$scope.title = 'NTA';
$scope.subtitle = 'National Treasury Administration, Ministry of Finance';
$scope.showTitle = true;

$scope.search = function () {
$scope.list = $filter('filter')(articles, $scope.keyword.title);
}

});
})();

自訂 pipe filter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
app.filter('itemFilter', function() {
return function(input) {
switch(input) {
case "01":
return "01-公保";
break;
case "02":
return "02-勞保";
break;
case "03":
return "03-勞工退休金";
break;
case "04":
return "04-健保";
break;
case "05":
return "05-退撫基金";
break;
default:
return "全部";
}
};
});

$watch

https://code.angularjs.org/1.2.6/docs/api/ng.$rootScope.Scope

ng-grid

https://github.com/angular-ui/ng-grid-legacy/wiki

eac108e.js

js 呼叫 rest api

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$scope.query = function(ped, omitAlerts) {
ped = ped || ($scope.model ? $scope.model.ped : "");
// var result = eac108eService.find({ped:ped},omitAlerts||false);
var result = eac108eService.findAll('rest/find/all', {
ped: ped
}, omitAlerts || false);
result.then(function(response) {
if (response) {
state.query();
}

if (angular.isArray(response)) {
$scope.gridData = response;
} else {
$scope.gridData = [ response ];
}
});
};
});

brt306e.html & brt306e.js 解說

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
<!DOCTYPE html>
<html lang="zh-Hant-TW" xmlns:th="http://www.thymeleaf.org">

<head>
<title>代繳作業繳費期限維護 - BRT306E</title>
<meta th:substituteby="fragments/head_meta :: meta">
</meta>
<meta th:substituteby="fragments/resources :: head_resources">
</meta>
</head>

<body>
<div class="container">
<div th:substituteby="fragments/body_header :: body_header"></div>
<div class="row" data-ng-app="brt306eApp"> <!-- 宣告angularjs 作用域 -->
<div class="container" data-ng-controller="brt306eController"> <!-- 宣告 controller 範圍和對應的 name -->
<section>
<!-- 宣告一個 form (form-inline), 通常會給一個 name, 為了給驗證元件使用 -->

<!-- data-c-form-validation: 如果此 FORM 要加上驗證 必須加上 -->

<!-- data-c-enter-tab: 依照 tabindex 的順序按 enter 自動跳欄 -->
<!-- 按下 enter 時自動跳到下一個欄位包含所有的 input、select、button, 若 tabIndexThreshold 有設定時,-->
<!--則 tabIndexThreshold< tabindex 以下的欄位 ,按下enter時不會進入。 -->
<form class="form-inline" name="brt306eForm" data-c-form-validation="{displayTrigger:'none'}"
data-c-enter-tab="{tabIndexThreshold:1}">
<div class="panel panel-info">
<div class="panel-heading col-sm-12">
<h3 class="panel-title">
<!-- ${FUNCTION_ID} 固定由後端讀取而來, 固定寫即可 -->
<span th:text="${FUNCTION_ID}"></span> <span class="text-muted">代繳作業繳費期限維護</span>
</h3>
</div>
<div class="panel-body">
<div class="row-fluid">
<div class="row">
<div class="col-sm-12 text-right">
<button type="button" class="btn btn-primary" data-ng-click="create()"
tabindex="3">
<i class="glyphicon glyphicon-plus"></i> 新增
</button>
<button type="button" class="btn btn-primary" data-ng-click="reset()">
<i class="glyphicon glyphicon-refresh"></i> 清除
</button>
<button type="button" class="btn btn-primary" data-ng-click="remove()">
<i class="glyphicon glyphicon-remove"></i> 刪除
</button>
<button type="button" class="btn btn-primary" data-ng-click="query(true)">
<i class="glyphicon glyphicon-search"></i> 查詢
</button>
</div>
</div>
<br>
<div class="row">
<div class="panel panel-info">
<div class="panel-body">
<div class="col-sm-4"></div>
<div class="col-sm-7">
<div class="row">
<div class="col-sm-3 text-right">
<label>付款類別</label>
</div>
<div class="col-sm-5">
<!-- data-c-validate = {函式名稱 : 要帶入顯示訊息內的參數,若沒有則寫true。} -->
<select id="item" name="item" class="form-control"
data-ng-model="model.item" data-c-validate="{checkItem : true}">
<option value="">&nbsp;&nbsp;&nbsp;全部</option>
<option value="01">01-公保</option>
<option value="02">02-勞保</option>
<option value="03">03-勞工退休金</option>
<option value="04">04-健保</option>
<option value="05">05-退撫基金</option>
</select>
</div>
</div>
<div class="row">
<div class="col-sm-3 text-right">
<label>款項所屬年月</label>
</div>
<!-- dateValidation 此函式 在 customValidation.js -->
<div class="col-sm-9">
<input type="text" class="form-control" id="payYyymm" name="payYyymm"
data-ng-model="model.payYyymm" data-ui-mask="99999"
data-c-validate="{dateValidation : ['yyyMM','Minguo'],messages:{dateValidation:'年月錯誤 (YYYMM)'}}"
data-ng-keydown="findEndDateByEnter($event)" style="width: 35%;"
tabindex="1" required>
</div>
</div>
<div class="row">
<div class="col-sm-3 text-right">
<label>代繳期限</label>
</div>
<div class="col-sm-9">
<input type="text" class="form-control" id="endDate" name="endDate"
data-ng-model="model.endDate" style="width: 40%;"
data-c-validate="{dateValidation : ['yyyMMdd','Minguo'], checkEndDate:true, messages:{dateValidation:'代繳期限錯誤!!'}}"
tabindex="2">
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row text-center">
<!-- 使用 ng-grid 元件 -->
<div class="grid-style" data-ng-grid="itemGrid" style="display: inline-block; height: 400px; width: 520px;"></div>
</div>
</div>
</div>
</div>
</form>
</section>
</div>
</div>
</div>
<div th:substituteby="fragments/body_footer :: footer"></div>
<div th:substituteby="fragments/resources :: body_resources"></div>
<script type="text/javascript" th:src="@{/scripts/brt306e.js}"></script>
</body>

</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
(function() {
var app = angular.module("brt306eApp", [ 'ntaCommonModule']); // 都一定會注入 ntaCommonModule

app.factory('brt306eService',function(cResource){ //注入 cResource 來使用框架提供的 ajax 方法,

// cResource 第一個參數 url pattern, 參數物件
// 這裡只有 下面 remove 會用到, 為了對應後端 rest api url

/*@RequestMapping(value = "/{payYyymm}/{item}", method = RequestMethod.DELETE)
public @ResponseBody
void delete(@PathVariableModel Brt352faId id, Alerter alerter) {*/


var resource = cResource('rest/:payYyymm/:item', {payYyymm :"@payYyymm", item :"@item"});

//如果只要用execute, 宣告這樣即可 var resource = cResource('rest', {});


return {
remove : function(model){
return resource.remove(model); // delete
},
execute : function (url , param, showAlerts, clearAlerts){
// omitAlerts: 忽略這次 ajax 的噴錯或警告, clearAlerts: 是否清除 alert
var options = {'omitAlerts': !showAlerts, 'clearAlerts': clearAlerts};
return resource.execute(url , param, options);
}
};
});

// 注入所需的 物件 $scope, brt306eService, alerter, cValidation
app.controller('brt306eController', function($scope, brt306eService, alerter, cValidation) {

$scope.addSpaces = function(input){
var result = "";
if(input == '') {
for(var i = 0; i < 3; i++){
result += String.fromCharCode(160);
}
}
return result;
};

//初始
var init = function() {
$scope.model = {};
$scope.itemData = []; // or $scope.itemData.length = 0
};

init();
$scope.model.item = '';

//帶出EndDate
$scope.findEndDateByEnter = function($event) {
// 檢查 是否按下的案件是 enter (13) 並檢查 payYyymm 這欄位是不是有通過檢核
if($event.keyCode == 13 && $scope.validate($scope.brt306eForm.payYyymm)) {

//呼叫 後端 findEndDate rest api
var result = brt306eService.execute('rest/findEndDate', $scope.model);
result.then(function(response){//針對回傳做事
//沒有錯誤發生時執行以下
if(response) {
$scope.model.endDate = response.endDate;
}
});
}
};

//查詢 load grid
$scope.query = function(showAlerts) {
//呼叫 後端 findGrid rest api
var result = brt306eService.execute('rest/findGrid', $scope.model, showAlerts);
result.then(function(response){//針對回傳做事
//沒有錯誤發生時執行以下
if(response) {
$scope.itemData = response;
}
});
};

//新增
$scope.create = function() {
// 在 js 檢查這三個欄位是否通過, 記得 要檢核的欄位要有 name
// 如果是針對整個 form 的欄位都要檢查 $scope.validate($scope.brt306eForm)
if($scope.validate($scope.brt306eForm.item, $scope.brt306eForm.payYyymm, $scope.brt306eForm.endDate)) {

//呼叫 後端create rest api
var result = brt306eService.execute('rest/create', $scope.model, true);
result.then(function(response){ //針對回傳做事
//沒有錯誤發生時執行以下
$scope.model.payYyymm = "";
$scope.model.endDate = "";
$scope.query();
});
}
};

//刪除
$scope.remove = function(){
bootbox.confirm("是否確定刪除", function(result) { //呼叫 bootbox 元件
if (result){ // 如果按下確定

//呼叫 後端 刪除 rest api
var result = brt306eService.remove($scope.model);
result.then(function(response) { //針對回傳做事
init(); //沒有錯誤發生時執行這
}, function(response) {
alerter.fatal("無資料可刪除! 失敗!"); //有錯誤發生時執行這
});
};
});
};

//做清除
$scope.reset = function () {
init(); //呼叫初始
alerter.clear(); //清除 alerter
$scope.clearValidationMessages(); //用來清除所有的驗證錯誤
};

//gird 欄位定義變數
$scope.itemGridCols = [ {field:'item', width:'120px', displayName:'付款類別', cellFilter: "itemFilter"},
{field:'payYyymm', width:'100px', displayName:'款項年月'},
{field:'endDate', width:'100px', displayName:'代繳期限'},
{field:'uids', width:'100px', displayName:'異動人'},
{field:'mdate', width:'120px', displayName:'異動日期'} ];
// grid 物件
$scope.itemGrid = {
multiSelect : false, //是否可多選
data : 'itemData', // 資料來源 陣列變數名稱: 請用字串
columnDefs : 'itemGridCols', //gird 欄位定義變數名稱: 請用字串
enableColumnResize: true, //欄位是否可以調整大小
selectedItems: $scope.selectedItemData, //選取那些資料
afterSelectionChange : function(rowItem){ // 選取後要做什麼事
if (rowItem.selected) {
$scope.model = angular.copy(rowItem.entity);
}
}
};

// 自訂檢核
var checkItem = function(value) {
return (value === '') ? false : true;
};
cValidation.register('checkItem', checkItem, '付款類別錯誤!');

var checkEndDate = function(value, scope) {
var payYyymm = parseInt(scope.model.payYyymm + '01', 10);
var endDate = parseInt(scope.model.endDate, 10);
return (payYyymm > endDate) ? false : true;
};
cValidation.register('checkEndDate', checkEndDate, '代繳期限錯誤!!');

});

//自訂 filter
app.filter('itemFilter', function() {
return function(input) {
switch(input) {
case "01":
return "01-公保";
break;
case "02":
return "02-勞保";
break;
case "03":
return "03-勞工退休金";
break;
case "04":
return "04-健保";
break;
case "05":
return "05-退撫基金";
break;
default:
return "全部";
}
};
});

})();

References: