Jak sformatować datę za pomocą modelu ng?

94

Mam dane wejściowe zdefiniowane jako

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

Który jest skonfigurowany do wyświetlania w innym miejscu na stronie:

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

Kiedy strona się ładuje, data urodzenia jest ładnie sformatowana jako coś podobnego Dec 22, 2009. Jednak kiedy patrzę do środka <input>, widać Tue Dec 22 2009 00:00:00 GMT-0800 (Pacific Standard Time), że wydaje mi się, że JS renderuje Dateobiekty jako ciągi.

Po pierwsze, jak mam powiedzieć Angularowi, żeby pokazywał datę <input>jako coś podobnego 12/22/2009? Nie mogę zastosować |filterswewnątrz ng-modelatrybutu.

Po drugie, gdy tylko edytuję datę, nawet zachowując ją w oryginalnym formacie, mój drugi tekst (wewnątrz <td>) nie wydaje się już stosować |datefiltra; nagle zmienia format, aby dopasować go do pola tekstowego wejściowego. Jak mam zastosować |datefiltr za każdym razem, gdy model się zmienia?


Powiązane pytania:

mpen
źródło
Miałem też z tym problem, ale otrzymałem prostsze rozwiązanie wykorzystujące standardowe Date()funkcje js : $scope.departDate = new Date(); $scope.departTime = $scope.departDate.toTimeString().slice(0, 5);i nie ma potrzeby stosowania innych filtrów lub trudnych obejść w AngularJS IMO.
boldnik

Odpowiedzi:

71

Użyj niestandardowej walidacji formularzy http://docs.angularjs.org/guide/forms Demo: http://plnkr.co/edit/NzeauIDVHlgeb6qF75hX?p=preview

Dyrektywa wykorzystująca formatery i parsery oraz 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
źródło
4
Chciałem stworzyć mały przykład formatera i parsera, dzięki twojemu pytaniu mam dziś powód, aby to zrobić.
SunnyShah
1
@Mark, Naprawiono problem z Nan w drugich skrzypcach. ;)
SunnyShah
1
Usunięto drugie podejście. To było zbyt zapchane.
SunnyShah
1
Możesz użyć dyrektywy przy zmianie, aby wprowadzić dalsze ulepszenia. stackoverflow.com/questions/14477904/…
SunnyShah
7
@SunnyShah - świetne rzeczy, bardzo pomocne, bardzo dziękuję. Zastanawiałem się, dlaczego masz, var dateFormat = attrs.moMediumDate;a nie, var dateFormat = attrs.moDateInput;moMediumDate nie wydaje się być nigdzie ustawione, więc jeśli dane wejściowe nie są tworzone dynamicznie, format początkowy nigdy nie jest wybierany.
toxaq
37

Tutaj jest bardzo przydatna dyrektywa kątowa-data-godzina . Możesz go używać w ten sposób:

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

Dodaje również maskę do danych wejściowych i przeprowadza walidację.

Siergiej Svekolnikov
źródło
1
Skorzystałem również z tej dyrektywy, dzięki. Jeśli używasz go z Datepicker interfejsu użytkownika jQuery, musisz upewnić się, że format określony w datetime="<format>"wartości atrybutu jest zgodny z formatem określonym w opcji Datepicker dateFormat.
tylerwal
1
Zastanawiam się, jak to możliwe, że jest tutaj idealne rozwiązanie z zaledwie kilkoma głosami pozytywnymi. Tyle na temat dat, a możesz je rozwiązać tutaj, teraz! Dzięki stary.
C0ZEN
2
Jestem pewien, że więcej osób zagłosowałoby za tym, gdyby dokumentacja została ulepszona, aby ułatwić włączenie do istniejącej aplikacji kątowej.
Joel Hansen,
8

Stworzyłem prostą dyrektywę, aby umożliwić input[type="date"]poprawną pracę standardowych elementów formularzy z AngularJS ~ 1.2.16.

Zajrzyj tutaj: https://github.com/betsol/angular-input-date

A oto demo: http://jsfiddle.net/F2LcY/1/

Slava Fomin II
źródło
Hej @Lorenzo, co masz na myśli? Czy mógłbyś to rozwinąć?
Slava Fomin II
Dzień miesiąca i roku są w języku francuskim, jak Lun Mar Mer dla Mon Tue, Wen
Merlin
@Lorenzo Nie jestem pewien, czy nie rozumiem. Dyrektywa nie ma nic wspólnego z lokalizacją. Prawdopodobnie funkcja formatowania, której używasz, pobiera ustawienia regionalne z przeglądarki / systemu. Jeśli podasz przykład, będę w stanie pomóc (jsfiddle).
Slava Fomin II
1
po prostu obsługuj chrome, tak źle.
GeminiYellow
Celem dyrektywy jest wsparcie wszystkich platform. Jeśli masz z tym problemy, dlaczego nie utworzysz problemu w repozytorium GitHub? Zawsze odpowiadam na wszystkie problemy.
Slava Fomin II
7

