Como formatar uma data usando o modelo ng?

93

Eu tenho uma entrada definida como

<input class="datepicker" type="text" ng-model="clientForm.birthDate" />

Que é preparado para ser exibido em outro lugar na página:

<tr>
    <th>Birth Date</th>
    <td>{{client.birthDate|date:'mediumDate'}}</td>
</tr>

Quando a página carrega, a data de nascimento é formatada de maneira agradável como algo parecido Dec 22, 2009. No entanto, quando eu olho dentro do meu <input>, é mostrado como Tue Dec 22 2009 00:00:00 GMT-0800 (Pacific Standard Time)o JS renderiza Dateobjetos como strings.

Em primeiro lugar, como faço para dizer ao Angular para mostrar a data no <input>como algo como 12/22/2009? Não consigo aplicar |filtersdentro do ng-modelatributo.

Em segundo lugar, assim que edito a data, mesmo mantendo-a no formato original, meu outro texto (dentro do <td>) parece não aplicar mais o |datefiltro; ele muda repentinamente de formato para corresponder ao da caixa de texto de entrada. Como faço para que ele aplique o |datefiltro sempre que o modelo mudar?


Perguntas relacionadas:

mpen
fonte
Eu também tive um problema com isso, mas vim com uma solução mais simples usando Date()funções js padrão : $scope.departDate = new Date(); $scope.departTime = $scope.departDate.toTimeString().slice(0, 5);E não há necessidade de outros filtros ou soluções alternativas complicadas no AngularJS IMO.
boldnik de

Respostas:

71

Use a validação personalizada de formulários http://docs.angularjs.org/guide/forms Demo: http://plnkr.co/edit/NzeauIDVHlgeb6qF75hX?p=preview

Diretiva usando formadores e analisadores e MomentJS )

angModule.directive('moDateInput', function ($window) {
    return {
        require:'^ngModel',
        restrict:'A',
        link:function (scope, elm, attrs, ctrl) {
            var moment = $window.moment;
            var dateFormat = attrs.moDateInput;
            attrs.$observe('moDateInput', function (newValue) {
                if (dateFormat == newValue || !ctrl.$modelValue) return;
                dateFormat = newValue;
                ctrl.$modelValue = new Date(ctrl.$setViewValue);
            });

            ctrl.$formatters.unshift(function (modelValue) {
                if (!dateFormat || !modelValue) return "";
                var retVal = moment(modelValue).format(dateFormat);
                return retVal;
            });

            ctrl.$parsers.unshift(function (viewValue) {
                var date = moment(viewValue, dateFormat);
                return (date && date.isValid() && date.year() > 1950 ) ? date.toDate() : "";
            });
        }
    };
});
SunnyShah
fonte
4
Eu queria criar um pequeno exemplo de formatador e analisador, graças à sua pergunta de hoje eu entendi o motivo para fazê-lo.
SunnyShah
1
@Mark, corrigido o problema de Nan no segundo violino. ;)
SunnyShah
1
Segunda abordagem removida. Estava cheio de erros.
SunnyShah
1
Você pode usar a diretiva ao alterar para fazer mais melhorias. stackoverflow.com/questions/14477904/…
SunnyShah
7
@SunnyShah - ótimo material, muito útil, muito obrigado. Eu queria saber por que você tem var dateFormat = attrs.moMediumDate;e não var dateFormat = attrs.moDateInput;moMediumDate não parece estar definido em nenhum lugar, então se as entradas não forem criadas dinamicamente, um formato inicial nunca é selecionado.
toxaq
37

Aqui está uma diretiva angular datetime muito útil . Você pode usá-lo assim:

<input type="text" datetime="yyyy-MM-dd HH:mm:ss" ng-model="myDate">

Ele também adiciona uma máscara à sua entrada e executa a validação.

Sergei Svekolnikov
fonte
1
Eu usei essa diretiva também, obrigado. Se você estiver usando-o com o jQuery UI Datepicker, deve certificar-se de que o formato especificado no datetime="<format>"valor do atributo corresponde ao formato especificado na dateFormatopção do Datepicker .
tylerwal
1
Eu me pergunto como é possível que haja uma solução perfeita aqui com apenas alguns votos positivos. Tanto para datas, e você pode resolvê-los aqui, agora mesmo! Valeu cara.
C0ZEN
2
Tenho certeza que mais pessoas votariam nisso, se a documentação fosse melhorada para torná-la mais fácil de incluir em um aplicativo angular existente.
Joel Hansen
8

Eu criei uma diretiva simples para permitir que input[type="date"]elementos de formulário padrão funcionem corretamente com AngularJS ~ 1.2.16.

Olhe aqui: https://github.com/betsol/angular-input-date

