Подтвердить что ты не робот

Как создать директиву обложки angularJs для yi-bootstrap datepicker?

Я использую директиву ui.bootstrap.datepicker для отображения некоторого поля даты. Однако большую часть времени мне нужна такая же настройка: я хочу, чтобы она появилась вместе с всплывающим окном и кнопкой всплывающего окна, а также я хочу, чтобы немецкие имена тексты. Это снова и снова создает тот же код для кнопки и текстов и форматирования, поэтому я написал свою собственную директиву, чтобы я не повторял себя.

Вот планком с моей директивой. Однако я, кажется, делаю это неправильно. Если вы выбираете дату с помощью выбора даты, используя параметр даты "date 1", который не использует мою директиву, все работает нормально. Я бы ожидал то же самое для Date 2, но вместо отображения даты в соответствии с шаблоном, который я предоставил в поле ввода (или любым другим ожидаемым значением), он отображает представление .toString() объекта даты (например, Fri Apr 03 2015 00:00:00 GMT+0200 (CEST)).

Вот моя директива:

angular.module('ui.bootstrap.demo').directive('myDatepicker', function($compile) {
  var controllerName = 'dateEditCtrl';
  return {
      restrict: 'A',
      require: '?ngModel',
      scope: true,
      link: function(scope, element) {
          var wrapper = angular.element(
              '<div class="input-group">' +
                '<span class="input-group-btn">' +
                  '<button type="button" class="btn btn-default" ng-click="' + controllerName + '.openPopup($event)"><i class="glyphicon glyphicon-calendar"></i></button>' +
                '</span>' +
              '</div>');

          function setAttributeIfNotExists(name, value) {
              var oldValue = element.attr(name);
              if (!angular.isDefined(oldValue) || oldValue === false) {
                  element.attr(name, value);
              }
          }
          setAttributeIfNotExists('type', 'text');
          setAttributeIfNotExists('is-open', controllerName + '.popupOpen');
          setAttributeIfNotExists('datepicker-popup', 'dd.MM.yyyy');
          setAttributeIfNotExists('close-text', 'Schließen');
          setAttributeIfNotExists('clear-text', 'Löschen');
          setAttributeIfNotExists('current-text', 'Heute');
          element.addClass('form-control');
          element.removeAttr('my-datepicker');

          element.after(wrapper);
          wrapper.prepend(element);
          $compile(wrapper)(scope);

          scope.$on('$destroy', function () {
              wrapper.after(element);
              wrapper.remove();
          });
      },
      controller: function() {
          this.popupOpen = false;
          this.openPopup = function($event) {
              $event.preventDefault();
              $event.stopPropagation();
              this.popupOpen = true;
          };
      },
      controllerAs: controllerName
  };
});

И как я его использую:

<input my-datepicker="" type="text" ng-model="container.two" id="myDP" />

(Концепция была вдохновлена ​​этим ответом)

Я использую angular 1.3 (plunker находится на 1.2, потому что я просто разблокировал плункер из angular -ui-bootstrap документация датпикера). Надеюсь, это не имеет никакого значения.

Почему текст выводится на моем входе неправильно и как это делается правильно?

Update

Тем временем я сделал небольшой прогресс. После более подробного ознакомления с подробностями о компиляции и ссылке в this plunkr Я использую функцию компиляции, а не функцию ссылки, чтобы выполнять мои манипуляции с DOM. Я все еще немного смущен этой выдержкой из документов:

Примечание: экземпляр шаблона и экземпляр ссылки могут быть разными объектами, если шаблон был клонирован. По этой причине небезопасно делать что-либо другое, кроме преобразований DOM, которые применяются ко всем клонированным узлам DOM в функции компиляции. В частности, регистрация слушателя DOM должна выполняться в функции связывания, а не в функции компиляции.

Особенно интересно, что подразумевается под ", которые применяются ко всем клонированным узлам DOM". Первоначально я думал, что это означает, что это применимо ко всем клонам шаблона DOM, но это, похоже, не так.

