web-dev-qa-db-ja.com

レガシコードからAngularJSを呼び出す

AngularJSを使用して、レガシーFlexアプリケーションと対話するHTMLコントロールを構築しています。 Flexアプリからのすべてのコールバックは、DOMウィンドウに添付する必要があります。

たとえば(AS3で)

ExternalInterface.call("save", data);

電話します

window.save = function(data){
    // want to update a service 
    // or dispatch an event here...
}

JSサイズ変更関数内から、コントローラーが聞くことができるイベントをディスパッチしたいと思います。サービスを作成するのが方法のようです。 AngularJSの外部からサービスを更新できますか?コントローラーはサービスからのイベントをリッスンできますか? 1つ 実験(フィドルをクリック) 私はサービスにアクセスできるように見えましたが、サービスのデータの更新はビューに反映されません(この例では<option><select>に追加する必要があります) )。

ありがとう!

180
kreek

angularの外部からangularへの相互運用は、angularアプリケーションのデバッグまたはサードパーティライブラリとの統合と同じです。

すべてのDOM要素に対して、これを行うことができます。

  • angular.element(domElement).scope()は、要素の現在のスコープを取得します
  • angular.element(domElement).injector()は、現在のアプリインジェクターを取得します
  • ng-controllerインスタンスを取得するためのangular.element(domElement).controller()

インジェクタから、angularアプリケーションの任意のサービスを取得できます。同様に、スコープから発行されたメソッドを呼び出すことができます。

angularモデルへの変更またはスコープでのメソッド呼び出しは、次のように$apply()でラップする必要があることに注意してください。

$scope.$apply(function(){
  // perform any model changes or method invocations here on angular app.
});
292
Misko Hevery

Miskoが正解を出しました(もちろん)が、初心者の中にはさらに単純化する必要がある人もいるかもしれません。

レガシーアプリ内からAngularJSコードを呼び出す場合、AngularJSコードをレガシーアプリケーションの保護されたコンテナ内に存在する「マイクロアプリ」と考えてください。 (非常に正当な理由で)直接呼び出すことはできませんが、$ scopeオブジェクトを使用してリモート呼び出しを行うことができます。

$ scopeオブジェクトを使用するには、$ scopeのハンドルを取得する必要があります。幸いなことに、これは非常に簡単です。

AngularJS "micro-app" HTML内の任意のHTML要素のIDを使用して、AngularJSアプリの$ scopeのハンドルを取得できます。

例として、sayHi()やsayBye()など、AngularJSコントローラー内のいくつかの関数を呼び出したいとしましょう。 AngularJS HTML(ビュー)には、「MySuperAwesomeApp」というIDを持つdivがあります。次のコードをjQueryと組み合わせて使用​​して、$ scopeのハンドルを取得できます。

var microappscope = angular.element($("#MySuperAwesomeApp")).scope();

これで、スコープハンドルを使用してAngularJSコード関数を呼び出すことができます。

// we are in legacy code land here...

microappscope.sayHi();

microappscope.sayBye();

物事をより便利にするために、アクセスしたいときはいつでも関数を使用してスコープハンドルを取得できます。

function microappscope(){

    return angular.element($("#MySuperAwesomeApp")).scope();

}

呼び出しは次のようになります。

microappscope().sayHi();

microappscope().sayBye();

ここで実際の例を見ることができます:

http://jsfiddle.net/peterdrinnan/2nPnB/16/

また、これをOttawa AngularJSグループのスライドショーで示しました(最後の2つのスライドにスキップしてください)

http://www.slideshare.net/peterdrinnan/angular-for-legacyapps

86
Peter Drinnan

私が見つけた概念の最大の説明はここにあります: https://groups.google.com/forum/#!msg/angular/kqFrwiysgpA/eB9mNbQzcHwJ

クリックを保存するには:

// get Angular scope from the known DOM element
e = document.getElementById('myAngularApp');
scope = angular.element(e).scope();
// update the model with a wrap in $apply(fn) which will refresh the view for us
scope.$apply(function() {
    scope.controllerMethod(val);
}); 
24
Wiseman

他の答えにさらに。コントローラーのメソッドにはアクセスしたくないが、サービスには直接アクセスしたい場合は、次のようなことができます。

// Angular code* :
var myService = function(){
    this.my_number = 9;
}
angular.module('myApp').service('myService', myService);


// External Legacy Code:
var external_access_to_my_service = angular.element('body').injector().get('myService');
var my_number = external_access_to_my_service.my_number 
13
Alec Hewitt

前の投稿のおかげで、非同期イベントでモデルを更新できます。

<div id="control-panel" ng-controller="Filters">
    <ul>
        <li ng-repeat="filter in filters">
        <button type="submit" value="" class="filter_btn">{{filter.name}}</button>
        </li>
    </ul>
</div>

