¿Cómo formatear una fecha usando ng-model?

93

Tengo una entrada definida como

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

Que está preparado para mostrarse en otra parte de la página:

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

Cuando se carga la página, la fecha de nacimiento tiene un bonito formato similar a Dec 22, 2009. Sin embargo, cuando miro dentro de mi, <input>se muestra como Tue Dec 22 2009 00:00:00 GMT-0800 (Pacific Standard Time), supongo, cómo JS representa los Dateobjetos como cadenas.

En primer lugar, ¿cómo le digo a Angular que muestre la fecha en el <input>como algo así 12/22/2009? Parece que no puedo aplicar |filtersdentro del ng-modelatributo.

En segundo lugar, tan pronto como edito la fecha, incluso manteniéndola en su formato original, mi otro texto (dentro de <td>) ya no parece aplicar el |datefiltro; de repente cambia de formato para que coincida con el del cuadro de texto de entrada. ¿Cómo hago para que aplique el |datefiltro cada vez que cambia el modelo?


Preguntas relacionadas:

mpen
fuente
También tuve un problema con eso, pero vine con una solución más simple usando Date()funciones js estándar : $scope.departDate = new Date(); $scope.departTime = $scope.departDate.toTimeString().slice(0, 5);y no es necesario en otros filtros o soluciones difíciles en AngularJS IMO.
boldnik

Respuestas:

71

Utilice la validación personalizada de formularios http://docs.angularjs.org/guide/forms Demostración: http://plnkr.co/edit/NzeauIDVHlgeb6qF75hX?p=preview

Directiva que utiliza formateadores y analizadores y 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
fuente
4
Quería crear un pequeño ejemplo de formateador y analizador, gracias a tu pregunta de hoy tengo la razón para hacerlo.
SunnyShah
1
@Mark, solucionó el problema de Nan en segundo lugar. ;)
SunnyShah
1
Se eliminó el segundo enfoque. Tenía demasiados errores.
SunnyShah
1
Puede utilizar la directiva de cambio para realizar más mejoras. stackoverflow.com/questions/14477904/…
SunnyShah
7
@SunnyShah - gran material, realmente útil, muchas gracias. Me preguntaba por qué tiene var dateFormat = attrs.moMediumDate;y no var dateFormat = attrs.moDateInput;moMediumDate no parece estar configurado en ningún lugar, por lo que si las entradas no se crean dinámicamente, nunca se selecciona un formato inicial.
toxaq
37

Aquí hay una directiva angular-datetime muy útil . Puedes usarlo así:

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

También agrega máscara a su entrada y realiza la validación.

Sergei Svekolnikov
fuente
1
Usé esta directiva también, gracias. Si lo está utilizando con jQuery UI Datepicker, debe asegurarse de que el formato especificado en el datetime="<format>"valor del atributo coincida con el formato especificado en la dateFormatopción Datepicker .
Tylerwal
1
Me pregunto cómo es posible que haya una solución perfecta aquí mismo con solo unos pocos votos a favor. Demasiado para las fechas, y puedes resolverlas aquí mismo, ¡ahora mismo! Gracias amigo.
C0ZEN
2
Estoy seguro de que más personas votarían por esto si se mejorara la documentación para que sea más fácil de incluir en una aplicación angular existente.
Joel Hansen
8

input[type="date"]Creé una directiva simple para permitir que los elementos de formulario estándar funcionen correctamente con AngularJS ~ 1.2.16.

Mira aquí: https://github.com/betsol/angular-input-date

Y aquí está la demostración: http://jsfiddle.net/F2LcY/1/

Slava Fomin II
fuente
Hola @Lorenzo, ¿qué quieres decir? ¿Podría explicarme por favor?
Slava Fomin II
El mes y el día están en francés como Lun Mar Mer para el lunes martes, Wen
Merlin
@Lorenzo No estoy seguro de entenderlo. La directiva no tiene nada que ver con la localización. Probablemente la función de formateo que está utilizando está tomando la configuración regional de su navegador / sistema. Si proporciona un ejemplo, podré ayudarlo (jsfiddle).
Slava Fomin II
1
solo es compatible con Chrome, muy mal.
GeminiYellow
El objetivo de la directiva es dar soporte a todas las plataformas. Si tiene problemas con él, ¿por qué no crea un problema en el repositorio de GitHub? Siempre estoy respondiendo a todos los problemas.
Slava Fomin II
7

