2013-12-18 18 views
13

Ho avuto l'idea di inserire gli input in direttive personalizzate per garantire un aspetto e un comportamento coerenti attraverso il mio sito. Voglio anche racchiudere il datapicker e il menu a discesa di ui di bootstrap. Inoltre, la direttiva dovrebbe gestire la convalida e visualizzare i suggerimenti.gli input di avvolgimento nelle direttive angolari

Il codice HTML dovrebbe essere simile a questo:

<my-input required max-length='5' model='text' placeholder='text' name='text'/> 

o

<my-datepicker required model='start' placeholder='start' name='start'/> 

nelle direttive Voglio creare una struttura DOM come:

<div> 
<div>..</div> //display validation in here 
<div>..</div> //add button to toggle datepicker (or other stuff) in here 
<div>..</div> //add input field in here 
</div> 

ho provato vari modi per raggiungere questo obiettivo, ma si è sempre imbattuto in alcuni compromessi:

  1. utilizzando transclude e sostituire inserire l'ingresso nella struttura direttive dom (in questo caso, la direttiva sarebbe limitato a 'A' non 'E' come nell'esempio precedente). Il problema qui è che non esiste un modo semplice per accedere all'elemento transclused in quanto voglio aggiungere attributi personalizzati in caso di datepicker. Potrei usare la funzione transclude e poi ricompilare il template nella funzione link, ma questo sembra un po 'complesso per questa attività. Ciò porta anche a problemi con l'ambito transclused e lo stato toggle per il datepicker (uno è nell'ambito delle direttive, l'altro nell'ambito transclused).

  2. utilizzando solo la sostituzione. In questo caso, tutti gli attributi vengono applicati al div più esterno (anche se si genera la struttura dom del modello nella funzione di compilazione). Se uso solo l'input come modello, allora gli attributi sono sull'input, ma ho bisogno di generare il template nella funzione link e quindi ricompilarlo. Per quanto comprendo il modello di fase di angolare, vorrei evitare di ricompilare e modificare il modello di dom nella funzione di collegamento (anche se ho visto molte persone farlo).

Attualmente sto lavorando con il secondo approccio e generando il modello nella funzione di collegamento, ma mi chiedevo se qualcuno avesse qualche idea migliore!

+0

Che tipo di attributi personalizzati si vuole essere in grado di aggiungere? Penso che un utile esempio dell'HTML in cui si vorrebbe rendere la direttiva (attributi personalizzati e tutti) sarebbe utile. –

+0

Nel caso del datepicker vorrei impostare i valori standard di applicazione ampi. Il tag di input HTML risultante dovrebbe essere simile a: ' ' – roemer

risposta

2

Perché non fare una direttiva del genere?

myApp.directive('wrapForm', function(){ 
    return { 
     restrict: 'AC', 
     link: function(scope, inputElement, attributes){      
      var overallWrap = angular.element('<div />'); 
      var validation = angular.element('<div />').appendTo(overallWrap); 
      var button = angular.element('<div />').appendTo(overallWrap); 
      var inputWrap = angular.element('<div />').appendTo(overallWrap); 

      overallWrap.insertBefore(inputElement); 
      inputElement.appendTo(inputWrap); 

      inputElement.on('keyup', function(){ 
       if (inputElement.val()) { 
        validation.text('Just empty fields are valid!'); 
       } else { 
        validation.text(''); 
       } 
      });    
     } 
    } 
}); 

Fiddle: http://jsfiddle.net/bZ6WL/

