Şablonu bir Direktif içinde özelleştirme


98

Aşağıdaki gibi Bootstrap'ten işaretlemeyi kullanan bir formum var:

<form class="form-horizontal">
  <fieldset>
    <legend>Legend text</legend>
    <div class="control-group">
      <label class="control-label" for="nameInput">Name</label>
      <div class="controls">
        <input type="text" class="input-xlarge" id="nameInput">
        <p class="help-block">Supporting help text</p>
      </div>
    </div>
  </fieldset>
</form>

Burada, aşağıdaki gibi yeni bir yönergeye (form-input) indirgemek istediğim çok sayıda standart kod var:

<form-input label="Name" form-id="nameInput"></form-input>

üretir:

   <div class="control-group">
      <label class="control-label" for="nameInput">Name</label>
      <div class="controls">
        <input type="text" class="input-xlarge" id="nameInput">
      </div>
    </div>

Basit bir şablonla bu kadar çalışıyorum.

angular.module('formComponents', [])
    .directive('formInput', function() {
        return {
            restrict: 'E',
            scope: {
                label: 'bind',
                formId: 'bind'
            },
            template:   '<div class="control-group">' +
                            '<label class="control-label" for="{{formId}}">{{label}}</label>' +
                            '<div class="controls">' +
                                '<input type="text" class="input-xlarge" id="{{formId}}" name="{{formId}}">' +
                            '</div>' +
                        '</div>'

        }
    })

Ancak, daha gelişmiş işlevler eklemeye başladığımda sıkışıp kalıyorum.

Şablondaki varsayılan değerleri nasıl destekleyebilirim?

Yönergemde "tür" parametresini isteğe bağlı bir öznitelik olarak göstermek istiyorum, örneğin:

<form-input label="Password" form-id="password" type="password"/></form-input>
<form-input label="Email address" form-id="emailAddress" type="email" /></form-input>

Ancak, hiçbir şey belirtilmezse, varsayılan olarak ayarlamak isterim "text". Bunu nasıl destekleyebilirim?

Özniteliklerin varlığına / yokluğuna göre şablonu nasıl özelleştirebilirim?

Ayrıca varsa "gerekli" özniteliği de destekleyebilmeyi isterim. Örneğin:

<form-input label="Email address" form-id="emailAddress" type="email" required/></form-input>

Eğer requiredyönergede mevcuttur, ben oluşturulan eklemek istediğiniz <input />çıktı ve başka türlü bunu görmezden. Bunu nasıl başaracağımdan emin değilim.

Bu gereksinimlerin basit bir şablonun ötesine geçmiş olabileceğinden ve ön derleme aşamalarını kullanmaya başlamam gerektiğinden şüpheleniyorum, ancak nereden başlayacağımı bilmiyorum.


Odada fili gören tek kişi ben miyim :) -> typeBağlama yoluyla dinamik olarak ayarlanırsa örneğin. type="{{ $ctrl.myForm.myField.type}}"? Aşağıdaki tüm yöntemleri kontrol ettim ve bu senaryoda işe yarayacak herhangi bir çözüm bulamadım. Görünüşe göre şablon işlevi özniteliklerin değişmez değerlerini görecek , ör. tAttr['type'] == '{{ $ctrl.myForm.myField.type }}'yerine tAttr['type'] == 'password'. Kafam karıştı.
Dimitry K

Yanıtlar:


211
angular.module('formComponents', [])
  .directive('formInput', function() {
    return {
        restrict: 'E',
        compile: function(element, attrs) {
            var type = attrs.type || 'text';
            var required = attrs.hasOwnProperty('required') ? "required='required'" : "";
            var htmlText = '<div class="control-group">' +
                '<label class="control-label" for="' + attrs.formId + '">' + attrs.label + '</label>' +
                    '<div class="controls">' +
                    '<input type="' + type + '" class="input-xlarge" id="' + attrs.formId + '" name="' + attrs.formId + '" ' + required + '>' +
                    '</div>' +
                '</div>';
            element.replaceWith(htmlText);
        }
    };
})

