15

Stworzyłem dyrektywę prostego zwyczaj angularjs dla tej wspaniałej jquery plugin jQuery-Select2 następująco:angularjs klienta select2 dyrektywa

dyrektywa

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

Wykorzystanie szablonów 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> 

Działa zgodnie z oczekiwaniami, a mój normalny select element zostanie zastąpiony przez wtyczki select2.

Jednak istnieje jedna kwestia, czasami pokazuje ona wartość domyślną, np. Select Country tutaj, chociaż w menu rozwijanym wybrana jest właściwa wartość modelu.

Teraz, jeśli zwiększam przedział $timeout z 200 na jakąś dużą wartość, powiedzmy 1500, to działa, ale opóźnia renderowanie dyrektywy. Też myślę, że to nie jest właściwe rozwiązanie dla tego, ponieważ moje dane są ładowane przez ajax.

Próbowałem również zaktualizować dyrektywę w następujący sposób, ale nie ma szczęścia w tym zarówno:

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: Wiem, że nie jest podobny moduł obecny ui-select, ale wymaga trochę innego znaczników w postaci <ui-select></ui-select> , a moja aplikacja jest już w pełni rozwinięta i chcę tylko zastąpić zwykłe pole wyboru select2.

Czy możesz mi doradzić, w jaki sposób mogę rozwiązać ten problem i upewnić się, że dyrektywa jest zsynchronizowana z najnowszymi zachowaniami?

+0

Czy to ma coś wspólnego z 'select2'? Jeśli usuniesz dyrektywę 'select2' i sprawisz, że będzie ona normalnym elementem, czy działa zgodnie z oczekiwaniami? –

+0

Tak, działa zgodnie z oczekiwaniami, jeśli usunę. –

+2

Używam również 'select2' w mojej aplikacji, ale używam [ui-select2] (https://github.com/angular-ui/ui-select2), który jest dla niego opakowaniem Angular, który jest teraz przestarzały . Select2 przysporzył mi wiele smutku, sugeruję, żebyś go unikał, jeśli możesz :) –

Odpowiedz

7

To może być prostsze, niż się spodziewaliśmy!

Proszę spojrzeć na ten Plunker

Zasadniczo wszystkie wtyczki, angularjs $ zegarek muszą opierać się na czymś. Nie jestem w 100% pewny dla jQuery-select2; ale myślę, że to tylko normalne zdarzenia DOM sterowania. (A w przypadku zegarka Angular $ jest to "brudna pętla kontrolna")

Mój pomysł polega na zaufaniu jquery-Select2 i AngularJS do obsługi tych zmian.

Musimy tylko uważać na zmiany sposobów kanciasty i aktualizować select w sposób select2 za

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

//... 

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

Wskazówka: Dodałem w 2 nowego zegarka, który myślę, że chciałbyś mieć!

+0

Z kontrolą jako podejście to przyniesie efekt, czy uda ci się go uruchomić i uwzględnić w odpowiedzi? patrz [to plunker] (http://plnkr.co/edit/MrAwXPQuQU6FcmyOvekH) zmiany w selekcji są odzwierciedlone w zmiennej zasięgu, ale zmiany w zmiennej zakresu nie są odzwierciedlone w wyborze – mohas

+0

Masz błąd literowy w swoim Plunkr. Powinieneś zmienić ng-kliknięcie przycisków, aby dołączyć DW. zbyt –

+1

Zdecydowanie sugerowałbym użycie zdarzenia "change.select2" zamiast "zmiany". Zawsze miałem nieskończone pętle zmian, dopóki nie użyłem [nieudokumentowanego zdarzenia 'change.select2'] (https://github.com/select2/select2/issues/3620) – smajl

3

nie jestem zaznajomiony z select2 (tak rzeczywista API do pobierania i ustawiania wartości wyświetlanej w kontroli mogą być błędne), ale proponuję to jako alternatywę:

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

pierwsze $ timeout jest konieczny, ponieważ używasz opcji ng, więc opcje nie będą w DOM aż do następnego cyklu trawienia. Problem polega na tym, że nowe opcje nie zostaną dodane do formantu, jeśli model krajów zostanie później zmieniony przez aplikację.

1

To nie odpowiada bezpośrednio na twoje pytanie, ale proszę przyjąć, ponieważ jest kilka osób, które chcą zrobić inne podejście, zamiast trzymać się wybranego jQuery2.

Skonstruowałem własne dla tego celu, ponieważ nie byłem zadowolony z istniejących, które nie są dokładnie następujące zasady Angular, HTML-pierwszy.

To wciąż wczesny etap, ale myślę, że wszystkie funkcje działają we wszystkich nowoczesnych przeglądarkach.

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

Są to przykłady

0

Próbowałem odtworzyć Twój problem i wygląda na to, że działa dobrze. oto skrzypce wymyśliłem:

http://jsfiddle.net/s24gLdgq/

mogą mieć różne zachowania w zależności od wersji kątowej i/lub Wybór2 używasz, można określić, że?

Jeśli chcesz zapobiec migotaniu, ukryj domyślny znacznik <select>, aby nic nie było wyświetlane przed wyskakującym elementem select2.

Jest to również zrobić w moim jsfiddle z CSS

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

Angular nie spodoba się model danych zmodyfikowany przez wtyczkę innej firmy. Moje przypuszczenie oparte na tym, że używasz $ timeout jest tam wyścigowy stan pomiędzy Angular aktualizując opcje lub model i wtyczkę select2. Rozwiązaniem, które wymyśliłem, jest pobranie aktualizacji głównie z rąk Angulara i zrobienie tego ręcznie z dyrektywy, w ten sposób można zapewnić, że wszystko jest dopasowane bez względu na to, kto je modyfikuje. Oto dyrektywa wymyśliłem:

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

     } 
    }; 
}); 

Dodałem do atrybutów select-options i select-model. Zostaną one wykorzystane do zapełnienia i aktualizacji danych za pomocą interfejsu select2. Oto html próbki:

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

Należy pamiętać jeszcze trochę czyszczenia, które mogą być wykonane z dyrektywą i są jakieś rzeczy w zakłada o wejściu, takich jak „w” w atrybut select-Opcje. Nie wymusza również atrybutów, ale po prostu się nie powiedzie, jeśli nie istnieją.

Należy również pamiętać, że używałem wersji Select2 4, o czym świadczy numer el.val(i).trigger("change"). Być może będziesz musiał przywrócić niektóre rzeczy, jeśli używasz starszej wersji.

Oto jsfiddle demo dyrektywy w akcji.

Powiązane problemy