Bir ui-bootstrap datepicker için angularJs sarıcı yönergesi nasıl oluşturulur?

Bazı tarih alanlarını görüntülemek için ui.bootstrap.datepicker yönergesini kullanıyorum. Ancak çoğu zaman aynı kuruma ihtiyacım var: Bir açılır pencere ve bir açılır düğmeyle gelmesini istiyorum ve ayrıca metinler için Almanca isimler istiyorum. Bu, düğme ve metinler ve tekrar tekrar biçimlendirme için aynı kodu yaratır, bu yüzden kendimi tekrar etmemek için kendi yönergemi yazdım.

Here is a plunkr with my directive. However I seem to be doing it wrong. If you choose a date with the date picker using the "Date 1" datepicker that does not use my directive everything works fine. I'd expect the same for Date 2, but instead of displaying the date according to the template I supplied in the input field (or any other value I expected) it displays the .toString() representation of the date object (e.g. Fri Apr 03 2015 00:00:00 GMT+0200 (CEST)).

İşte benim direktifim:

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">' +
                '' +
                  '<button type="button" class="btn btn-default" ng-click="' + controllerName + '.openPopup($event)"></button>' +
                '' +
              '</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
  };
});

Ve bunu böyle kullanıyorum:

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

(Konsept bu cevap 'dan ilham aldı)

Açısal 1.3 kullanıyorum (buracağı 1.2'de açık çünkü bu formayı angular-ui- önyükleme datepicker belgeleri). Umarım bu bir fark yaratmaz.

Girişimdeki metinler neden yanlış? ve doğru şekilde nasıl yapılıyor?

Güncelleştirme

Bu arada biraz ilerleme kaydettim. Derleme ve bağlantıyla ilgili ayrıntılar hakkında daha fazla okuduktan sonra, bu plunkr 'da derleme işlevini kullanıyorum DOM manipülasyon yapmak için link işlevi. Hala doktorlardan bu alıntıyla biraz kafam karıştı:

Not: Şablon klonlanmışsa şablon örneği ve link örneği farklı nesneler olabilir. Bu nedenle, derleme işlevindeki tüm klonlanmış DOM düğümleri için geçerli olan DOM dönüşümlerinden başka bir şey yapmak güvenli değildir. Özellikle, DOM dinleyici kaydı derleme işlevinden ziyade bir bağlama işlevinde yapılmalıdır.

Özellikle "tüm klonlanmış DOM düğümleri için geçerli" ile ne anlama geldiğini merak ediyorum. Aslında bunun "DOM şablonunun tüm klonlarına uygulanan" anlamına geldiğini düşündüm, ancak durum böyle değil.

Her neyse: Yeni derleme sürümüm kromda gayet iyi çalışıyor. Firefox'ta önce bir tarih seçiciyi kullanarak bir tarih seçmem gerekiyor ve bundan sonra her şey yolunda gidiyor (null olarak tanımsız değiştirirsem Firefox'taki sorun çözüldü ( plunkr ) tarih seçicinin tarih ayrıştırıcısında). Yani bu da en son şey değil. Ayrıca, derleme sırasında yeniden adlandırdığım ng-model yerine ng-model2 kullanıyorum. Bunu yapmazsam her şey hala bozuk. Neden hala hiçbir fikrim yok.

50
@ jme11: Tekrar denedim. Ancak en azından Firefox'um (37.0.1) bu alanda geçici olarak geçersiz kılacak herhangi bir girişi reddediyor.
katma yazar yankee, kaynak
En son Plunkr ng-modelinizi kullanırsam tüm tarayıcılarımda benim için iyi sonuç verdiğini söylemek isterim: Safari, Chrome, Firefox. Değiştirdiğim tek şey ng-model2'yi ng-modeliyle değiştirmek ve ng-model2 için ayarlanmadıysa bitini yorumlamaktı. Tekrar test etmek isteyebilirsin.
katma yazar jme11, kaynak
Bu beni kesinlikle güldü! Dalgayı açıp ui-bootstrap-tpls-0.12.1.js satırının 1541 satırına bir kesme noktası koyarsanız ve özel yönergeli tarih belirleyiciden bölünmüş bir saniye için tarih seçin; hata ayıklamayı durdurduğunuzda toString sürümünün üzerine yazılır.
katma yazar TwitchBronBron, kaynak
UI seçicisiyle aynı tarihte ISO dizesi sorunuyla karşılaştım, tarih dizesini bir tarih nesnesine dönüştürmek için bir model alıcı ayarlayıcı kullanarak bu sorunu çözebildim.
katma yazar Chris Gunawardena, kaynak

8 cevap

Dürüst olmak gerekirse, girişte gösterilmeden önce neden kaynaklandığına ve tarihinizin "toString-ed" olmasına neden olduğundan emin değilim.

Bununla birlikte, direktifinizi yeniden yapılandıracak yerler buldum ve $ compile hizmeti, özelliklerdeki değişiklikler, kapsam devralma, zorunlu gibi gereksiz kodları kaldırdım. İzole kapsam kullandım, çünkü her yönerge kullanımının ana kapsamı bilmesi gerektiğini sanmıyorum, çünkü bu durum daha fazla böcek kullanımına neden olabilir. Bu benim değişen direktifim:

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

      }
  };
});

