15

Я создал простой пользовательский AngularJs директиву для этой удивительной JQuery плагин jQuery-Select2 следующим образом:Angularjs Пользовательские Выбор2 директива

Директива

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

Использование в 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> 

Работает так, как ожидалось, и мой нормальный 012 Элементзаменен плагинами select2.

Однако есть одна проблема, хотя иногда она показывает значение по умолчанию i.e Select Country здесь, хотя в раскрывающемся списке правильное значение модели выбрано автоматически.

Теперь, если я увеличиваю $timeout интервал от 200 до некоторого высокого значения, скажем 1500, он работает, но задерживает рендеринг директивы. Также я думаю, что это не является правильным решением для него, так как мои данные загружаются через ajax.

Я также пытался обновить директиву следующим образом, но не повезло в том, что либо:

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: Я знаю, что есть модуль присутствует ui-select подобное, но она не требует какой-то иной разметки в виде <ui-select></ui-select> , и мое приложение уже полностью разработано, и я просто хочу заменить нормальный флажок select2.

Итак, можете ли вы, пожалуйста, направить меня, как я могу решить эту проблему и убедиться, что директива поддерживает синхронизацию с последним поведением?

+0

Это как-то связано с 'select2'? Если вы удалите директиву 'select2' и сделаете ее обычным элементом выбора, работает ли она так, как ожидалось? –

+0

Да, он работает как ожидалось, если я удалю так. –

+2

Я также использую 'select2' в своем приложении, но я использую [ui-select2] (https://github.com/angular-ui/ui-select2), который для него является оберткой Angular, которая теперь устарела , Select2 вызвал у меня много горя, я предлагаю вам избежать этого, если вы можете :) –

ответ

7

Это может быть проще, чем вы ожидали!

Пожалуйста, посмотрите на это Plunker

В принципе, все плагины, Angularjs $ часы должны быть основаны на чем-то. Я не уверен на 100% для jQuery-select2; но я думаю, что это обычные нормальные события DOM. (И в случае угловых $ watch это «грязная контрольная петля»)

Моя идея заключается в том, что давайте доверять jquery-Select2 и AngularJS для обработки этих событий изменения.

Нам просто нужно следить за изменение способов угловых и обновить выбор способов ВЫБ.2 в

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

//... 

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

Обратите внимание: я добавил в 2 новых часах, которые я думаю, что вы хотели бы иметь!

+0

С контролем, поскольку этот подход не работает, можете ли вы заставить его работать и включить его в свой ответ? см. [этот plunker] (http://plnkr.co/edit/MrAwXPQuQU6FcmyOvekH) изменения в выборе отражаются в переменной области видимости, но изменения в переменной области не отражаются в выборе – mohas

+0

. У вас есть ошибка опечатки в вашем Plunkr. Вы должны изменить ng-click кнопок, чтобы включить cc. слишком –

+1

Я бы настоятельно предложил использовать событие change.select2, а не «change». Я всегда получал бесконечные циклы изменений, пока не использовал [недокументированное событие «change.select2»] (https://github.com/select2/select2/issues/3620) – smajl

3

Я не знаком с Выбор2 (поэтому фактическим API для получения и установок отображаемого значения в элементе управления может быть неправильным), но я предлагаю это в качестве альтернативы:

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

Первого $ таймаут необходим, потому что вы используете ng-options, поэтому параметры не будут находиться в DOM до следующего цикла дайджеста. Проблема в том, что новые параметры не будут добавлены в элемент управления, если модель стран позже будет изменена вашим приложением.

1

Он не отвечает на ваш вопрос напрямую, но, пожалуйста, возьмите его, поскольку есть люди, которые хотят сделать другой подход, а не придерживаться jQuery select2.

Я построил свою собственную для этой цели, потому что я не был удовлетворен существующими, которые не точно следуют Угловым принципам, сначала HTML.

Это еще ранний этап, но я думаю, что все функции работают во всех современных браузерах.

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

Эти примеры

0

Я попытался воспроизвести вашу проблему, и, похоже, она работает хорошо. вот скрипку я придумал:

http://jsfiddle.net/s24gLdgq/

Вы можете иметь различное поведение в зависимости от версии Угловое и/или Выбор2 вы используете, не могли бы вы уточнить, что?

Также, если вы хотите предотвратить мерцание, обязательно скройте тег по умолчанию <select>, чтобы ничего не отображалось до того, как выйдет элемент select2.

Это также сделано в моем jsfiddle с CSS

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

Угловые не хотят иметь данные модели, измененные сторонним плагином. Мое предположение основано на том, что при использовании $ timeout существует условие гонки между Угловым обновлением опций или модели и плагином select2. Решение, которое я придумал, состоит в том, чтобы сделать обновление в основном из рук Углового и сделать это вручную из директивы, таким образом вы можете обеспечить, чтобы все соответствовало независимо от того, кто изменяет. Вот директива я придумал:

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

     } 
    }; 
}); 

Я добавил к атрибутам select-options и select-model. Они будут использоваться для заполнения и обновления данных с помощью интерфейса select2. Вот пример HTML:

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

Пожалуйста, обратите внимание, что есть еще некоторые очистки, что может быть сделано для директивы, и есть какие-то вещи в принимает на себя о входе, например, «в» в атрибуте Select-варианте. Он также не применяет атрибуты, а просто терпит неудачу, если они не существуют.

Также обратите внимание, что я использовал Select2 version 4, о чем свидетельствует el.val(i).trigger("change"). Возможно, вам придется вернуться к некоторым вещам при использовании более старой версии.

Адрес jsfiddle demo в действии.

Смежные вопросы