6
Bu biraz geç, ancak htmlTextbir ng-clickyere bir yer eklerseniz , tek değişiklik ile değiştirmek element.replaceWith(htmlText)olur element.replaceWith($compile(htmlText))mu?
jclancy

@ Misko, kapsamdan kurtulmaktan bahsetmiştin. Neden? İzole kapsam ile kullanıldığında derlenmeyen bir yönergem var.
Syam

1
htmlTextbir ng-transclude yönergesi içeriyorsa bu işe yaramaz
Alp

3
Ne yazık ki, form doğrulamanın bununla çalışmadığını buldum $error, eklenen girdideki bayraklar asla ayarlanmıyor. Bunu bir yönergenin link özelliği içinde yapmak zorundaydım: $compile(htmlText)(scope,function(_el){ element.replaceWith(_el); });formun denetleyicisinin yeni oluşan varoluşunu tanıması ve onu doğrulamaya dahil etmesi için. Bir yönergenin derleme özelliğinde çalışmasını sağlayamadım.
meconroy

5
Tamam, orada 2015 yılı ve el ile komut dosyalarında işaretleme oluşturmada çok yanlış bir şeyler olduğundan eminim .
BorisOkunskiy

38

Misko tarafından önerilen çözümü kullanmaya çalıştım, ancak benim durumumda html şablonumla birleştirilmesi gereken bazı özniteliklerin kendileri de yönergelerdi.

Ne yazık ki, sonuçta ortaya çıkan şablon tarafından atıfta bulunulan tüm yönergeler doğru çalışmadı. Açısal koda dalmak ve temel nedeni bulmak için yeterli zamanım olmadı, ancak potansiyel olarak yardımcı olabilecek bir çözüm buldum.

Çözüm, şablon html'yi oluşturan kodu derlemeden şablon işlevine taşımaktı. Yukarıdaki koda dayalı örnek:

    angular.module('formComponents', [])
  .directive('formInput', function() {
    return {
        restrict: 'E',
        template: function(element, attrs) {
           var type = attrs.type || 'text';
            var required = attrs.hasOwnProperty('required') ? "required='required'" : "";
            var htmlText = '<div class="control-group">' +
                '<label class="control-label" for="' + attrs.formId + '">' + attrs.label + '</label>' +
                    '<div class="controls">' +
                    '<input type="' + type + '" class="input-xlarge" id="' + attrs.formId + '" name="' + attrs.formId + '" ' + required + '>' +
                    '</div>' +
                '</div>';
             return htmlText;
        }
        compile: function(element, attrs)
        {
           //do whatever else is necessary
        }
    }
})

Bu, şablona gömülü bir ng-tıklama ile sorunumu çözdü
joshcomley

Teşekkürler, bu benim için de çalıştı. Bazı varsayılan öznitelikleri uygulamak için bir yönergeyi kaydırmak istedi.
martinoss

2
Teşekkürler, şablonun bir işlevi kabul ettiğinin farkında bile değildim!
Jon Snow

2
Bu geçici bir çözüm değildir. OP'ye doğru cevap bu. Öğenin özelliklerine bağlı olarak koşullu olarak bir şablon oluşturmak, bir yönerge / bileşen şablon işlevinin tam amacıdır. Bunun için derlemeyi kullanmamalısınız. Angular ekibi, bu tarz kodlamayı (derleme işlevini kullanmadan) büyük ölçüde teşvik ediyor.
jose.angel.jimenez

Bu doğru cevap olmalı, şablonun işlev aldığından haberdar
NeverGiveUp161

5

Yukarıdaki cevaplar maalesef pek işe yaramıyor. Özellikle, derleme aşamasının kapsama erişimi yoktur, bu nedenle alanı dinamik özniteliklere göre özelleştiremezsiniz. Bağlama aşamasını kullanmak (eşzamansız olarak dom oluşturma vb. Açısından) en fazla esnekliği sunuyor gibi görünüyor. Aşağıdaki yaklaşım şunları ele alır:

