Comment formater une date en utilisant ng-model?

93

J'ai une entrée définie comme

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

Qui est truqué pour être affiché ailleurs sur la page:

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

Lorsque la page se charge, la date de naissance est bien formatée comme quelque chose comme Dec 22, 2009. Cependant, quand je regarde à l'intérieur de mon, <input>cela montre Tue Dec 22 2009 00:00:00 GMT-0800 (Pacific Standard Time)comment JS rend les Dateobjets sous forme de chaînes.

Premièrement, comment puis-je dire à Angular d'afficher la date dans le <input>comme quelque chose comme 12/22/2009? Je n'arrive pas à m'appliquer |filtersà l'intérieur de l' ng-modelattribut.

Deuxièmement, dès que je modifie la date, même en la conservant dans son format d'origine, mon autre texte (à l'intérieur du <td>) ne semble plus appliquer le |datefiltre; il change soudainement de format pour correspondre à celui de la zone de texte d'entrée. Comment puis-je lui faire appliquer le |datefiltre à chaque fois que le modèle change?


Questions connexes:

mpen
la source
J'ai également eu un problème à ce sujet, mais je suis venu avec une solution plus simple utilisant les Date()fonctions js standard : $scope.departDate = new Date(); $scope.departTime = $scope.departDate.toTimeString().slice(0, 5);Et pas besoin d'autres filtres ou de solutions de contournement délicates dans AngularJS IMO.
boldnik

Réponses:

71

Utilisez la validation personnalisée des formulaires http://docs.angularjs.org/guide/forms Démo: http://plnkr.co/edit/NzeauIDVHlgeb6qF75hX?p=preview

Directive utilisant des formateurs et des analyseurs et 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
la source
4
Je voulais créer un petit exemple de formateur et d'analyseur, grâce à votre question d'aujourd'hui, j'ai eu la raison de le faire.
SunnyShah
1
@Mark, Correction du problème Nan dans le second violon. ;)
SunnyShah
1
Suppression de la deuxième approche. C'était beaucoup trop bogué.
SunnyShah
1
Vous pouvez utiliser la directive on-change pour apporter d'autres améliorations. stackoverflow.com/questions/14477904/…
SunnyShah
7
@SunnyShah - super truc, vraiment utile, merci beaucoup. Je me demandais pourquoi vous avez var dateFormat = attrs.moMediumDate;et non var dateFormat = attrs.moDateInput;moMediumDate ne semble pas être défini nulle part, donc si les entrées ne sont pas créées dynamiquement, un format initial n'est jamais sélectionné.
toxaq
37

Voici une directive angular-datetime très pratique . Vous pouvez l'utiliser comme ceci:

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

Il ajoute également un masque à votre entrée et effectue la validation.

Sergueï Svekolnikov
la source
1
J'ai également utilisé cette directive, merci. Si vous l'utilisez avec jQuery UI Datepicker, vous devez vous assurer que le format spécifié dans la datetime="<format>"valeur d'attribut correspond au format spécifié dans l' dateFormatoption Datepicker .
tylerwal
1
Je me demande comment il est possible qu'il y ait une solution parfaite ici avec seulement quelques votes. Voilà pour les dates, et vous pouvez les résoudre ici, maintenant! Merci mec.
C0ZEN
2
Je suis sûr que plus de personnes voteraient pour cela, si la documentation était améliorée pour faciliter son inclusion dans une application angulaire existante.
Joel Hansen du
8

J'ai créé une directive simple pour permettre aux input[type="date"]éléments de formulaire standard de fonctionner correctement avec AngularJS ~ 1.2.16.

Regardez ici: https://github.com/betsol/angular-input-date

Et voici la démo: http://jsfiddle.net/F2LcY/1/

Slava Fomin II
la source
Hé @Lorenzo, qu'est-ce que tu veux dire? Pourriez-vous élaborer s'il vous plaît?
Slava Fomin II
Le mois et l'année sont en français comme Lun Mar Mer pour Mon Tue, Wen
Merlin
@Lorenzo Je ne suis pas sûr de ne pas me tenir debout. La directive n'a rien à voir avec la localisation. La fonction de formatage que vous utilisez utilise probablement les paramètres régionaux de votre navigateur / système. Si vous donnez un exemple, je pourrai vous aider (jsfiddle).
Slava Fomin II
1
supporte juste le chrome, si mauvais.
GeminiYellow
Le but de la directive est de soutenir toutes les plates-formes. Si vous rencontrez des problèmes, pourquoi ne créez-vous pas un problème sur le dépôt GitHub? Je réponds toujours à tous les problèmes.
Slava Fomin II
7

