5

jquery -select2 ( ui-select ではない) と angular を使用して、値を ng-model に設定しようとしています。

と を使用してみまし$watchng-changeが、select2 で項目を選択した後、何も起動しないようです。

残念ながら、私は購入したテンプレートを使用しており、angular-ui を使用できません。

HTML:

<input type="hidden" class="form-control select2remote input-medium"
    ng-model="contact.person.id"
    value="{{ contact.person.id }}"
    data-display-value="{{ contact.person.name }}"
    data-remote-search-url="api_post_person_search"
    data-remote-load-url="api_get_person"
    ng-change="updatePerson(contact, contact.person)">

クライアントコントローラー:

$scope.updatePerson = function (contact, person) {
    console.log('ng change');
    console.log(contact);
    console.log(person);
} // not firing

$scope.$watch("client", function () {
    console.log($scope.client);
}, true); // not firing either

JQuery 統合:

var handleSelect2RemoteSelection = function () {
    if ($().select2) {
        var $elements = $('input[type=hidden].select2remote');
        $elements.each(function(){
            var $this = $(this);
            if ($this.data('remote-search-url') && $this.data('remote-load-url')) {
                $this.select2({
                    placeholder: "Select",
                    allowClear: true,
                    minimumInputLength: 1,
                    ajax: { // instead of writing the function to execute the request we use Select2's convenient helper
                        url: Routing.generate($this.data('remote-search-url'), {'_format': 'json'}),
                        type: 'post',
                        dataType: 'json',
                        delay: 250,
                        data: function (term, page) {
                            return {
                                query: term, // search term
                            };
                        },
                        results: function (data, page) { // parse the results into the format expected by Select2.
                            return {
                                results: $.map(data, function (datum) {
                                    var result = {
                                        'id': datum.id,
                                        'text': datum.name
                                    };
                                    for (var prop in datum) {
                                        if (datum.hasOwnProperty(prop)) {
                                            result['data-' + prop] = datum[prop];
                                        }
                                    }
                                    return result;
                                })
                            }
                        }
                    },
                    initSelection: function (element, callback) {
                        // the input tag has a value attribute preloaded that points to a preselected movie's id
                        // this function resolves that id attribute to an object that select2 can render
                        // using its formatResult renderer - that way the movie name is shown preselected
                        var id = $(element).val(),
                            displayValue = $(element).data('display-value');
                        if (id && id !== "") {
                            if (displayValue && displayValue !== "") {
                                callback({'id': $(element).val(), 'text': $(element).data('display-value')});
                            } else {
                                $.ajax(Routing.generate($this.data('remote-load-url'), {'id': id, '_format': 'json'}), {
                                    dataType: "json"
                                }).done(function (data) {
                                    callback({'id': data.id, 'text': data.name});
                                });
                            }
                        }
                    },
                });
            }
        });
    }
};

どんなアドバイスでも大歓迎です!:)

アップデート

同様に問題を再現しているように見えるプランクをまとめることができました.ng -watchと$watchイベントは、最初に値を変更したときにのみ発生するように見えます。それにもかかわらず、私のコードでは (そして、コレクションに動的に追加および削除するなどの複雑さをさらに追加する場合)、一度も起動していないようです。

繰り返しになりますが、正しい方向 (または実際には任意の方向) へのポインタをいただければ幸いです。

4

1 に答える 1

7

あなたの例には多くの問題があります。「答え」を提供できるかどうかはわかりませんが、次の提案と説明が役立つことを願っています.

まず、jQuery と Angular を「混合」しています。一般に、これは実際には機能しません。例えば:

script.js で実行します。

$(document).ready(function() {
  var $elements = $('input[type=hidden].select2remote');
  $elements.each(function() {
     //...
  });
});

このコードは、DOM が最初に準備できたときに 1 回実行されます。現在DOMにあり、select2プラグインを初期化したselect2remoteクラスを持つ非表示の入力要素を選択します。

問題は、この関数の実行後に追加された新しい input[type=hidden].select2remote 要素がまったく初期化されないことです。これは、たとえば、データを非同期にロードして ng-repeat を設定している場合に発生します。

修正は、select2 初期化コードをディレクティブに移動し、このディレクティブを各入力要素に配置することです。要約すると、このディレクティブは次のようになります。

.directive('select2', function() {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function(scope, element, attr, ngModel) {

            //$this becomes element

            element.select2({
                //options removed for clarity
            });

            element.on('change', function() {
                 console.log('on change event');
                 var val = $(this).value;
                 scope.$apply(function(){
                     //will cause the ng-model to be updated.
                     ngModel.setViewValue(val);
                 });
            });
            ngModel.$render = function() {
                 //if this is called, the model was changed outside of select, and we need to set the value
                //not sure what the select2 api is, but something like:
                element.value = ngModel.$viewValue;
            }

        }
    }
});

コントロールの現在の値を取得および設定するための API を知るには、select2 に精通していないことをお詫びします。コメントでそれを提供していただければ、例を変更できます。

マークアップは次のように変更されます。

<input select2 type="hidden" class="form-control select2remote input-medium"
    ng-model="contact.person.id"
    value="{{ contact.person.id }}"
    data-display-value="{{ contact.person.name }}"
    data-remote-search-url="api_post_person_search"
    data-remote-load-url="api_get_person"
    ng-change="updatePerson(contact, contact.person)">

このディレクティブを実装した後、script.js 全体を削除できます。

コントローラーには次のものがあります。

$('.select2remote').on('change', function () {
  console.log('change');
  var value = $(this).value;
  $scope.$apply(function () {
      $scope.contact.person.id = value;
  });
});

ここには 2 つの問題があります。

まず、コントローラーで jQuery を使用していますが、これは本当にすべきではありません。次に、このコード行は、コントローラがインスタンス化されたときにDOM にあったアプリケーション全体
の select2remote クラスを持つすべての要素で変更イベントを発生させます。

コントローラーがインスタンス化された後(次のダイジェスト サイクルで) DOM に追加されるため、Angular によって (つまり、ng-repeat を介して) 追加された要素には変更リスナーが登録されない可能性があります。

また、変更イベントを持つコントローラーのスコープ外の要素は、コントローラーの $scope の状態を変更します。これに対する解決策は、この機能をディレクティブに移動し、ng-model 機能に依存することです。

Angular のコンテキストを離れるとき (つまり、jQuery の $.ajax 機能を使用している場合) はいつでも、scope.$apply() を使用して Angular の実行コンテキストに再入する必要があることに注意してください。

これらの提案がお役に立てば幸いです。

于 2015-04-27T23:45:36.673 に答える