Ve HTML kullanımınız:

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

Edit: Added the id as a parameter to the directive. Plunker has been updated.

Plunker

17
katma
Bu çözümle ilgili sorun, direktifimde belirlediğim varsayılan değerlerin üzerine yazma yeteneğimi yitirmemdir. Ek olarak, id alanı, <input> öğesinde ayarlanmamış, ancak
katma yazar yankee, kaynak
@OmriAharon: Evet, çözümünüz işe yarayacak. Ancak yine de çözülemeyen şeyler: <input my-datepicker myid = "myId" /> Geçerlilik denetleyicilerine
katma yazar yankee, kaynak
@DaveAlperovich: Direktifimden orijinalin sağlamadığı ne istiyorum? 1. Bir dizi varsayılan değer, 2. açılır pencereyi açan bir düğmenin otomatik olarak eklenmesi. DRY ilkesini ihlal edecek şekilde, her yere kopyalayıp yapıştırmak zorunda kalacağım kod.
katma yazar yankee, kaynak
Kimliği myid niteliği olarak eklediniz. Bu sorunlara yol açar: Ya hiç bir kimlik belirtmezsem? Bu myid ve boş dize olur, değil mi? Ve eğer bunlardan ikisine sahipsem, bu kimlikleri benzersiz kılan bir ihlaldir. Dış dünya tarafından ayarlanmadıysa, myid özelliğini izleyerek ve rastgele bir değere ayarlayarak bunu çözebilirim. Ve bunu, geçersiz kılınabilir varsayılan değerlere sahip olmak istediğim tüm özelliklerle yapmam gerekiyor ?! Bir başka (daha az kritik) sorun, bir etiket olmayan bir kimliğe atıfta bulunursa, ancak IDE myid hakkında bir şey bilmiyorsa, genellikle IDE'm beni uyarıyor.
katma yazar yankee, kaynak
@ yankee, ben orijinal repo çatal ve ne istediğini için orijinal Direktif/Denetleyici değiştiririm. Şu anki olanı istediğiniz gibi davranmaya çalışmaktan daha az sorun.
katma yazar Dave Alperovich, kaynak
Omri'nin yaptığı, orijinal DatePicker Direktifini kendi Direktifi/Şablonuna sarmaktır, ancak iş atı hala bootstrap-UI-DatePicker'dir. Avantajın ne olduğundan emin değilim, ama Omri'nin savunmasında @yankee, direktifinden istediğin, orjinalinin sağlamadığı şey ...
katma yazar Dave Alperovich, kaynak
@ jme11 İyi fikir :)
katma yazar Omri Aharon, kaynak
@ jme11 Yalnızca bir şey olan datepicker HTML yönerge bildirimi, benzersiz bir soruna neden olan myid yerine id özniteliğini kullanıyor (ve yanlışlıkla?) ve @yankee - etiketin açılmasını istersiniz. popup Varsayım? Bu yüzden girişteki id 'a ve düğmenin üzerindeki {{id}} _ button ' a plnkr.co/edit/jevzBrCLeLiSNwe6KAk4?p=preview
katma yazar Omri Aharon, kaynak
@yankee etiket için çözümüm, 0 $ zaman aşımı etiketi HTML öğesi oluşturan bir dynamicLabel yönergesi olacak >, çalışması gerekiyordu. Bir yıldan fazla bir süredir Saf Açısal deneyimim oldu ve projem hakkında sıfırdan yarattığım birçok geliştiriciyi öğrettim ve sezgilerinin model 'in kod olduğunu bilmek için yeterince iyi olduğunu gördüm. > ng-model ve myId kimliği olacaktır. Onlara inanın :-) Ekstra özellikler konusunda hemfikirim, ancak size esneklik ile sağlam bir yönerge verecek, ihtiyaç duymazsanız hepsini geçmek zorunda değilsiniz.
katma yazar Omri Aharon, kaynak
@yankee Kimliği belirtmeniz gerekmez, yönergeyi kullanırken bunu yayabilirsiniz. Gerçekleşecek olan şey, girişin boş bir kimlik özelliğine sahip olacağı ve bu kadar, hiçbir şeyin cezası kalmayacak. Bunu kaldırmak istiyorsanız, eğer myid parametresi bir değere sahipse, id özniteliğini bağlantı işlevine ekleyebilirsiniz ve aksi halde hiçbir şey yapmadan ve girişi olmadan bırakın. kimliği hiç.
katma yazar Omri Aharon, kaynak
@yankee Angular 1.4 ve bu seçeneğe aşina olmadığım için yazdığınız gettSetter seçeneği hakkında biraz bilgi edinin. Bir parametre kullanarak ve/veya en kötü durumda - bunu saracak bir yönerge ile aynı şekilde yerleştirilebileceğinden eminim.
katma yazar Omri Aharon, kaynak
@ ABOS Hataya neyin neden olduğunu bilmek ilginç olacağını kabul ediyorum, ancak bence daha iyi bir yapı olduğu gibi, daha iyi bir yönerge yapısının kullanılabileceği gerçeğini değiştirmiyor. Ve hala cevaplar "ve doğru nasıl yapılır?" OP sorusunun bir parçası.
katma yazar Omri Aharon, kaynak
@ABOS Teşekkürler, bu çok daha iyi. Şablonda kalan sabit kodlanmış değişkendi. Şimdi düzeltildi.
katma yazar Omri Aharon, kaynak
@ jme11 Teşekkürler, önemli olan :-)
katma yazar Omri Aharon, kaynak
@yankee Güncellemeyi görebilirsiniz. Mesele şu ki, direktifiniz istediğiniz kadar esnek olabilir. Sonsuza dek parametreleştirebilirsiniz.
katma yazar Omri Aharon, kaynak
@yankee Bunların hepsi kolayca çözülebilir problemler. Yapmanız gereken tek şey, başka bir özellik eklemek ve yalıtılmış kapsamda elde etmektir. id için nasıl yapılacağını göstermek için bir saniye içinde güncelleme yapacağım.
katma yazar Omri Aharon, kaynak
@OmriAharon kimliği gösterdiğiniz için teşekkür ederiz. Daha önce bahsettiğim diğer kaygıların bir kısmını ele almak için kasten kimliğimi kimliğimle değiştirdim, ancak link işlevinden element.removeAttr ('id') eklemeyi unuttum. Şimdi güncelledim. OP için en uygun görünmese de, yaklaşımınızın en net olduğunu (ve sonuç olarak sürdürülebilirlik için en iyi olacağını düşünüyorum) düşünüyorum. Yine de, bu sitenin niteliği göz önüne alındığında, başkaları için bir ilham kaynağı olacağından emin olun (açıkça aldığınız oyların sayısına dayanıyor).
katma yazar jme11, kaynak
Bu konuyu takiben, etiket öğesinin yönergeye eklenmesi gerektiğini savunuyorum. Bu, kimliğin etiketteki for niteliğiyle eşleşme olasılığını ortadan kaldırır ve erişilebilirlik sağlar. Etiketi her zaman görüntülemek istemiyorsanız, Plunkr plnkr.co/edit adresimi güncellemek istemiyorum/NvdwXkPIBvLVzDadjEo0? P = önizleme , istendiğinde etiketi gizlemek için Boostrap .sr-only sınıfının nasıl kullanılacağını göstermek için ancak yine de ekran okuyucuları tarafından okunabilir durumda. Ayrıca, etiketin kendisi için varsayılan bir değer ekledim, böylece işaretlemeye eklenmesine gerek kalmadı.
katma yazar jme11, kaynak
Sorun değil. Bir diğer nokta, yaklaşımınız, açısal kullanıcı yönlendiricisinden standart bir yaklaşımdır, OP ise mevcut bir direktifin sargısını soruyor. Çiviniz bile çalışıyor, hala OP'nin orijinal sorusuna cevap vermiyor. IMHO, bu durumda ne olduğuna dair derin bir anlayış daha ilginç olurdu.
katma yazar ABOS, kaynak
"Seçilen tarih 2:" metni yalnızca hiçbir şey göstermiyor.
katma yazar ABOS, kaynak
Bu güncelleme hiç işe yaramadı.
katma yazar ABOS, kaynak