Во всяком случае: Моя новая версия компиляции отлично работает с хромом. В Firefox мне нужно сначала выбрать дату, используя средство выбора даты, и после этого все будет хорошо (проблема с Firefox решена сама собой, если я изменю undefined на null (plunkr) в парсере даты выбора даты). Так что это тоже не последняя вещь. Кроме того, я использую ng-model2 вместо ng-model, который я переименовываю во время компиляции. Если я этого не сделаю, все еще сломано. По-прежнему не знаю, почему.

4b9b3361

Ответ 1

Ваша директива будет работать, если вы добавите эти 2 строки в определение своей директивы:

return {
    priority: 1,
    terminal: true,
    ...
 }

Это связано с порядком выполнения директив.

Итак, в вашем коде

<input my-datepicker="" type="text" ng-model="container.two" id="myDP" />

Существуют две директивы: ngModel и myDatepicker. С приоритетом вы можете выполнить свою собственную директиву до выполнения ngModel.

Ответ 2

Честно говоря, я не совсем уверен, почему это вызвало и почему ваша дата была "toString-ed", прежде чем показывать ее на входе.

Однако я нашел места для реструктуризации вашей директивы и удаления ненужного кода, такого как $compile сервис, изменения атрибутов, наследование областей, require в директиве и т.д. Я использовал изолированную область, так как я не думайте, что каждое использование директивы должно знать родительскую область, поскольку это может привести к порочным ошибкам в будущем. Это моя измененная директива:

angular.module('ui.bootstrap.demo').directive('myDatepicker', function() {
  return {
      restrict: 'A',
      scope: {
          model: "=",
          format: "@",
          options: "=datepickerOptions",
          myid: "@"
      },
      templateUrl: 'datepicker-template.html',
      link: function(scope, element) {
          scope.popupOpen = false;
          scope.openPopup = function($event) {
              $event.preventDefault();
              $event.stopPropagation();
              scope.popupOpen = true;
          };

          scope.open = function($event) {
            $event.preventDefault();
            $event.stopPropagation();
            scope.opened = true;
          };

      }
  };
});

И ваше использование HTML будет:

<div my-datepicker model="container.two" 
                   datepicker-options="dateOptions" 
                   format="{{format}}"  
                   myid="myDP">
</div>

Изменить. Добавлен параметр id в качестве параметра для директивы. Plunker обновлен.

Plunker

Ответ 3

Я думаю, что ответ от @omri-aharon - лучший, но я хотел бы указать на некоторые улучшения, которые здесь не упоминались:

Обновлено Plunkr

Вы можете использовать конфигурацию для равномерного задания ваших параметров, таких как формат и текстовые параметры, следующим образом:

angular.module('ui.bootstrap.demo', ['ui.bootstrap'])
.config(function (datepickerConfig, datepickerPopupConfig) {
  datepickerConfig.formatYear='yy';
  datepickerConfig.startingDay = 1;
  datepickerConfig.showWeeks = false;
  datepickerPopupConfig.datepickerPopup = "shortDate";
  datepickerPopupConfig.currentText = "Heute";
  datepickerPopupConfig.clearText = "Löschen";
  datepickerPopupConfig.closeText = "Schließen";
});

Я считаю, что это проще и проще обновлять. Это также позволяет значительно упростить директиву, шаблон и разметку.

Пользовательская директива

angular.module('ui.bootstrap.demo').directive('myDatepicker', function() {
  return {
      restrict: 'E',
      scope: {
          model: "=",
          myid: "@"
      },
      templateUrl: 'datepicker-template.html',
      link: function(scope, element) {
          scope.popupOpen = false;
          scope.openPopup = function($event) {
              $event.preventDefault();
              $event.stopPropagation();
              scope.popupOpen = true;
          };

          scope.open = function($event) {
            $event.preventDefault();
            $event.stopPropagation();
            scope.opened = true;
          };

      }
  };
});

Шаблон

<div class="row">
    <div class="col-md-6">
        <p class="input-group">
          <input type="text" class="form-control" id="{{myid}}" datepicker-popup ng-model="model" is-open="opened" ng-required="true"  />
          <span class="input-group-btn">
            <button type="button" class="btn btn-default" ng-click="open($event)"><i class="glyphicon glyphicon-calendar"></i></button>
          </span>
        </p>
    </div>
</div> 

Как использовать его

<my-datepicker model="some.model" myid="someid"></my-datepicker>

