15

Ho creato direttiva semplice personalizzato AngularJs per questo fantastico plugin per jQuery jQuery-Select2 come segue:Angularjs personalizzato select2 direttiva

direttiva

app.directive("select2",function($timeout,$parse){ 
    return { 
     restrict: 'AC', 
     link: function(scope, element, attrs) { 
      $timeout(function() { 
       $(element).select2(); 
      },200); 
     } 
    }; 
}); 

uso in modelli HTML:

<select class="form-control" select2 name="country" 
data-ng-model="client.primary_address.country" 
ng-options="c.name as c.name for c in client.countries"> 
    <option value="">Select Country</option> 
</select> 

Sta funzionando come previsto e il mio normale 012 L'elementoè sostituito dai plug-in select2.

Tuttavia c'è un problema però, a volte sta mostrando il valore predefinito cioè Select Country qui anche se in discesa valore del modello corretto viene automaticamente selezionato.

Ora se aumento l'intervallo da 200 ad alto valore, ad esempio 1500, funziona ma ritarda il rendering della direttiva. Inoltre, ritengo che non sia una soluzione adeguata, dato che i miei dati vengono caricati tramite ajax.

Ho anche cercato di aggiornare la direttiva come segue, ma fortuna dal fatto che uno:

app.directive("select2",function($timeout,$parse){ 
    return { 
     restrict: 'AC', 
     require: 'ngModel', 
     link: function(scope, element, attrs) { 
      var modelAccessor = $parse(attrs.ngModel); 
      $timeout(function() { 
       $(element).select2(); 
      }); 
      scope.$watch(modelAccessor, function (val) { 
       if(val) { 
        $(element).select2("val",val); 
       } 
      }); 
     } 
    }; 
}); 

PS: So che c'è modulo simile presente ui-select, ma richiede una certa marcatura diversi in forma di <ui-select></ui-select> e la mia app è già completamente sviluppata e voglio solo sostituire la normale casella di selezione con select2.

Quindi, per favore, puoi indicarmi come risolvere questo problema e assicurarmi che la direttiva sia in sincronia con il comportamento più recente?

+0

Questo ha a che fare con 'select2'? Se rimuovi la direttiva 'select2' e la rendi un normale elemento select, funziona come previsto? –

+0

Sì, funziona come previsto se rimuovo così. –

+2