モデルを宣言します

function Filters($scope) {
    $scope.filters = [];
}

そして、スコープ外からモデルを更新します

ws.onmessage = function (evt) {
    dictt = JSON.parse(evt.data);
    angular.element(document.getElementById('control-panel')).scope().$apply(function(scope){
        scope.filters = dictt.filters;
    });
};
13

特にデバッグデータがオフの場合、より安全でパフォーマンスの高い方法は、共有変数を使用してコールバック関数を保持することです。 angularコントローラーは、この関数を実装して、その内部を外部コードに返します。

var sharedVar = {}
myModule.constant('mySharedVar', sharedVar)
mymodule.controller('MyCtrl', [ '$scope','mySharedVar', function( $scope, mySharedVar) {

var scopeToReturn = $scope;

$scope.$on('$destroy', function() {
        scopeToReturn = null;
    });

mySharedVar.accessScope = function() {
    return scopeToReturn;
}
}]);

再利用可能なディレクティブとして一般化:

同様の方法で動作する「exposeScope」ディレクティブを作成しましたが、使用方法は簡単です。

<div ng-controller="myController" expose-scope="aVariableNameForThisScope">
   <span expose-scope='anotherVariableNameForTheSameScope" />
</div>

これにより、現在のスコープ(ディレクティブのリンク関数に与えられる)が、すべてのスコープのホルダーであるグローバルな「スコープ」オブジェクトに保存されます。ディレクティブ属性に提供された値は、このグローバルオブジェクトのスコープのプロパティ名として使用されます。

デモを参照してください here 。デモで示したように、スコープがグローバルな「スコープ」オブジェクトから保存および削除されると、jQueryイベントをトリガーできます。

<script type="text/javascript" >
    $('div').on('scopeLinked', function(e, scopeName, scope, allScopes) {
      // access the scope variable or the given name or the global scopes object
    }.on('scopeDestroyed', function(e, scopeName, scope, allScopes) {
      // access the scope variable or the given name or the global scopes object
    }

</script>

実際の要素がDOMから削除されるときに、on( 'scopeDestroyed')をテストしていないことに注意してください。動作しない場合は、要素ではなくドキュメント自体でイベントをトリガーすると役立つ場合があります。 (app.jsを参照)デモプランカーのスクリプト。

6
Cagatay Kalan

これは古い質問であることは知っていますが、最近これを行うためのオプションを探していたので、誰にも役立つ場合に備えてここに調査結果を入れようと思いました。

ほとんどの場合、外部のレガシーコードがUIの状態やアプリケーションの内部動作とやり取りする必要がある場合、サービスはそれらの変更を抽象化するのに役立ちます。外部コードがangularコントローラー、コンポーネント、またはディレクティブと直接やり取りしている場合、アプリをレガシーコードと大きく結合していることになり、これは悪いニュースです。

私の場合、最終的に使用したのは、ブラウザーでアクセス可能なグローバル(つまり、window)とイベント処理の組み合わせです。私のコードには、フォームを初期化するためにCMSからのJSON出力を必要とするスマートフォーム生成エンジンがあります。これが私がやったことです:

function FormSchemaService(DOM) {
    var conf = DOM.conf;

    // This event is the point of integration from Legacy Code 
    DOM.addEventListener('register-schema', function (e) {

       registerSchema(DOM.conf); 
    }, false);

    // service logic continues ....

フォームスキーマサービスは、予想どおりangularインジェクターを使用して作成されます。

angular.module('myApp.services').
service('FormSchemaService', ['$window' , FormSchemaService ])

そして、私のコントローラーで:function(){'use strict';

angular.module('myApp').controller('MyController', MyController);

MyEncapsulatorController.$inject = ['$scope', 'FormSchemaService'];

function MyController($scope, formSchemaService) {
    // using the already configured formSchemaService
    formSchemaService.buildForm(); 

これまでのところ、これは純粋なangularおよびjavascriptサービス指向プログラミングです。ただし、レガシー統合は次のとおりです。

<script type="text/javascript">

   (function(app){
        var conf = app.conf = {
       'fields': {
          'field1: { // field configuration }
        }
     } ; 

     app.dispatchEvent(new Event('register-schema'));

 })(window);
</script>

明らかに、どのアプローチにもメリットとデメリットがあります。このアプローチの利点と使用法は、UIに依存します。私のフォームスキーマとレガシコードにはangularスコープの制御と知識がないため、以前に提案されたアプローチは私の場合は機能しません。したがって、スコープを変更すると、angular.element('element-X').scope();に基づいてアプリを構成すると、アプリが破損する可能性があります。ただし、アプリがスコーピングの知識を持ち、頻繁に変化しないことを信頼できる場合、以前に提案されたのは実行可能なアプローチです。

お役に立てれば。フィードバックも歓迎します。

3
Shakus