E aqui está a demonstração: http://jsfiddle.net/F2LcY/1/

Slava Fomin II
fonte
Olá @Lorenzo, o que você quer dizer? Você poderia explicar, por favor?
Slava Fomin II
Os dias do mês do ano são em francês como Lun Mar Mer para Mon Tue, Wen
Merlin
@Lorenzo Não tenho certeza se entendi. A diretiva não tem nada a ver com localização. Provavelmente, a função de formatação que você está usando está usando a configuração local do seu navegador / sistema. Se você der um exemplo, poderei ajudar (jsfiddle).
Slava Fomin II
1
apenas suporte cromo, tão ruim.
GeminiYellow
O objetivo da diretiva é oferecer suporte a todas as plataformas. Se você tiver problemas com isso, por que não cria um problema no repositório GitHub? Estou sempre respondendo a todos os problemas.
Slava Fomin II
7

Estou usando o jquery datepicker para selecionar a data. Minha diretiva lê a data e converte para o formato de data json (em milissegundos), armazena em ng-modeldados enquanto exibe a data formatada. E reverte se o modelo ng tem data json (em milissegundos), meu formatador exibe em meu formato como jquery datepicker.

Código HTML:

<input type="text" jqdatepicker  ng-model="course.launchDate" required readonly />

Diretiva Angular:

myModule.directive('jqdatepicker', function ($filter) {
    return {
        restrict: 'A',
        require: 'ngModel',
         link: function (scope, element, attrs, ngModelCtrl) {
            element.datepicker({
                dateFormat: 'dd/mm/yy',
                onSelect: function (date) {   
                    var ar=date.split("/");
                    date=new Date(ar[2]+"-"+ar[1]+"-"+ar[0]);
                    ngModelCtrl.$setViewValue(date.getTime());            
                    scope.$apply();
                }
            });
            ngModelCtrl.$formatters.unshift(function(v) {
            return $filter('date')(v,'dd/MM/yyyy'); 
            });

        }
    };
});
Amit Bhandari
fonte
Oh meu Deus!! Muito obrigado !! Estou há dois dias procurando por algo assim, literalmente, mas não consegui encontrar! Você é demais!
Michael Rentmeister
Fantástico. Há algum tempo que procuro algo assim. Estou usando materializecss para minha solução. Se alguém hesitar sobre isso e precisar converter esta diretiva para materializar, basta trocar element.datepickercomelement.pickadate
IWI
7

Já que você usou datepicker como classe, estou assumindo que está usando um datepicker Jquery ou algo semelhante.

Existe uma maneira de fazer o que você pretende sem usar moment.js, usando apenas as diretivas datepicker e angularjs.

Eu dei um exemplo aqui neste Fiddle

Trechos do violino aqui:

  1. Datepicker tem um formato diferente e o formato angularjs é diferente, precisa encontrar a correspondência apropriada para que a data seja pré-selecionada no controle e também seja preenchida no campo de entrada enquanto o modelo ng é vinculado. O formato abaixo é equivalente ao 'mediumDate'formato de AngularJS.

    $(element).find(".datepicker")
              .datepicker({
                 dateFormat: 'M d, yy'
              }); 
  2. A diretiva de entrada de data precisa ter uma variável de string provisória para representar a forma legível de data.

  3. A atualização em diferentes seções da página deve acontecer por meio de eventos, como $broadcaste $on.

  4. Usar filtro para representar a data em forma legível por humanos também é possível no modelo ng, mas com uma variável de modelo temporária.

    $scope.dateValString = $filter('date')($scope.dateVal, 'mediumDate');
Praveenram Balachandar
fonte
6

Eu uso a seguinte diretiva que deixa a mim e a muitos usuários muito felizes! Ele usa o momento para análise e formatação. Parece um pouco com o de SunnyShah, mencionado anteriormente.

angular.module('app.directives')

.directive('appDatetime', function ($window) {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, element, attrs, ngModel) {
            var moment = $window.moment;

            ngModel.$formatters.push(formatter);
            ngModel.$parsers.push(parser);

            element.on('change', function (e) {
                var element = e.target;
                element.value = formatter(ngModel.$modelValue);
            });

            function parser(value) {
                var m = moment(value);
                var valid = m.isValid();
                ngModel.$setValidity('datetime', valid);
                if (valid) return m.valueOf();
                else return value;
            }

            function formatter(value) {
                var m = moment(value);
                var valid = m.isValid();
                if (valid) return m.format("LLLL");
                else return value;

            }

        } //link
    };

}); //appDatetime

No meu formulário, eu o uso assim:

<label>begin: <input type="text" ng-model="doc.begin" app-datetime required /></label>
<label>end: <input type="text" ng-model="doc.end" app-datetime required /></label>