Sto anche usando 'select2' nella mia applicazione, ma sto usando [ui-select2] (https://github.com/angular-ui/ui-select2) che è il wrapper di Angular per questo, che ora è deprecato . Select2 mi ha causato un sacco di dolore btw, ti suggerisco di evitarlo se puoi :) –

risposta

7

Potrebbe essere più semplice del previsto!

Si prega di dare un'occhiata a questo Plunker

In sostanza, tutti i plugin, Angularjs $ orologio devono basarsi su qualcosa. Non sono sicuro al 100% per jQuery-select2; ma penso che siano solo gli eventi DOM normali del controllo. (E nel caso di Angular $ watch, si tratta di un "ciclo di controllo sporco")

La mia idea è di fidarsi di jquery-Select2 e AngularJS per la gestione di tali eventi di modifica.

abbiamo solo bisogno di guardare per il cambiamento nelle vie di angolari e aggiornare il prescelto nelle vie di Select2

var refreshSelect = function() { 
    if (!element.select2Initialized) return; 
    $timeout(function() { 
     element.trigger('change'); 
    }); 
}; 

//... 

scope.$watch(attrs.ngModel, refreshSelect); 

Avviso: ho aggiunto in 2 nuovo orologio che credo si vorrebbe avere!

+0

Con il controllo come approccio questo non funzionerà, puoi farlo funzionare e includerlo nella tua risposta? vedi [questo plunker] (http://plnkr.co/edit/MrAwXPQuQU6FcmyOvekH) le modifiche in select si riflettono nella variabile scope, ma le modifiche nella variabile scope non si riflettono in select – mohas

+0

Hai un errore di battitura nel tuo Plunkr. È necessario modificare il ng-clic dei pulsanti per includere cc. anche –

+1

Suggerisco caldamente di utilizzare l'evento 'change.select2' piuttosto che 'cambiare'. Ho sempre avuto infiniti cicli di eventi di cambiamento fino a quando non ho usato [evento non documentato 'change.select2'] (https://github.com/select2/select2/issues/3620) – smajl

3

Io non sono che la familiarità con select2 (in modo che l'API effettivo per ottenere e impostare il valore visualizzato nel controllo potrebbe non essere corretto), ma vi suggerisco questo come alternativa:

app.directive("select2",function($timeout){ 
    return { 
     restrict: 'AC', 
     require: 'ngModel', 
     link: function(scope, element, attrs, model) { 

      $timeout(function() { 
       element.select2(); 
      }); 

      model.$render = function() { 
       element.select2("val",model.$viewValue); 
      } 
      element.on('change', function() { 
       scope.$apply(function() { 
        model.$setViewValue(element.select2("val")); 
       }); 
      }) 
     } 
    }; 
}); 

Il primo $ il timeout è necessario perché stai usando ng-options, quindi le opzioni non saranno nel DOM fino al prossimo ciclo di digest. Il problema con questo è che le nuove opzioni non verranno aggiunte al controllo se il modello Paesi viene successivamente modificato dall'applicazione.

1

Non risponde direttamente alla tua domanda, ma per favore prendilo perché ci sono alcune persone là fuori che vogliono fare un altro approccio piuttosto che attenersi a jQuery select2.

Ho costruito il mio per questo scopo perché non ero soddisfatto di quelli esistenti che non seguono esattamente i principi angolari, prima HTML.

È ancora in fase iniziale, ma penso che tutte le funzionalità funzionino in tutti i browser moderni.

https://github.com/allenhwkim/angular-autocomplete

Questi sono esempi

0

Ho provato a riprodurre il problema e sembra funzionare bene. qui è il violino mi si avvicinò con:

http://jsfiddle.net/s24gLdgq/

Si può avere un comportamento diverso a seconda della versione di angolare e/o Select2 che si sta utilizzando, si potrebbe specificare che?

Inoltre, se si desidera evitare lo sfarfallio, assicurarsi di nascondere il tag <select> predefinito in modo che non venga visualizzato nulla prima che l'elemento select2 venga espulso.

Questo avviene anche nel mio jsfiddle con il CSS

.form-control { width: 200px; opacity: 0 } 
3

Angolare non ha intenzione di modificare i dati del modello da un plug-in di terze parti. La mia ipotesi basata sul fatto che si utilizza $ timeout è che c'è una condizione di competizione tra l'aggiornamento angolare delle opzioni o il modello e il plugin select2. La soluzione che ho trovato è di prendere l'aggiornamento per lo più dalle mani di Angular e farlo manualmente dalla direttiva, in questo modo è possibile garantire che tutto corrisponda a chiunque stia modificando. Ecco la direttiva mi si avvicinò con:

app.directive("select2",function($timeout,$parse){ 
    return { 
     restrict: 'AC', 
     link: function(scope, element, attrs) { 
      var options = [], 
       el = $(element), 
       angularTriggeredChange = false, 
       selectOptions = attrs["selectOptions"].split(" in "), 
       property = selectOptions[0], 
       optionsObject = selectOptions[1]; 
      // watch for changes to the defining data model 
      scope.$watch(optionsObject, function(n, o){ 
       var data = []; 
       // format the options for select2 data interface 
       for(var i in n) { 
        var obj = {id: i, text: n[i][property]}; 
        data.push(obj); 
       } 
       el.select2({data: data}); 
       // keep local copy of given options 
       options = n; 
      }, true); 
      // watch for changes to the selection data model 
      scope.$watch(attrs["selectSelection"], function(n, o) { 
       // select2 is indexed by the array position, 
       // so we iterate to find the right index 
       for(var i in options) { 
        if(options[i][property] === n) { 
         angularTriggeredChange = true; 
         el.val(i).trigger("change"); 
        } 
       } 
      }, true); 
      // Watch for changes to the select UI 
      el.select2().on("change", function(e){ 
       // if the user triggered the change, let angular know 
       if(!angularTriggeredChange) { 
        scope.$eval(attrs["selectSelection"]+"='"+options[e.target.value][property]+"'"); 
        scope.$digest(); 
       } 
       // if angular triggered the change, then nothing to update 
       angularTriggeredChange = false; 
      }); 

     } 
    }; 
}); 

ho aggiunto agli attributi select-options e select-model. Questi verranno utilizzati per popolare e aggiornare i dati utilizzando l'interfaccia di select2. Ecco un HTML di esempio:

<select id="sel" class="form-control" select2 name="country" 
    select-selection="client.primary_address.country" 
    select-options="name in client.countries" > 
    <option value="">Select Country</option> 
</select> 
<div>Selected: {{client.primary_address.country}}</div> 

Si prega di notare c'è ancora un po di pulizia che potrebbe essere fatto alla direttiva e ci sono tutte le cose si assume circa l'ingresso, come ad esempio la "in" in attributo SELECT-opzioni. Inoltre non applica gli attributi ma fallisce solo se non esistono.

Si noti inoltre che ho utilizzato Select2 versione 4, come evidenziato dallo el.val(i).trigger("change"). Potrebbe essere necessario ripristinare alcune cose se si utilizza una versione precedente.

Questo è il jsfiddle demo della direttiva in azione.