J'utilise jquery datepicker pour sélectionner la date. Ma directive lit la date et la convertit au format de date json (en millisecondes), stocke les ng-modeldonnées tout en affichant la date formatée et inversée si ng-model a une date json (en millisecondes), mon formateur s'affiche dans mon format en tant que jquery datepicker.

Code HTML:

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

Directive angulaire:

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
la source
Oh mon Dieu!! Merci beaucoup !! J'ai littéralement cherché pendant deux jours quelque chose comme cette réponse, mais je n'en ai pas trouvé! Tu gères!
Michael Rentmeister
Fantastique. Je cherchais quelque chose comme ça depuis un moment. J'utilise materializecss pour ma solution. Si quelqu'un trébuche là-dessus et doit convertir cette directive pour se matérialiser, remplacez-le element.datepickerparelement.pickadate
IWI
7

Puisque vous avez utilisé datepicker comme classe, je suppose que vous utilisez un datepicker Jquery ou quelque chose de similaire.

Il existe un moyen de faire ce que vous prévoyez sans utiliser du tout moment.js, en utilisant uniquement les directives datepicker et angularjs.

J'ai donné un exemple ici dans ce violon

Extraits du violon ici:

  1. Datepicker a un format différent et le format angularjs est différent, il faut trouver la correspondance appropriée pour que la date soit présélectionnée dans le contrôle et soit également renseignée dans le champ de saisie pendant que le ng-model est lié. Le format ci-dessous est équivalent au 'mediumDate'format d'AngularJS.

    $(element).find(".datepicker")
              .datepicker({
                 dateFormat: 'M d, yy'
              }); 
  2. La directive d'entrée de date doit avoir une variable de chaîne provisoire pour représenter la forme de date lisible par l'homme.

  3. L'actualisation des différentes sections de la page doit se faire via des événements, tels que $broadcastet $on.

  4. L'utilisation d'un filtre pour représenter la date sous une forme lisible par l'homme est également possible dans ng-model, mais avec une variable de modèle temporaire.

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

J'utilise la directive suivante qui me rend très heureux et la plupart des utilisateurs! Il utilise moment pour l'analyse et le formatage. Cela ressemble un peu à celui de SunnyShah, mentionné plus tôt.

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

Dans mon formulaire, je l'utilise comme ceci:

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

Cela liera un horodatage (millisecondes depuis 1970) à doc.beginet doc.end.

Elmer
la source
1
Il est facile d'oublier le fait que, dans cette fonction de lien, se ngModeltrouve votre identifiant personnalisé pour l'argument du contrôleur, pas une référence directe et nommée à ngModel. C'est un endroit où la convention de nommer l'argument «ctrl» ou «controller» rend les choses plus claires et évite toute confusion.
XML
Merci! J'ai cherché toute la journée une bonne façon de faire cela. Malheureusement, la plupart des autres correctifs impliquaient le passage à un sélecteur de date écrit spécifiquement pour angular, et c'était trop de travail.
Josh Mouch
J'ai essayé la vôtre et la réponse de @SunnyShah, mais la sienne ne semblait pas fonctionner. Pas certain de pourquoi.
Josh Mouch
3

Dans Angular2 + pour toute personne intéressée:

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

avec le type de filtres dans DatePipe Angular.

Quan VO
la source
Dang, je suis coincé dans l'héritage AngularJS sur lequel j'ai été obligé de travailler, et je déteste ça! J'aimerais avoir ce qui précède pour travailler.
Jordan
Il ne s'agit que de taquiner les gens et de les inciter à utiliser Angular2 +. Beurk! AngularJs est totalement la bombe. Pourquoi quelqu'un voudrait-il une solution simple comme celle-ci?
Nebulosar il y a
2

Je préfère que le serveur renvoie la date sans modification, et que javascript fasse le massage de la vue. Mon API renvoie «MM / JJ / AAAA hh: mm: ss» de SQL Server.

Ressource

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' }
        });
    }
);

Manette

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);
        }
    );
};

Directive de validation 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
la source
1

Angularjs ui bootstrap, vous pouvez utiliser angularjs ui bootstrap, il fournit également la validation de la date

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



dans le contrôleur peut spécifier le format que vous souhaitez afficher la date comme datefilter

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

P.JAYASRI
la source
vous n'avez pas besoin de mettre en œuvre la directive pour un seul champ d'entrée
P.JAYASRI