5

我的名为 datePicker 的指令位于输入元素上,它从模型中获取公历日期并将其转换为最终用户的波斯日期。从 datePicker 更改日期(这里是 jalali(波斯日期选择器)),转换为 gregorian 并更新模型是必要的。

  1. 我不确定我使用的绑定类型。
  2. $watch 功能对我不起作用,它在第一次加载页面时起作用。
  3. ngModel 和 dateDirVal 属性在隔离范围内发生了什么?
  4. gregorianToJalali 函数中 ngModelCtrl.$viewValue 的值等于 NaN。为什么?

我的 HTML:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <link href="css/jalali/theme.css" rel="stylesheet" />
    <script src="Scripts/jalali/jalali.js"></script>
    <script src="Scripts/jalali/calendar.js"></script>
    <script src="Scripts/jalali/calendar-setup.js"></script>
    <script src="Scripts/jalali/calendar-fa.js"></script>

    <script src="Scripts/jquery-1.7.1.min.js"></script>
    <script src="Scripts/angular.min.js"></script>
    <script src="Scripts/directive.js"></script>

</head>
<body ng-app="myApp">
    <div ng-controller="myController">
        <input ng-model="dateValue1" date id="id1" />
    </div>
</body>
</html>

和我的指令:

var app = angular.module('myApp', []);

app.controller('myController', function ($scope) {
    var date = new Date('2014', '06', '03');
    var newdate = new Date(date);

    newdate.setDate(newdate.getDate() + 1);
    $scope.dateValue1 = new Date(newdate);
})
.directive('date', function ($compile) {
    return {
        restrict: 'A',
        require: ['date', '^ngModel'],
        scope: {
            dateDirVal: "=ngModel"
        },
        controller: function ($scope) {

        },
        compile: function (element, attr, linker) {
            return {

                post: function postLink($scope, element, attrs, ctrls) {
                    var myCtrl = ctrls[0], ngModelCtrl = ctrls[1];

                    var inputId = element.attr('id').toString();
                    gregorianToJalali();
                    function gregorianToJalali() {
                        var val = ngModelCtrl.$viewValue;
                        if ($scope.dateDirVal) {
                            var inputValue = $scope.dateDirVal;
                            var gDate = new Date(inputValue);
                            var gY = gDate.getFullYear();
                            var gM = gDate.getMonth();
                            var gD = gDate.getDate();
                            var value = JalaliDate.gregorianToJalali(gY, gM, gD);
                            var jalali = value[0].toString() + "/" + value[1].toString() + "/" + value[2].toString();
                            $scope.dateDirVal = jalali;
                        }
                    }

                    Calendar.setup({
                        inputField: inputId,
                        ifFormat: '%Y/%m/%d',
                        dateType: 'jalali'
                    });

                    element.bind("blur", function (e) {
                        var jDate = element.val().split('/');
                        var value = JalaliDate.jalaliToGregorian(jDate[0], jDate[1], jDate[2]);
                        var gDate = new Date(value[0], value[1], value[2]);
                        ngModelCtrl.$setViewValue(gDate);
                        $scope.dateDirVal = value;
                    });

                    $scope.$watch("dateDirVal", function (newValue, OldValue, scope) {
                        if (newValue) {
                            alert(JSON.stringify(newValue));
                        }
                        //ngModelCtrl.$setViewValue(gDate);
                        //$scope.dateDirVal = value;
                    });

                }
            }
        }
    };
});
4

1 回答 1

1

You could use $parsers and $formatters functions pipelines to do that.

Functions on $parsers react with DOM changes and its return changes the model. So, you could use a toGregorianDate function to convert persian date and persist the gregorian.

Functions on $formatters react with model changes, so, whenever the model changes (including rendering the first time) it will be triggered and its returns are rendered into the DOM. So you could use it to call a toPersianDate function and convert the gregorian into persian date to be rendered to your user.

These functions are listeners, and there are a big caution on compile functions documented on ngModelController DOCs alerting that listenners are supposed to be used on link functions

So I recomend you change a bit your code to look more like that

.directive('date', function ($compile) {
    return {
        restrict: 'A',
        require: ['date', '^ngModel'],
        scope: {
            dateDirVal: "=ngModel"
        },
        controller: function ($scope) {

        },
        //use link instead of compiler to use DOM listeners
        link: function ($scope, element, attrs, ctrls) {
            var myCtrl = ctrls[0], ngModelCtrl = ctrls[1];

            var inputId = element.attr('id').toString();

            function gregorianToJalali(viewValue) {
                if ($scope.dateDirVal) {
                    var inputValue = $scope.dateDirVal;
                    var gDate = new Date(inputValue);
                    var gY = gDate.getFullYear();
                    var gM = gDate.getMonth();
                    var gD = gDate.getDate();
                    var value = JalaliDate.gregorianToJalali(gY, gM, gD);
                    var jalali = value[0].toString() + "/" + value[1].toString() + "/" + value[2].toString();

                    //The return of a $parsers is what is saved to the model
                    return jalali;
                }
            }

            Calendar.setup({
                inputField: inputId,
                ifFormat: '%Y/%m/%d',
                dateType: 'jalali'
            });

            function jalaliToGregorian(viewValue) {
                var jDate = viewValue.split('/');
                var value = JalaliDate.jalaliToGregorian(jDate[0], jDate[1], jDate[2]);
                var gDate = new Date(value[0], value[1], value[2]);

                //The return of a formmatter is what is rendered on the DOM
                return gDate;
            });

            //with that, jalaliToGregorian will be called every time a user fill the input
            ngModelCtrl.$parsers.unshift(jalaliToGregorian(viewValue))

            //gregorianToJalali will be called every time a model change, converting 
            // gregorian dates and presenting persian date on the view
            ngModelCtrl.$formatters.unshift(gregorianToJalali(viewValue))

        }
    };
});

I tried to not change so much your code to you figure out what happened... so I neither changed your jalaliToGregorian function, but you need to pay attention, that the $parsers function will bind to the model its return... and you are not checking if the inputed date were a persian date at all... if the function return undefined, the model will not be changed... and if return something else, that return will be binded, being a date or not (null can be binded if the inputed date were wrong)

于 2014-06-07T17:24:09.270 回答