Bu 2 satırı direktif tanımınıza eklediğinizde direktifiniz çalışacaktır:

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

Bunun direktiflerin uygulanma sırası ile ilgisi vardır.

Yani kodunuzda

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

İki yönerge vardır: ngModel ve myDatepicker . Öncelikle ngModel'den önce kendi yönergenizi çalıştırabilirsiniz.

9
katma
Parlak! terminal tam olarak özlediğim şeydi :-). (Daha fazla test yapmam gerekiyor, ancak plunkr burada uyarladım ve iyi çalışıyor gibi görünüyor ).
katma yazar yankee, kaynak
Terminal özelliği, Angular’a stackoverflow.com/questions/15266840/… yönergesinin terminalinin nasıl anlaşılacağı
katma yazar jediz, kaynak

@ Omri-aharon'dan gelen cevabın en iyisi olduğunu düşünüyorum, ancak burada belirtilmeyen bazı gelişmelere dikkat çekmek istiyorum:

Güncellenen Plunkr

Format ve metin seçenekleri gibi seçeneklerinizi aşağıdaki gibi eşit şekilde ayarlamak için config komutunu kullanabilirsiniz:

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

Bunu daha net ve güncellemesi daha kolay buluyorum. Bu ayrıca yönergeyi, şablonu ve işaretlemeyi büyük ölçüde basitleştirmenize olanak sağlar.

