加载 Angular UI Bootstrap Datepicker 时将日期字符串解析为 Date 对象

Posted

技术标签:

【中文标题】加载 Angular UI Bootstrap Datepicker 时将日期字符串解析为 Date 对象【英文标题】:Parse date string to Date object when loading Angular UI Bootstrap Datepicker 【发布时间】:2016-02-11 02:36:31 【问题描述】:

我正在使用 Angular UI Bootstrap Datepicker: https://angular-ui.github.io/bootstrap/#/datepicker

当我使用从服务器接收到的数据呈现表单时,日期时间字段存在问题。我的输入日期选择器如下所示:

<form name="itemForm">
    <input type="datetime" class="form-control" id="startedAt" name="startedAt"
           ng-model="item.startedAt"
           ng-click="open($event, 'startedAt')"
           uib-datepicker-popup="yyyy-MM-dd"
           is-open="datepickers.startedAt"
    />
</form>

我的服务器以 JSON 字符串形式返回响应日期时间:

    
   ...
   startedAt: "2015-05-29T02:00:00+0200"

当我将响应数据分配给模型 $scope.item = response; 时,日期选择器输入字段会正确呈现(选择了正确的日期,并且它的格式正确地以我选择的格式格式化)。问题是验证没有通过。我明白了:

itemForm.startedAt.$invalid == true

我注意到绑定到 datepicker 字段的数据应该是 Date 对象而不是字符串(当我从 datepicker 中选择新日期时,$scope.item.startedAtDate

我设法解决了这个问题并在控制器中执行此操作:

$scope.item = response;
$scope.item.startedAt = new Date($scope.item.startedAt);

它是这样工作的……但我不想每次从服务器收到响应时都手动转换字符串做日期。我尝试创建一个指令,我可以将其分配给 datepicker 输入字段,以便它为我转换 ng-model

.directive("asDate", function () 
    return 
        require: 'ngModel',
        link: function (scope, element, attrs, modelCtrl) 

            modelCtrl.$formatters.push(function (input) 

                var transformedInput = new Date(input);

                if (transformedInput != input) 
                    modelCtrl.$setViewValue(transformedInput);
                    modelCtrl.$render();
                

                return transformedInput;
            );
        
    
)

它工作得很好,因为现在我可以看到Date 对象,当我在我的视图中输出模型时:item.startedAt。然而仍然验证失败!我怀疑这是我理解数据如何在模型和视图之间流动以及 UI Bootstrap 如何挂钩到其中的问题。

此外,当我将指令从 $formatters.push 更改为 $formatters.unshift 时,验证工作正常,但 datepicker 没有格式化我的日期时间(插入了很好的格式化 yyyy-MM-dd 我在输入中看到 ISO 字符串)

【问题讨论】:

天啊,我们在这里遇到了完全相同的问题!!! @VictorParmar 目前我正在这样做 - 从服务器接收时将响应字符串转换为 Date 对象。并在发送到服务器时将 Date 对象转换为字符串。所有这些都在 Angular 控制器中手动完成。也许我会将这个逻辑提取为 Angular 服务,但我认为它不可能与指令有关 是的,加入俱乐部 - 我们最终做了同样的事情 :) 【参考方案1】:

这从 Angular.UI.Bootstrap v0.13.2 (8-2-2015) 开始失效 降级到 0.13.1 有效,这就是我今天遇到的问题。

Wesleycho 说这是故意的 https://github.com/angular-ui/bootstrap/issues/4690

如果有人有建议,我已准备好使用其他支持字符串的日期选择器

...发布此消息后不久,我走上了一条我并不引以为豪的非角度路径,但它适用于 html5 type="date" 和 uib-datepicker-popup。我有一个正则表达式来确定一个字符串是否类似于我见过的两种序列化日期格式之一,然后我有一个递归 javascript 函数来遍历 json 树并用 Date() 替换这些字符串。您可以在放入 $scope(或 viewmodel)之前调用它...

$http.get("../api/comm/" + commId
    ).success(function (resp) 
        fixDates(resp);
        vm.comm = resp;
    );

(我不需要检查字符串长度,但我认为如果字符串显然不是日期,则不运行正则表达式可以节省一些 cpu 周期)

//2015-10-01T00:00:00-04:00
//2015-11-20T18:15:56.6229516-05:00
var isDate = new RegExp("\\d4-\\d2-\\d2T\\d2:\\d2:\\d2(\\.\\d7)?-\\d2:00");

function fixDates(json) 
    for (i in json)
        if (typeof (json[i]) == "object")
            fixDates(json[i]);
        else if (typeof (json[i]) == "string" && (json[i].length == 25 || json[i].length == 33) && isDate.test(json[i]))
            json[i] = new Date(json[i]);
;

【讨论】:

当您使用 javascript for-in 循环时,您可能还应该使用 hasOwnProperty()【参考方案2】:

由于这是 angular-ui-bootstrap datepicker (https://github.com/angular-ui/bootstrap/issues/4690) 的故意行为,我最终使用了 Angular 服务/工厂和 moment 库。

服务dateConverter 可以全局注入以拦截所有 HTTP 请求/响应,或者仅在所需的控制器中。

这里我使用 Restangular 库来处理对 REST API 的请求,因此 response.plain() 方法只接受对象属性,而不是 Restangular 方法/属性。

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

Services
    .factory('dateConverter', ['dateFilter', function (dateFilter) 

        var dateConverter = ;

        dateConverter.prepareResponse = function (response) 
            for(prop in response.plain()) 
                if (response.hasOwnProperty(prop)) 
                    if(moment(response[prop], moment.ISO_8601, true).isValid()) 
                        response[prop] = new Date(response[prop]);
                    
                
            
            return response;
        ;

        dateConverter.prepareRequest = function (item) 
            for(prop in item.plain()) 
                if (item.hasOwnProperty(prop)) 
                    if(angular.isDate(item[prop]))
                         item[prop] = dateFilter(item[prop] , "yyyy-MM-ddTHH:mm:ssZ")
                    
                
            
            return item;
        ;

        return dateConverter;
    ])
;

【讨论】:

【参考方案3】:

你可以在 restangular 转换器中将字符串转换为日期,像这样

RestangularConfigurer
  .addElementTransformer('<RESTRESOURCENAME>', false, function (element) 
      element.createDate = new Date(element.createDate);
      return element;
  )

【讨论】:

有什么使用方法的指导吗?

以上是关于加载 Angular UI Bootstrap Datepicker 时将日期字符串解析为 Date 对象的主要内容,如果未能解决你的问题,请参考以下文章

html angular-ui-bootstrap Typeahead的示例用法(https://angular-ui.github.io/bootstrap/)

在 webpack 中使用 angular-ui-bootstrap

如何在 Angular 1.5 中将 Angular 组件与 ui.bootstrap.modal 一起使用?

我应该使用 Angular UI Bootstrap 还是普通的 Bootstrap 3? [关闭]

如何在打字稿中使用 angular-ui-bootstrap(模态)?

angular-ui-bootstrap 日期选择器和屏蔽输入