或是某個欄位AngularJS並沒有實作Validation時(例如input type="file"),
就需要自訂有客製化驗證能力的directive
在這邊的需求如下:
- 製作一個directive,名稱為file-validator
- 配合input type="file"使用
- 可以設定file size(大小,單位Byte)和file type(副檔名), 用法範例:
- 如果fileSize沒指定或小於等於0,則不對fileSize做限制驗證。
- 如果fileType沒指定或為空字串,則不對fileType做限制驗證。
說明:
- 當我們在ng-form裡面的input設置name及ng-model (及ngModelController)後,如果AngularJS有實作此種input type類型的話,Angular會在View中的值或對應model的
值改變時,進行View及model的雙向挷定、同時變更。
ngModelController裡面會存放model的值,即ngModel.$modelValue。
也會存放view(通常為input type中顯示的值),即ngModel.$viewValue。
並且也會管理此input的valid狀態。
但因為AngularJS並沒有對input type="file"進行實作,也就是,不管User在input中選了什麼檔案,皆不會存值到ngModel.$modelValue及ngModel.$viewValue中,當然雙向挷定的model中也不會有值。
所以為們要帶自制的directive中,指定
required : ngModel
來得到管理這個input的ngModel,並且手動的設定model的值。
(!!不要手動用ngModel.$setViewValue()設定viewValue的值,會導致model value 會被蓋掉) - 因為要設定model的值,所以我們要引入$parse,$parse(code)可以代入一串程式碼,有點
像eval,$parse(code)會返回一個函式,並且此函式有一個函式,assign(scope, value),
可以在scope中執行程式碼,並將value賦值給程式碼執行得到的變數。
例如:
$parse("myCtrl.fileUpload").assign(scope, file);
代表在scope中執行 : myCtrl.fileUpload = file; - 在ngModel中,可以使用
ngModel.$setValidity( validType, isValid)
來設定此input的validType是valid還是invalid。 - 在解析如以下字串時,
file-validator="fileType:'png'; fileSize:333;"
我們使用了兩個正規表達式:
fileSize\s*:\s*(\d+)\s;
fileType\s*:\s*(["'])(\w+)\1\s*;
其中第二個正規達式中的 \1 的意思是跟第一個Group相匹配,而第一個Group就是(["']),
也就是fileType:"png"; 或 fileType: 'png';可以匹配,但
fileType:'png"; 或 fileType:"png'; 不可以匹配,
雙引號及單引號要成對使用。
原碼紀錄(怕jsFiddle出問題):
使用angular.js
html:
javascript:
請選擇size小於3MB的PNG檔案
file valid: {{myForm.fileUpload.$valid}}file size (Byte): {{myCtrl.fileUpload.size}}副檔名錯誤檔案過大
javascript:
var myApp = angular.module("myApp", []);
myApp.controller("myController", [function() {
//
}]);
//自製的file validator
myApp.directive('fileValidator', ["$parse", function($parse) {
return {
restrict: 'A',
scope: true,
require: 'ngModel', //代表管理input type="file"這個input的ngModelController,例如此例
//ngModel.name = "fileUpload",
//可以連結ng-model裡指定的變數值(ngModel.modelValue) 和
//input view欄位中顯示的值(ngModel.viewValue)
link: function(scope, elm, attrs, ngModel) {
var expression = attrs.fileValidator;
var fileSizeReg = /fileSize:\s*(\d+)\s*;/;
var fileTypeReg = /fileType\s*:\s*(["'])(\w+)\1\s*;/;
//規定file的size,單位Byte
var fileSizeLimit = fileSizeReg.exec(expression)[1] || 0;
//規定副檔名
var fileTypeLimit = fileTypeReg.exec(expression)[2] || "";
elm.bind('change', function() { //發現input type file值改變時
scope.$apply(function() {
var file = elm[0].files[0]; //取得file資料
var fileType = /.+\.(.+)/.exec(file.name)[1];
//$parse("程式碼")可以返回一個function,之後可以用這個function的
//assign(scope, value)來在scope中進行賦值(value)動作,
//例如: 在scope中墸行: 程式碼 = value (有點像eval())
$parse(attrs.ngModel).assign(scope, file);
//檢查file副檔名及size
//用ngModel設置fileSize的valid
if (fileTypeLimit === "" || fileType === fileTypeLimit) {
ngModel.$setValidity("fileType", true);
} else {
ngModel.$setValidity("fileType", false);
}
//用ngModel設置fileSize的invalid
if (fileSizeLimit <= 0 || file.size < fileSizeLimit) {
ngModel.$setValidity("fileSize", true);
} else {
ngModel.$setValidity("fileSize", false);
}
});
});
}
};
}]);


沒有留言 :
張貼留言