Özel direktif

angular.module('ui.bootstrap.demo').directive('myDatepicker', function() {
  return {
      restrict: 'E',
      scope: {
          model: "=",
          myid: "@"
      },
      şablonUrl: 'datepicker-şablon.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;
          };

      }
  };
});

şablon

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

</div> </div>

Bu nasıl kullanılır


Ayrıca, bir Alman yerel ayarını kullanmak istiyorsanız, angular-locale_de.js dosyasını ekleyebilirsiniz. Bu, 'shortDate' gibi tarih sabitlerinin kullanımında eşitliği sağlar ve Alman ay ve gün adlarının kullanılmasını zorlar.

4
katma

İşte dalgacının maymun yaması.

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

Temelde yaptığım şey, bir tarih olan modelinizi değiştirmek için bir direktif kullanarak biçimlendirilmiş dizgiyi döndürmekti.

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

input etiketiniz için tarih biçimi niteliğini iletmeniz gerekir.

If I were you, I would not go that far to make a complex directive. I would simply add a appended to your input tag with the same ng-model, and control show/hide with a button. You may experiment your option starting from my plunker

2
katma
Senin plunkr çalışmıyor. Tarih yakalayıcısını kullanarak bir tarih seçebilirim, ancak metin girişi kullanamıyorum. Firefox, date2 alanındaki herhangi bir girişi kabul etmeyi reddedecek ve chrome, hemen bir tarih yazmaya başladığım anda derhal bir toString() tarih gösterimini dönüştürecek ve görüntüleyecektir.
katma yazar yankee, kaynak
vay, işte orada
katma yazar Dave Alperovich, kaynak

Tam olarak istediğiniz gibi olmayabilir, sadece bazı kaba fikirler, bu çalışmayı (biraz kesmek) yapmaya çalıştım. Bu yüzden hala biraz düzeltmen gerekiyor. Boğmaca:

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

Temel olarak, direktif kapsamını değiştirdim ve ayrıca kapsam var container.two için de saat ekledim.

1
katma
Evet, değişkenlerimi sürekli izleyerek dizgiye çevirebiliyordum. Ama aslında burada neler oluyor? Bu neden oluyor?
katma yazar yankee, kaynak

Bir kişi, siz bir Typcript uygulamasıyla ilgileniyorsa (gevşekçe @ jme11 koduna dayanarak):

Direktif:

'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())

Şablon:


<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"/><button type="button" class="btn btn-default" ng-click="open($event)"></button>

Kullanımı:


0
katma

Tarih saat biçimleri için kapsamlı bir desen seti sağlamak için yönergeyi oluşturmak üzere momenti ile ui-bootstrap datepicker bileşenini kullanın. İzole edilen kapsam içindeki herhangi bir zaman biçimini kabul edebilirsiniz.

0
katma

Yönergeyi oluşturmak, özellikleri eklemek kolaylık sağlıyorsa, orijinal girdi üzerinde 2 yönergeye sahip olabilirsiniz:

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

Ardından, myDatepicker yönergesinde kapsamı: true öğesini kapsamı: false olarak değiştirerek birden fazla ayırma kapsamından kaçının.

Bu işe yarar ve tarih girişini istenen formata değiştirmek için başka bir yönerge oluşturmanın tercih edilebilir olduğunu düşünüyorum:

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

Neden özniteliği yönergenin içinden ekliyorsunuz? Bu sorunun nedenini bilmiyorum, sanırım aynı girişte 2 tarih seçiciniz var, biri sizin biçiminiz, diğeri de varsayılanın ardından geçerli olacak.

0
katma