iT邦幫忙

DAY 9
1

我在前端 ng 時系列 第 9

自己的 input 自己 format

延續昨天的介紹,今天來討論 ng-model controller 的兩個 Properties: $viewValue 跟 $modelValue 。

$viewValue: String type,顯示給使用者的值
$modelValue: model controller 綁定的值。不一定是 string type,而是我們指定的格式。

ex: Date 格式的 input 在畫面上,顯示的是 “Sep 10 , 2014”。
$viewValue: String type,“Sep 10 , 2014”
$modelValue: Date type,”new Date()"。


範例:
使用 $formatters 跟 $parsers 就可操作 model 跟使用者 input 的 directive.
範例:輸入的數字都會轉成中文的數字
http://plnkr.co/edit/4rwWNTp1qgJ5ZoutaSoc?p=preview

angular.module('myExample')
    .directive('chineseNumber', function () {
        return {
            restrict: 'A',
            // 必須使用 ngModel 取得 input value
            require: 'ngModel',
            link: function (scope, element, attrs, ngModel) {
                // 假如沒有 ngModel 就 gg 了...
                if (!ngModel) return;

                var numbers = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
                // $parsers 是檢查使用者輸入的 value
                ngModel.$parsers.push(changeFormat);

                // $formatter 是檢查來自程式的改變
                ngModel.$formatters.push(changeFormat);
                
                function changeFormat (value) {
                  console.log('value', value);
                    if ('string' === (typeof value)) {
                        var temp = [];
                        // 把 Number 換成中文
                        for (var i = 0, len = value.length; i < len; i++ ) {
                            var num = value[i];
                            if (isNumber(num)) {
                              var cn = numbers[parseFloat(num)];
                              temp.push(cn);
                            } else {
                              temp.push(num)
                            }
                        }
                        var result = temp.join('');
                        // update input value after format, 會改變 $viewValue
                        // 可以使用 element set value 的方式
                        // element.val(result);
                        // 或 再次跑 $parses 確認一次
                        if (result != value) {
                          ngModel.$setViewValue(result);
                          ngModel.$render();
                        }
                        // return format result, 改變 $modelValue
                        return result;
                    } else {
                       return value;
                    }
                }

                // 檢查 input string 是不是 number string
                function isNumber(n) {
                    return !isNaN(parseFloat(n)) && isFinite(n);
                }
            }
        }
    });

結合昨天跟今天的範例,可以製作出有使用者體驗比較好的 input.
範例:只能輸入英文並會自動把英文小寫轉成英文大寫
http://plnkr.co/edit/uTpNzsj2biAC9qkxsn0K?p=preview

angular.module('myExample')
    .directive('upperValidation', function () {
        return {
            restrict: 'A',
            require: 'ngModel',
            link: function (scope, element, attrs, ngModelCtrl) {

                if (!ngModelCtrl) return;

                // uppercase and number only
                var regex = new RegExp('^[A-Z0-9\s]*$');

                // check user input
                ngModelCtrl.$formatters.unshift(function (value) {
                    return checker(value);
                });

                // check model change by code
                ngModelCtrl.$parsers.unshift(function (value) {
                    var upperValue = checker(value);
                    element.val(upperValue);
                    // 檢查是否 viewValue 已經變成 uppercase 了,如沒有,重新 render 一次
                    if(upperValue !== value) {
                        ngModelCtrl.$setViewValue(upperValue);
                        ngModelCtrl.$render();
                    }
                    return upperValue;
                });

                function checker (value) {
                    var valid = regex.test(value);
                    ngModelCtrl.$setValidity('upperMatch', valid);
                    return value ? value.toUpperCase() : '';
                }
            }
        }

    }).directive('match', function () {
        return {
            restrict: 'A',
            require: 'ngModel',
            scope: {
                match: '=match'
            },
            link: function (scope, element, attrs, ngModelCtrl) {

                ngModelCtrl.$parsers.unshift(function(value) {
                    return checker(value);
                });

                scope.$watch('match', function () {
                    // check match value with current value in input
                    checker(ngModelCtrl.$viewValue);
                });

                function checker (value) {
                    var valid = scope.match === value;
                    ngModelCtrl.$setValidity('match', valid);
                    // return value anyway, make sure valid is true/not true
                    return value;
                }

            }
        }

    });
;

總結一下:
ngModelController 提供的 Methods:
$formatters: 檢查當 model 值被程式的改變時候
$parsers: 檢查使用者輸入的 value
$setValidity: 設定 input 是否 valid

Properties:
$viewValue: String type,顯示給使用者的值
$modelValue: model controller 綁定的值。不一定是 string type,而是我們指定的格式。

ngModelController 呼叫 Methods 的順序:
$formatters: (當程式改變 model)
$modelValue -> $formatters -> $viewValue -> $render

$parsers: (當使用者改變 value)
$modelValue <- $parsers <- $viewValue <- $setViewValue

注意:在 $parsers 中 $setValidity set false 的時候, $modelValue 變成 undefined. (註一)

我覺得 validation 還有更多可以發掘的用法,如果有任何想法的,歡迎留言一起討論喔!


(*註一) 當我在研究過程中,發現 AngularJS 1.3.x 有新增 $$invalidModelValue Property 跟 $validators Method。

$$invalidModelValue 是當 ngModelController validity false 的時候,會把 invalid 的值儲存在 $$invalidModelValue property,並把$modelValue 設成 undefined。
$validators: object ,取代原本的 $setValidity method

我還沒有詳細的研究,有興趣的朋友可以看看這篇範例。
[http://stackoverflow.com/a/25488071](http://stackoverflow.com/a/25488071" style="font-family: sans-serif, Arial, Verdana, 'Trebuchet MS'; line-height: 1.6;)


AngularJS Forms Validation 介紹系列文:
AngularJS Forms Validation 介紹
自己的 input 自己 validation
自己的 input 自己 format (本篇)


上一篇
自己的 input 自己驗證
下一篇
初談 ng-filter
系列文
我在前端 ng 時30

尚未有邦友留言

立即登入留言