<!-- Usage: -->
<form>
  <form-field ng-model="formModel[field.attr]" field="field" ng-repeat="field in fields">
</form>
// directive
angular.module('app')
.directive('formField', function($compile, $parse) {
  return { 
    restrict: 'E', 
    compile: function(element, attrs) {
      var fieldGetter = $parse(attrs.field);

      return function (scope, element, attrs) {
        var template, field, id;
        field = fieldGetter(scope);
        template = '..your dom structure here...'
        element.replaceWith($compile(template)(scope));
      }
    }
  }
})

Oluşturduğum bir özü daha tam kod ve bir ile writeup yaklaşımının.


güzel yaklaşım. maalesef ngTransclude ile kullanırken şu hatayı alıyorum:Error: [ngTransclude:orphan] Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found.
Alp

ve neden 'field: "="' ile izole bir kapsam kullanılmıyor?
IttayD

Çok hoş teşekkürler! Maalesef yazılı yaklaşımınız çevrimdışı :(
Michiel

Hem öz hem de yazma ölü bağlantılar.
binki

4

İşte kullanmaya son verdiğim şey.

AngularJS'de çok yeniyim, bu yüzden daha iyi / alternatif çözümler görmeyi çok isterim.

angular.module('formComponents', [])
    .directive('formInput', function() {
        return {
            restrict: 'E',
            scope: {},
            link: function(scope, element, attrs)
            {
                var type = attrs.type || 'text';
                var required = attrs.hasOwnProperty('required') ? "required='required'" : "";
                var htmlText = '<div class="control-group">' +
                    '<label class="control-label" for="' + attrs.formId + '">' + attrs.label + '</label>' +
                        '<div class="controls">' +
                        '<input type="' + type + '" class="input-xlarge" id="' + attrs.formId + '" name="' + attrs.formId + '" ' + required + '>' +
                        '</div>' +
                    '</div>';
                element.html(htmlText);
            }
        }
    })

Örnek kullanım:

<form-input label="Application Name" form-id="appName" required/></form-input>
<form-input type="email" label="Email address" form-id="emailAddress" required/></form-input>
<form-input type="password" label="Password" form-id="password" /></form-input>

10
Daha iyi bir çözüm şudur: (1) bağlama işlevi yerine bir derleme işlevi kullanmak ve değiştirmeyi orada yapmak. Şablonu özelleştirmek istediğiniz için sizin durumunuzda çalışmayacaktır. (2) kapsamdan kurtulun:
Misko Hevery 18'12

@MiskoHevery Geri bildiriminiz için teşekkürler - burada bir bağlantı işlevi için neden bir derleme işlevinin tercih edildiğini açıklar mısınız?
Marty Pitt

4
Yanıtın bu olduğunu düşünüyorum, docs.angularjs.org/guide/directive : "Yönerge örnekleri arasında paylaşılabilen herhangi bir işlem [ör. Şablon DOM'u dönüştürmek], performans nedenleriyle derleme işlevine taşınmalıdır."
Mark Rajcok

@Marty Özel girdilerinizden birini hala bir modele bağlayabiliyor musunuz? (ör. <form-input ng-model="appName" label="Application Name" form-id="appName" required/></form-input>)
Jonathan Wilson

1
@MartyPitt O'Reilly'nin "AngularJS" kitabından: "Öyleyse compile, şablonu dönüştürmeyle ilgilenen linkaşamaya ve görünümdeki verileri değiştirmeyle ilgilenen aşamaya sahibiz . Bu satırlar boyunca, compileve linkyönergelerdeki compileişlevler , işlevlerin şablonun kendisini dönüştürmesiyle ilgilenmesidir ve linkişlevler model ile görünüm arasında dinamik bir bağlantı kurar. Bu ikinci aşamada kapsamlar derlenen linkişlevlere eklenir ve yönerge, veri bağlama yoluyla canlı hale gelir. "
Julian
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.