Do wyboru daty używam jquery datepicker. Moja dyrektywa odczytuje datę i konwertuje ją na format daty json (w milisekundach), przechowując w ng-modeldanych podczas wyświetlania sformatowanej daty. I odwracaj, jeśli model ng ma datę JSON (w milisekundach), mój formater wyświetla w moim formacie jako jquery datepicker.

Kod HTML:

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

Dyrektywa kątowa:

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
źródło
O mój Boże!! Dziękuję bardzo !! Dosłownie przez dwa dni szukałem czegoś takiego jak ta odpowiedź, ale nie mogłem jej znaleźć! Rządzisz!
Michael Rentmeister
Fantastyczny. Od jakiegoś czasu szukałem czegoś takiego. Używam materializecss do mojego rozwiązania. Jeśli ktoś stubles na tej i potrzeb przekonwertować tę dyrektywę do urzeczywistnią, po prostu zamienić się element.datepickerzelement.pickadate
IWI
7

Ponieważ użyłeś Datepicker jako klasy, zakładam, że używasz Jquery Datepicker lub czegoś podobnego.

Istnieje sposób na zrobienie tego, co zamierzasz, bez używania w ogóle moment.js, używając wyłącznie dyrektyw datepicker i angularjs.

Podałem tutaj przykład w tym skrzypcach

Fragmenty ze skrzypiec tutaj:

  1. Datepicker ma inny format, a format angularjs jest inny, należy znaleźć odpowiednie dopasowanie, aby data została wstępnie wybrana w kontrolce i jest również wypełniana w polu wejściowym, gdy model ng jest związany. Poniższy format jest odpowiednikiem 'mediumDate'formatu AngularJS.

    $(element).find(".datepicker")
              .datepicker({
                 dateFormat: 'M d, yy'
              }); 
    
  2. Dyrektywa wejściowa daty musi mieć tymczasową zmienną łańcuchową, która reprezentuje datę w postaci czytelnej dla człowieka.

  3. Odświeżanie w różnych sekcjach strony powinno odbywać się za pośrednictwem wydarzeń, takich jak $broadcasti $on.

  4. Użycie filtru do przedstawienia daty w postaci czytelnej dla człowieka jest również możliwe w modelu ng, ale z tymczasową zmienną modelu.

    $scope.dateValString = $filter('date')($scope.dateVal, 'mediumDate');
    
Praveenram Balachandar
źródło
6

Używam następującej dyrektywy, która bardzo mnie cieszy i większość użytkowników! Wykorzystuje moment do analizowania i formatowania. Wygląda trochę jak wspomniany wcześniej SunnyShah.

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

W mojej formie używam tego w ten sposób:

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

Spowoduje to powiązanie znacznika czasu (w milisekundach od 1970 r.) Z doc.begini doc.end.

Elmer
źródło
1
Łatwo przeoczyć fakt, że wewnątrz tej funkcji łączenia ngModelznajduje się niestandardowy identyfikator argumentu kontrolera, a nie bezpośrednie nazwane odwołanie do ngModel. Jest to miejsce, w którym konwencja nazywania argumentów „ctrl” lub „controller” zapewnia większą przejrzystość i pozwala uniknąć nieporozumień.
XML
Dzięki! Cały dzień szukałem fajnego sposobu na zrobienie tego. Niestety, większość innych poprawek polegała na przełączeniu się na selektor dat napisany specjalnie dla kątów, a to było zbyt dużo pracy.
Josh Mouch
Wypróbowałem odpowiedź zarówno twoją, jak i @SunnyShah, ale jego wydaje się nie działać. Nie pewny dlaczego.
Josh Mouch
3

W Angular2 + dla wszystkich zainteresowanych:

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

z typem filtrów w DatePipe Angular.

Quan VO
źródło
Cholera, utknąłem w starszym AngularJS, nad którym byłem zmuszony pracować i nienawidzę tego! Chciałbym mieć powyższe do pracy.
Jordan
To tylko drażnienie ludzi i nakłanianie ich do używania Angular2 +. Fuj! AngularJs to prawdziwa bomba. Dlaczego ktoś miałby chcieć takiego prostego rozwiązania.
Nebulosar
2

Wolę, aby serwer zwracał datę bez modyfikacji, a javascript wykonał masowanie widoku. Mój interfejs API zwraca „MM / DD / RRRR gg: mm: ss” z SQL Server.

Ratunek

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

Kontroler

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

Dyrektywa walidacyjna 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>
Northstrider
źródło
1

Angularjs ui bootstrap możesz użyć angularjs ui bootstrap, zapewnia również walidację daty

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



w kontrolerze może określić dowolny format, w jakim chcesz wyświetlać datę jako datefilter

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

P.JAYASRI
źródło
nie ma potrzeby implementowania dyrektywy tylko dla pojedynczego pola wejściowego
P.JAYASRI