Isso vinculará um carimbo de data / hora (milissegundos desde 1970) a doc.begine doc.end.

Elmer
fonte
1
É fácil ignorar o fato de que, dentro desta função de link, ngModelestá o seu identificador personalizado para o argumento do controlador, não uma referência direta nomeada ngModel. Este é um ponto onde a convenção de nomear aquele arg 'ctrl' ou 'controlador' mantém as coisas mais claras e evita confusão.
XML de
Obrigado! Procurei o dia todo por uma maneira legal de fazer isso. Infelizmente, a maioria das outras correções envolvia a troca para um selecionador de data escrito especificamente para angular, e isso dava muito trabalho.
Josh Mouch
Tentei a sua resposta e a de @SunnyShah, mas a resposta dele não funcionou. Não sei por quê.
Josh Mouch
3

No Angular2 + para qualquer pessoa interessada:

<input type="text" placeholder="My Date" [ngModel]="myDate | date: 'longDate'">

com tipos de filtros em DatePipe Angular.

Quan VO
fonte
Caramba, estou preso no AngularJS legado no qual fui forçado a trabalhar e odeio isso! Gostaria de ter o acima para trabalhar.
Jordan
Isso é apenas provocar as pessoas e induzi-las a usar o Angular2 +. Que nojo! AngularJs é totalmente a bomba. Por que alguém iria querer uma solução simples como essa.
Nebulosar
2

Prefiro que o servidor retorne a data sem modificação, e que o javascript faça a massagem da visualização. Minha API retorna "MM / DD / AAAA hh: mm: ss" do SQL Server.

Recurso

angular.module('myApp').factory('myResource',
    function($resource) {
        return $resource('api/myRestEndpoint/', null,
        {
            'GET': { method: 'GET' },
            'QUERY': { method: 'GET', isArray: true },
            'POST': { method: 'POST' },
            'PUT': { method: 'PUT' },
            'DELETE': { method: 'DELETE' }
        });
    }
);

Controlador

var getHttpJson = function () {
    return myResource.GET().$promise.then(
        function (response) {

            if (response.myDateExample) {
                response.myDateExample = $filter('date')(new Date(response.myDateExample), 'M/d/yyyy');
            };

            $scope.myModel= response;
        },
        function (response) {
            console.log(response.data);
        }
    );
};

Diretiva de validação myDate

angular.module('myApp').directive('myDate',
    function($window) {
        return {
            require: 'ngModel',
            link: function(scope, element, attrs, ngModel) {

                var moment = $window.moment;

                var acceptableFormats = ['M/D/YYYY', 'M-D-YYYY'];

                function isDate(value) {

                    var m = moment(value, acceptableFormats, true);

                    var isValid = m.isValid();

                    //console.log(value);
                    //console.log(isValid);

                    return isValid;

                };

                ngModel.$parsers.push(function(value) {

                    if (!value || value.length === 0) {
                         return value;
                    };

                    if (isDate(value)) {
                        ngModel.$setValidity('myDate', true);
                    } else {
                        ngModel.$setValidity('myDate', false);
                    }

                    return value;

                });

            }
        }
    }
);

HTML

<div class="form-group">
    <label for="myDateExample">My Date Example</label>
    <input id="myDateExample"
           name="myDateExample"
           class="form-control"
           required=""
           my-date
           maxlength="50"
           ng-model="myModel.myDateExample"
           type="text" />
    <div ng-messages="myForm.myDateExample.$error" ng-if="myForm.$submitted || myForm.myDateExample.$touched" class="errors">
        <div ng-messages-include="template/validation/messages.html"></div>
    </div>
</div>

template / validation / messages.html

<div ng-message="required">Required Field</div>
<div ng-message="number">Must be a number</div>
<div ng-message="email">Must be a valid email address</div>
<div ng-message="minlength">The data entered is too short</div>
<div ng-message="maxlength">The data entered is too long</div>
<div ng-message="myDate">Must be a valid date</div>
meffect
fonte
1

Angularjs ui bootstrap você pode usar o angularjs ui bootstrap, ele fornece validação de data também

<input type="text"  class="form-control" 
datepicker-popup="{{format}}" ng-model="dt" is-open="opened" 
min-date="minDate" max-date="'2015-06-22'"  datepickeroptions="dateOptions"
date-disabled="disabled(date, mode)" ng-required="true"> 



no controlador pode especificar qualquer formato que você deseja exibir a data como datafiltro

$ scope.formats = ['dd-MMMM-aaaa', 'aaaa / MM / dd', 'dd.MM.aaaa', 'shortDate'];

P.JAYASRI
fonte
você não precisa implementar a diretiva apenas para um único campo de entrada
P.JAYASRI