Fondamentalmente si prende il campo di inserimento originale (che è, tra l'altro,) e costruire gli involucri separatamente. In questo esempio ho semplicemente creato manualmente i DIV. Per cose più complesse, potresti anche usare un modello che ottiene $compile (d) da angularjs.

Il vantaggio utilizzando questo attributo di classe o html "wrapForm": è possibile utilizzare la stessa direttiva per diversi tipi di input di modulo.

+0

L'ho visto in fiddle ma questo non funziona per me, non so perché. Ho jquery-1.10.2 e angular.js nella mia applicazione e ancora questo non funziona !! –

+1

Attenzione - se lo usi con ng-se non cancella i wrapper! – jantimon

+0

Questo è un buon suggerimento, ma angolare non ha appendTo o insertBefore. Append e after (rispettivamente) possono essere usati invece per raggiungere un risultato simile. – Theo

8

Ecco quello che credo sia il modo corretto per farlo. Come l'OP, volevo essere in grado di utilizzare una direttiva di attributo su wrapper e input. Ma volevo anche che funzionasse con ng-if e tale senza perdite di elementi. Come ha sottolineato @jantimon, se non si puliscono gli elementi del wrapper essi si attarderanno dopo ng-se distrugge l'elemento originale.

app.directive("checkboxWrapper", [function() { 
    return { 
     restrict: "A", 
     link: function(scope, element, attrs, ctrl, transclude) { 
     var wrapper = angular.element('<div class="wrapper">This input is wrappered</div>'); 

     element.after(wrapper); 
     wrapper.prepend(element); 

     scope.$on("$destroy", function() { 
      wrapper.after(element); 
      wrapper.remove(); 
     }); 
     } 
    }; 
    } 
]); 

E here's a plunker si può giocare con.

IMPORTANTE: scope vs element $ destroy. È necessario effettuare la pulizia in scope.$on("$destroy") e non in element.on("$destroy") (che è ciò che stavo tentando in origine). Se lo fai nel secondo (elemento), allora un tag di commento "ngIf end" verrà filtrato. Ciò è dovuto al modo in cui Angular's ngIf esegue la pulizia del suo tag di commento finale quando fa la sua logica falsa. Mettendo il codice cleanup della direttiva all'interno dell'ambito $ destroy è possibile reinserire il DOM come lo era prima che l'input venisse inserito e quindi il codice cleanup di ng-if è felice. Quando viene chiamato element.on ("$ destroy"), è troppo tardi nel flusso ng-if falsey per scartare l'elemento originale senza causare una perdita di tag di commento.

+1

[Angular's jqLite ha 'wrap'] (https://docs.angularjs.org/api/ng/function/angular.element#angular-s-jqlite). Perché non usarlo vs 'dopo' e' anteporre'? – TheSharpieOne

+0

Provare 'wrap' mi ha causato qualche difficoltà. Sicuramente sarebbe una sintassi più pulita/più snella, ma per qualche motivo la pulizia non funziona quando si usa 'wrap'. Prova il 'wrap' nel Plunker, quindi attiva la casella Mostra (attiva la condizione ngIf). Ammetto che non l'ho approfondito ulteriormente, ma sono interessato ai dettagli se qualcuno vuole approfondire la questione. – BrandonLWhite

2

Perché non includere l'input nella funzione di compilazione? Il vantaggio è che non sarà necessario copiare gli attributi e non sarà necessario ripulire la funzione di distruzione dell'ambito. Si noti che è necessario rimuovere l'attributo direttiva per impedire l'esecuzione circolare.

(http://jsfiddle.net/oscott9/8er3fu0r/)

angular.module('directives').directive('wrappedWithDiv', [ 
    function() { 
     var definition = { 
      restrict: 'A', 
      compile: function(element, attrs) { 
       element.removeAttr("wrapped-with-div"); 
       element.replaceWith("<div style='border:2px solid blue'>" + 
        element[0].outerHTML + "</div>") 
      } 
     } 
     return definition; 
    } 
]); 
0

Sulla base di questo: http://angular-tips.com/blog/2014/03/transclusion-and-scopes/

Questa direttiva fa inclusione, ma la roba transclusa utilizza il campo di applicazione genitore, in modo da tutti gli attacchi funzionano come se il contenuto transclusa era nell'originale ambito in cui viene utilizzato il wrapper. Ciò include ovviamente il modello ng, anche min/max e altre direttive/attributi di validazione. Dovrebbe funzionare per qualsiasi contenuto. Non sto usando la direttiva ng-transclude perché sto clonando manualmente gli elementi e fornendo loro l'ambito genitore (controllore). "my-transclude" viene utilizzato al posto di ng-transclude per specificare dove inserire il contenuto escluso.

Peccato che ng-transclude non ha un'impostazione per controllare l'ambito. Ciò renderebbe inutile tutta questa goffaggine. e sembra che non risolvere il problema: https://github.com/angular/angular.js/issues/5489

controlsModule.directive('myWrapper', function() { 
     return { 
      restrict: 'E', 
      transclude: true, 
      scope: { 
       label: '@', 
       labelClass: '@', 
       hint: '@' 
      }, 

      link: link, 
      template: 
       '<div class="form-group" title="{{hint}}"> \ 
        <label class="{{labelClass}} control-label">{{label}}</label> \ 
        <my-transclude></my-transclude> \ 
       </div>' 
     }; 

     function link(scope, iElement, iAttrs, ctrl, transclude) { 

      transclude(scope.$parent, 
       function (clone, scope) { 

        iElement.find("my-transclude").replaceWith(clone); 

        scope.$on("$destroy", function() { 
         clone.remove(); 
        }); 
       }); 
     } 
    }); 
Problemi correlati