Как отформатировать дату с помощью ng-модели?

94

У меня есть вход, определенный как

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

Что настроено для отображения в другом месте на странице:

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

Когда страница загружается, дата рождения красиво оформляется как что-то вроде Dec 22, 2009. Однако, когда я заглядываю внутрь, <input>он показывает, что, как Tue Dec 22 2009 00:00:00 GMT-0800 (Pacific Standard Time)я предполагаю, JS отображает Dateобъекты как строки.

Во-первых, как мне сказать Angular показывать дату в <input>виде чего-то вроде 12/22/2009? Кажется, я не могу применить |filtersвнутри ng-modelатрибута.

Во-вторых, как только я редактирую дату, даже сохраняя ее в исходном формате, мой другой текст (внутри <td>), похоже, больше не применяет |dateфильтр; он внезапно меняет форматы, чтобы соответствовать формату текстового поля ввода. Как заставить его применять |dateфильтр каждый раз при изменении модели?


Связанные вопросы:

mpen
источник
У меня тоже была проблема, но я пришел с более простым решением с использованием стандартных Date()функций js : $scope.departDate = new Date(); $scope.departTime = $scope.departDate.toTimeString().slice(0, 5);и мне не нужны были другие фильтры или хитрые обходные пути в AngularJS IMO.
boldnik

Ответы:

71

Используйте настраиваемую проверку форм http://docs.angularjs.org/guide/forms Демо: http://plnkr.co/edit/NzeauIDVHlgeb6qF75hX?p=preview

Директива с использованием форматеров и парсеров и 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() : "";
            });
        }
    };
});
СанниШах
источник
4
Я хотел создать небольшой пример средства форматирования и синтаксического анализатора. Благодаря вашему сегодняшнему вопросу у меня появилась причина это сделать.
SunnyShah
1
@Mark, Исправлена ​​проблема с наном во второй скрипке. ;)
SunnyShah
1
Удален второй подход. Это было слишком плохо.
SunnyShah
1
Вы можете использовать директиву on-change для дальнейших улучшений. stackoverflow.com/questions/14477904/…
SunnyShah
7
@SunnyShah - отличный материал, очень полезный, большое спасибо. Мне было интересно, почему у вас есть, var dateFormat = attrs.moMediumDate;а не var dateFormat = attrs.moDateInput;moMediumDate, похоже, нигде не установлен, поэтому, если входы не создаются динамически, исходный формат никогда не выбирается.
toxaq
37

Вот очень удобная директива angular-datetime . Вы можете использовать это так:

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

Он также добавляет маску к вашему вводу и выполняет проверку.

Сергей Свекольников
источник
1
Я тоже использовал эту директиву, спасибо. Если вы используете его с jQuery UI Datepicker, вы должны убедиться, что формат, указанный в datetime="<format>"значении атрибута, соответствует формату, указанному в параметре Datepicker dateFormat.
Tylerwal 06
1
Интересно, как это возможно, что прямо здесь есть идеальное решение с несколькими голосами за. Так много свиданий, и вы можете решить их прямо здесь, прямо сейчас! Спасибо чувак.
C0ZEN
2
Я уверен, что больше людей проголосовали бы за это, если бы документация была улучшена, чтобы ее было легче включать в существующее приложение angular.
Joel Hansen,
8

Я создал простую директиву для включения стандартных input[type="date"] элементам формы правильно работать с AngularJS ~ 1.2.16.

Смотри сюда: https://github.com/betsol/angular-input-date

А вот и демонстрация: http://jsfiddle.net/F2LcY/1/

Слава Фомин II
источник
Привет, @ Лоренцо, что ты имеешь в виду? Не могли бы вы уточнить, пожалуйста?
Слава Фомин II
День месяца и года на французском языке, например, Lun Mar Mer для Mon Вт, Wen
Merlin
@ Лоренцо Я не уверен, что понял. Директива не имеет ничего общего с локализацией. Вероятно, используемая вами функция форматирования берет настройки локали из вашего браузера / системы. Если вы приведете пример, я смогу помочь (jsfiddle).
Слава Фомин II
1
просто поддерживаю хром, так плохо.
GeminiYellow 05
Цель директивы - поддерживать все платформы. Если у вас есть проблемы с этим, почему бы вам не создать проблему в репозитории GitHub? Я всегда отвечаю на все вопросы.
Слава Фомин II
7