Кроме того, если вы хотите принудительно использовать форматирование немецкого языка, вы можете добавить angular -locale_de.js. Это обеспечивает единообразие в использовании констант даты, например 'shortDate', и заставляет использовать немецкие имена месяца и дня.

Ответ 4

Вот мой патч обезьяны вашего плункера,

http://plnkr.co/edit/9Up2QeHTpPvey6jd4ntJ?p=preview

В основном, я сделал, чтобы изменить вашу модель, которая является датой, вернуть форматированную строку с помощью директивы

.directive('dateFormat', function (dateFilter) {
  return {
    require:'^ngModel',
    restrict:'A',
    link:function (scope, elm, attrs, ctrl) {
      ctrl.$parsers.unshift(function (viewValue) {
        viewValue.toString = function() {
          return dateFilter(this, attrs.dateFormat);
        };
        return viewValue;
      });
    }
  };
});

Вам нужно передать атрибут date-format для вашего тега input.

Если бы я был вами, я бы не пошел так далеко, чтобы составить сложную директиву. Я просто добавлю <datepicker> к вашему тегу input с той же ng-моделью, а также кнопками управления show/hide. Вы можете поэкспериментировать свой вариант, начиная с моего плункера

Ответ 5

Если создание директивы - это удобство добавления атрибутов, вы можете иметь 2 директивы на исходном вводе:

<input my-datepicker="" datepicker-popup="{{ format }}" type="text" ng-model="container.two" id="myDP" />

Затем избегайте множественных областей выделения, изменяя scope: true на scope: false в директиве myDatepicker.

Это работает, и я думаю, что предпочтительнее создать дополнительную директиву для изменения ввода даты в желаемый формат:

http://plnkr.co/edit/23QJ0tjPy4zN16Sa7svB?p=preview

Почему вы добавляете атрибут из директивы, эта проблема не имеет понятия, она почти такая же, как у вас есть два сборщика даты на одном и том же входе, один с вашим форматом и один со значением по умолчанию, который применяется после.

Ответ 6

Используйте moment.js с компонентом datepicker ui-bootstrap, чтобы создать директиву, чтобы предоставить полный набор шаблонов для форматов даты. Вы можете принять любой формат времени в пределах выделенной области.

Ответ 7

Если кто-либо заинтересован в реализации Typescript (на основе кода @jme11):

Директива

'use strict';

export class DatePickerDirective implements angular.IDirective {
    restrict = 'E';
    scope={
        model: "=",
        myid: "@"
    };
    template = require('../../templates/datepicker.tpl.html');

    link = function (scope, element) {
        scope.altInputFormats = ['M!/d!/yyyy', 'yyyy-M!-d!'];
        scope.popupOpen = false;
        scope.openPopup = function ($event) {
            $event.preventDefault();
            $event.stopPropagation();
            scope.popupOpen = true;
        };

        scope.open = function ($event) {
            $event.preventDefault();
            $event.stopPropagation();
            scope.opened = true;
        };
    };

    public static Factory() : angular.IDirectiveFactory {
        return () => new DatePickerDirective();
    }
}

angular.module('...').directive('datepicker', DatePickerDirective.Factory())

Шаблон:

<p class="input-group">
    <input type="text" class="form-control" id="{{myid}}"
           uib-datepicker-popup="MM/dd/yyyy" model-view-value="true"
           ng-model="model" ng-model-options="{ getterSetter: true, updateOn: 'blur' }"
           close-text="Close" alt-input-formats="altInputFormats"
           is-open="opened" ng-required="true"/><span class="input-group-btn"><button type="button" class="btn btn-default" ng-click="open($event)"><i
        class="glyphicon glyphicon-calendar"></i></button>
          </span>
</p>

Использование:

<datepicker model="vm.FinishDate" myid="txtFinishDate"></datepicker>

Ответ 8

Я попытался сделать эту работу (несколько взломать), что может быть не совсем то, что вы хотите, только некоторые грубые идеи. Поэтому вам все равно нужно немного подстроить его. Плункер:

`http://plnkr.co/edit/aNiL2wFz4S0WPti3w1VG?p=preview'

В принципе, я изменил область действия директивы, а также добавлю часы для области var container.two.