Estoy usando jquery datepicker para seleccionar la fecha. Mi directiva lee la fecha y la convierte al formato de fecha json (en milisegundos), almacena en ng-modeldatos mientras muestra la fecha formateada.

Código HTML:

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

Directiva 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
fuente
¡¡Oh Dios mío!! ¡¡Muchísimas gracias !! Literalmente he estado buscando durante dos días algo como esta respuesta, ¡pero no pude encontrar una! ¡Tú Molas!
Michael Rentmeister
Fantástico. He estado buscando algo como esto por un tiempo. Estoy usando materializecss para mi solución. Si alguien stubles sobre esto y necesidades para convertir esta directiva para materializarse, simplemente intercambiar element.datepickerconelement.pickadate
IWI
7

Dado que ha utilizado datepicker como clase, supongo que está utilizando un datepicker de Jquery o algo similar.

Hay una manera de hacer lo que pretendes sin usar moment.js en absoluto, simplemente usando solo las directivas datepicker y angularjs.

He dado un ejemplo aquí en este violín.

Extractos del violín aquí:

  1. Datepicker tiene un formato diferente y el formato angularjs es diferente, es necesario encontrar la coincidencia adecuada para que la fecha esté preseleccionada en el control y también se complete en el campo de entrada mientras el modelo ng está vinculado. El siguiente formato es equivalente al 'mediumDate'formato de AngularJS.

    $(element).find(".datepicker")
              .datepicker({
                 dateFormat: 'M d, yy'
              }); 
  2. La directiva de entrada de fecha debe tener una variable de cadena provisional para representar la forma de fecha legible por humanos.

  3. La actualización en diferentes secciones de la página debe ocurrir a través de eventos, como $broadcasty $on.

  4. El uso de filter para representar la fecha en forma legible por humanos también es posible en ng-model pero con una variable de modelo temporal.

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

¡Utilizo la siguiente directiva que me hace muy feliz a mí y a la mayoría de los usuarios! Utiliza el momento para analizar y formatear. Se parece un poco al 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

En mi forma lo uso así:

<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>

Esto vinculará una marca de tiempo (milisegundos desde 1970) a doc.beginy doc.end.

Elmer
fuente
1
Es fácil pasar por alto el hecho de que, dentro de esta función de enlace, se ngModelencuentra su identificador personalizado para el argumento del controlador, no una referencia directa con nombre a ngModel. Este es un lugar donde la convención de nombrar ese argumento 'ctrl' o 'controlador' mantiene las cosas más claras y evita confusiones.
XML
¡Gracias! He estado buscando todo el día una buena manera de hacer esto. Desafortunadamente, la mayoría de las otras correcciones implicaron cambiar a un selector de fechas escrito específicamente para angular, y eso fue demasiado trabajo.
Josh Mouch
Probé tanto la respuesta tuya como la de @SunnyShah, pero la suya no pareció funcionar. No estoy seguro de por qué.
Josh Mouch
3

En Angular2 + para cualquier persona interesada:

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

con tipo de filtros en DatePipe Angular.

Quan VO
fuente
Diablos, estoy atrapado en el AngularJS heredado en el que me he visto obligado a trabajar, ¡y lo odio! Ojalá tuviera lo anterior para trabajar.
Jordania
2

Prefiero que el servidor devuelva la fecha sin modificaciones y que javascript haga la vista de masaje. Mi API devuelve "MM / DD / YYYY hh: mm: ss" de 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);
        }
    );
};

Directiva de validación 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>
efecto
fuente
1

Angularjs ui bootstrap puede usar angularjs ui bootstrap, también proporciona validación de fecha

<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"> 



en el controlador puede especificar cualquier formato que desee para mostrar la fecha como filtro de fecha

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

P.JAYASRI
fuente
no es necesario implementar la directiva solo para un solo campo de entrada
P.JAYASRI