AngularJS + JQuery:動的コンテンツをangularjsで機能させる方法


84

私はjQueryとAngularJSの両方を使用してAjaxアプリに取り組んでいます。

jQueryを使用してdivのコンテンツ(AngularJSバインディングを含む)を更新すると html関数すると、AngularJSバインディングが機能しません。

以下は私がやろうとしていることのコードです:

$(document).ready(function() {
  $("#refreshButton").click(function() {
    $("#dynamicContent").html("<button ng-click='count = count + 1' ng-init='count=0'>Increment</button><span>count: {{count}} </span>")
  });
});
</style><script src="http://docs.angularjs.org/angular-1.0.1.min.js"></script><style>.ng-invalid {
  border: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="">
  <div id='dynamicContent'>
    <button ng-click="count = count + 1" ng-init="count=0">
        Increment
      </button>
    <span>count: {{count}} </span>
  </div>


  <button id='refreshButton'>
    Refresh
  </button>
</div>

IDを持つdiv内に動的コンテンツがあります #dynamicContent更新がクリックされたときにこのdivのコンテンツを更新する更新ボタンがあります。コンテンツを更新しない場合、インクリメントは期待どおりに機能しますが、更新すると、AngularJSバインディングが機能しなくなります。

これはAngularJSでは有効ではないかもしれませんが、最初はjQueryでアプリケーションを構築し、後でAngularJSを使い始めたため、すべてをAngularJSに移行することはできません。AngularJSでこれを機能させるための助けをいただければ幸いです。


1
この機能にJQueryを使用する特別な理由はありますか?これはAngularでうまく簡単にカバーされるため:< jsfiddle.net/pkozlowski_opensource/YCrFD/2 >
pkozlowski.opensource 2012

3
これは、問題を示すための私の実際のユースケースの単純化されたバージョンです。実際のアプリケーションでは、動的コンテンツは、htmlとしてjqueryに渡されるgrailstaglibによって生成されます。そのため、grailstaglibのすべてのロジックをangularjsに移植して純粋なangularjsにすることはできません。
ラジャ

1
@ pkozlowski.opensource、そのリンクは死んでいますか?また
リアム

回答:


115

$compileDOMに挿入する前にHTML文字列を呼び出す必要があります。これにより、angularがバインディングを実行する機会が得られます。

あなたのフィドルでは、それはこのように見えるでしょう。

$("#dynamicContent").html(
  $compile(
    "<button ng-click='count = count + 1' ng-init='count=0'>Increment</button><span>count: {{count}} </span>"
  )(scope)
);

明らかに、$compileこれが機能するためには、コントローラーに注入する必要があります。

詳細については、$compileドキュメントをご覧ください。


2
Noahに感謝します。コントローラーメソッドでコンパイルを実行し、そのコントローラーメソッドをボタンのクリックイベントに直接バインドすると、コンパイルが機能します。これが実際のです。しかし、実際のアプリでは、別のjquery関数からコントローラーメソッドを呼び出す必要がありました。そこで、スコープオブジェクトへの参照を取得し、refreshメソッドを呼び出して再コンパイルしましたが、機能しません。これがです。この例を機能させるのを手伝ってくれませんか。
ラジャ

1
@Raja角度フレームワークの外部で角度メソッド/式を呼び出す場合は、$ scope。$ apply()を呼び出して、例外処理、ウォッチの実行などの適切なスコープライフサイクルを実行する必要があります。jsfiddle.net/ fABdD / 14
Artem Andreev

1
@ArtemAndreevはまさにその通りです。@Rajaアーキテクチャにとって意味がある場合$scope.$apply()は、呼び出しをrefresh()関数自体に移動することもできます。jsfiddle.net
Noah Freitas

3
リンクangular-1.0.1.min.js元の例では、ここでは404だった@ ArtemAndreev-sの例の作業バージョンに更新されていますjsfiddle.net/pmorch/fABdD/186と@NoahFreitas例:jsfiddle.net/pmorch/8xkeh/43
ピーターV.Mørchを

1
$ compileを使用し、コントローラー内でDOMを操作すると、テスト不可能なコードが存在する可能性があります。ディレクティブは、コントローラーではなくDOMを変更するために使用する必要があります。
Enzey 2014

57

動的コンテンツを制御できない場合の別の解決策

これは、ディレクティブを介して要素をロードしなかった場合に機能します(つまり、コメント付きのjsfiddlesの例のように)。

コンテンツをまとめる

JQueryを使用している場合に選択できるように、コンテンツをdivでラップします。また、ネイティブjavascriptを使用して要素を取得することも選択します。

<div class="selector">
    <grid-filter columnname="LastNameFirstName" gridname="HomeGrid"></grid-filter>
</div>

AngularInjectorを使用する

$ compileがない場合は、次のコードを使用して$ compileへの参照を取得できます。

$(".selector").each(function () {
    var content = $(this);
    angular.element(document).injector().invoke(function($compile) {
        var scope = angular.element(content).scope();
        $compile(content)(scope);
    });
});

概要

元の投稿は、$ compileリファレンスが手元にあることを前提としているようです。参照があれば明らかに簡単ですが、私はそうしなかったので、これが私にとっての答えでした。

前のコードの1つの警告

minifyシナリオでasp.net/mvcバンドルを使用している場合、リリースモードで展開すると問題が発生します。この問題は、Uncaught Error:[$ injector:unpr]の形で発生します。これは、ミニファイアが角度のあるjavascriptコードをいじることによって引き起こされます。

これを修正する方法は次のとおりです。

前のコードスニペットを次のオーバーロードに置き換えます。

...
angular.element(document).injector().invoke(
[
    "$compile", function($compile) {
        var scope = angular.element(content).scope();
        $compile(content)(scope);
    }
]);
...

これは私がそれをつなぎ合わせる前に私に多くの悲しみを引き起こしました。


1
上記のコードが私のために働いたので、スコープとコンパイルについての説明が利用可能であることに感謝します。簡単な部分を見逃すことがあります:)
Abs 2014

コンパイル後に$ digestする必要がありました。
knu 2014

2
絶対的な命の恩人、私はコードに角度の条件付きチェックを追加しました。これにより、角度アプリに接続したときにすべての角度の良さを使用できるようになります
LiamRyan 2014

18

@jwizeの回答への追加

angular.element(document).injector()エラーが発生したためinjector is not defined 、AJAX呼び出し後、またはjQueryを使用してDOMが変更されたときに実行できる関数を作成しました。

  function compileAngularElement( elSelector) {

        var elSelector = (typeof elSelector == 'string') ? elSelector : null ;  
            // The new element to be added
        if (elSelector != null ) {
            var $div = $( elSelector );

                // The parent of the new element
                var $target = $("[ng-app]");

              angular.element($target).injector().invoke(['$compile', function ($compile) {
                        var $scope = angular.element($target).scope();
                        $compile($div)($scope);
                        // Finally, refresh the watch expressions in the new element
                        $scope.$apply();
                    }]);
            }

        }

新しい要素のセレクターだけを渡すことで使用します。このような

compileAngularElement( '.user' ) ; 

おかげで、これはダイアログにUIKitを使用し、$ scope関数内でその場でHTMLを生成するパッケージで私の命を救いました。補足として、ニーズに合わせて$ targetのセレクター(私の場合は$ ["data-ng-controller]")を変更し、$ scope.apply();をコメントアウトする必要がありました。この関数はすでに角度コールバックで呼び出されているため(したがって、適用はすでに行われていました)
zontar 2017年

@zontartarget引数から取得することで、動的にすることもできます。
shyammakwana.me
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.