Я использую jquery datepicker для выбора даты. Моя директива считывает дату и конвертирует ее в формат даты json (в миллисекундах), хранится вng-model данные при отображении отформатированной даты. И наоборот, если ng-модель имеет дату json (в миллисекундах), мой форматировщик отображается в моем формате как jquery datepicker.

HTML-код:

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

Угловая директива:

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

        }
    };
});
Амит Бхандари
источник
Боже мой !! Оооочень большое спасибо !! Я буквально два дня искал что-то вроде этого ответа, но не нашел! Ты жжешь!
Майкл Рентмайстер
Фантастика. Я давно искал что-то подобное. Я использую для своего решения materializecss. Если кто - то stubles на этом и потребности преобразовать эту директиву для материализации, просто выгрузить element.datepickerсelement.pickadate
IWI
7

Поскольку вы использовали datepicker как класс, я предполагаю, что вы используете datepicker JQuery или что-то подобное.

Есть способ делать то, что вы намереваетесь, вообще без использования moment.js, используя только директивы datepicker и angularjs.

Я привел пример здесь, в этой скрипке

Выдержки из скрипки здесь:

  1. Datepicker имеет другой формат, а формат angularjs отличается, необходимо найти соответствующее совпадение, чтобы дата была предварительно выбрана в элементе управления и также заполнялась в поле ввода, пока привязана ng-модель. Приведенный ниже формат эквивалентен 'mediumDate'формату AngularJS.

    $(element).find(".datepicker")
              .datepicker({
                 dateFormat: 'M d, yy'
              }); 
    
  2. Директива ввода даты должна иметь промежуточную строковую переменную для представления даты в удобочитаемой форме.

  3. Обновление в разных разделах страницы должно происходить с помощью событий, таких как $broadcastи $on.

  4. Использование фильтра для представления даты в удобочитаемой форме возможно и в ng-модели, но с временной переменной модели.

    $scope.dateValString = $filter('date')($scope.dateVal, 'mediumDate');
    
Правеенрам Балачандар
источник
6

Я использую следующую директиву, которая делает меня и большинство пользователей очень счастливыми! Он использует момент для синтаксического анализа и форматирования. Он немного похож на упомянутый ранее 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

В своей форме я использую это так:

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

Это свяжет временную метку (миллисекунды с 1970 года) с doc.beginи doc.end.

Элмер
источник
1
Легко упустить из виду тот факт, что внутри этой функции ссылки ngModelнаходится ваш пользовательский идентификатор для аргумента контроллера, а не прямая именованная ссылка на ngModel. Это место, где соглашение об именовании этого аргумента «ctrl» или «контроллер» делает вещи более ясными и позволяет избежать путаницы.
XML
Благодарность! Я весь день искал хороший способ сделать это. К сожалению, большинство других исправлений связано с переключением на средство выбора даты, написанное специально для angular, а это было слишком много работы.
Джош Муч
Я попробовал как ваш, так и ответ @SunnyShah, но его, похоже, не сработало. Не знаю почему.
Джош Муч
3

В Angular2 + для всех, кому интересно:

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

с типом фильтров в DatePipe Angular.

Куан В.О.
источник
Черт, я застрял в устаревшем AngularJS, над которым мне пришлось работать, и я его ненавижу! Жаль, что у меня не было вышеуказанного, чтобы работать.
Jordan
Это просто дразнить людей и заставлять их использовать Angular2 +. Фу! AngularJs - настоящая бомба. Зачем кому-то нужно такое простое решение.
Nebulosar
2

Я предпочитаю, чтобы сервер возвращал дату без изменений, а javascript выполнял массирование представления. Мой API возвращает «ММ / ДД / ГГГГ чч: мм: сс» из SQL Server.

Ресурс

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

Контроллер

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

Директива проверки 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>

шаблон / проверка / 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>
Северный странник
источник
1

Angularjs ui bootstrap, вы можете использовать angularjs ui bootstrap, он также обеспечивает проверку даты

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



в контроллере можно указать любой формат для отображения даты как datefilter

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

П.ДЖАЯСРИ
источник
вам не нужно внедрять директиву только для одного поля ввода
П.ДЖАЯСРИ 05