Appeler AngularJS à partir du code d'héritage

J'utilise AngularJS pour construire des contrôles HTML qui interagissent avec une application legacy Flex. Tous les appels de L'application Flex doivent être attachés à la fenêtre DOM.

par exemple (dans AS3)

ExternalInterface.call("save", data);

appellera

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

de l'intérieur de la fonction de redimensionnement JS j'aimerais envoyer un événement qu'un contrôleur peut entendre. Il semble que la création d'un service est le chemin à parcourir. Pouvez-vous mettre à jour un service depuis l'extérieur de AngularJS? Un contrôleur peut-il écouter les événements d'un service? Dans une expérience (click for fiddle) j'ai fait il semble que je peux accéder à un service, mais la mise à jour des données du service ne se reflète pas dans la vue (dans l'exemple un <option> devrait être ajouté au <select> ).

Merci!

180
demandé sur chanafdo 2012-05-08 03:35:52

7 réponses

Interop de l'extérieur angulaire angulaire est la même que le débogage angulaire de l'application ou de s'intégrer à la troisième partie de la bibliothèque.

pour tout élément DOM vous pouvez faire ceci:

  • angular.element(domElement).scope() pour obtenir la portée actuelle de l'élément
  • angular.element(domElement).injector() pour obtenir l'application en cours d'injecteur
  • angular.element(domElement).controller() pour mettre la main sur l'instance ng-controller .

de l'injecteur, vous pouvez obtenir une prise de n'importe quel service en application angulaire. De même, à partir de la portée, vous pouvez invoquer toutes les méthodes qui ont été publiées.

garder à l'esprit que tout changement au modèle angulaire ou toute invocation de méthode sur la portée doivent être enveloppés dans $apply() comme ceci:

$scope.$apply(function(){
  // perform any model changes or method invocations here on angular app.
});
291
répondu Misko Hevery 2012-05-09 01:29:49

Misko a donné la bonne réponse (évidemment), mais certains d'entre nous les débutants pourraient avoir besoin de la simplifier davantage.

lorsqu'il s'agit d'appeler du code AngularJS à partir d'applications traditionnelles, pensez au code AngularJS comme à une "micro-application" existant dans un conteneur protégé dans votre application traditionnelle. Vous ne pouvez pas faire des appels directement (pour de très bonnes raisons), mais vous pouvez faire des appels à distance via l'objet $scope.

pour utiliser l'objet $ scope, vous avez besoin pour avoir la poignée de $scope. Heureusement, il est très facile à faire.

vous pouvez utiliser l'id de n'importe quel élément HTML dans votre AngularJS" micro-app " HTML pour obtenir la poignée de L'AngularJS app $scope.

par exemple, disons que nous voulons appeler quelques fonctions dans notre contrôleur AngularJS comme sayHi() et sayBye(). Dans L'AngularJS HTML (view) Nous avons une div avec l'id "MySuperAwesomeApp". Vous pouvez utiliser le code suivant, combiné avec jQuery pour obtenir la poignée de $ scope:

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

maintenant vous pouvez appeler vos fonctions de code AngularJS par le biais de la poignée de portée:

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

microappscope.sayHi();

microappscope.sayBye();

pour rendre les choses plus pratiques, vous pouvez utiliser une fonction pour saisir la poignée scope à tout moment vous voulez y accéder:

function microappscope(){

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

}

vos appels ressembleraient alors à ceci:

microappscope().sayHi();

microappscope().sayBye();

vous pouvez voir un exemple de travail ici:

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

j'ai aussi montré ceci dans un diaporama pour le groupe AngularJS D'Ottawa (passez aux 2 dernières diapositives)

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

86
répondu Peter Drinnan 2013-07-29 15:45:20

la plus grande explication du concept que j'ai trouvé se trouve ici: https://groups.google.com/forum/#!msg / angular/kqFrwiysgpA / eB9mNbQzcHwJ

pour vous sauver le clic:

// 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
répondu Wiseman 2013-03-09 19:34:58

grâce au post précédent, je peux mettre à jour mon modèle avec un événement asynchrone.

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

je déclare mon modèle

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

et je mets à jour mon modèle hors de mon champ d'application

ws.onmessage = function (evt) {
    dictt = JSON.parse(evt.data);
    angular.element(document.getElementById('control-panel')).scope().$apply(function(scope){
        scope.filters = dictt.filters;
    });
};
13
répondu Guillaume Vincent 2015-12-08 01:43:15

suite aux autres réponses. Si vous ne voulez pas accéder à une méthode dans un controller mais que vous voulez accéder directement au service, vous pouvez faire quelque chose comme ceci:

// 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 
12
répondu Alec Hewitt 2014-10-24 09:29:23

façon plus sûre et performante surtout quand les données de débogage est éteint est d'utiliser une variable partagée pour maintenir une fonction de rappel. Votre contrôleur angulaire implémente cette fonction pour retourner ses internes au code externe.

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

généralisée en tant que directive réutilisable :

j'ai créé une directive 'exposeScope' qui fonctionne de la même manière mais l'usage est plus simple:

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

Cela stocke le champ d'application actuel ( qui est donné à la fonction lien de la directive) dans un objet global 'scopes' qui est un support pour tous les scopes. La valeur fournie à l'attribut directive est utilisée comme nom de propriété de la portée dans cet objet global.

voir la démo ici . Comme je l'ai montré dans la démo, vous pouvez déclencher des événements jQuery lorsque le scope est stocké et retiré de l'objet global 'scopes'.

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

Notez que je n'ai pas testé l'on('scopeDestroyed') lorsque l'élément réel est retiré du DOM. Si cela ne fonctionne pas, déclencher l'événement sur le document lui-même au lieu de l'élément peut aider. ( voir l'application.js) script dans le plunker de démo.

6
répondu Cagatay Kalan 2016-06-19 03:27:06

je sais que c'est une vieille question, mais je cherchais des options pour le faire récemment, donc j'ai pensé que je mettais mes conclusions ici au cas où il serait utile à quelqu'un.

dans la plupart des cas, s'il y a un besoin de code externe pour interagir avec L'état de L'UI ou le fonctionnement interne de l'application, un service pourrait être utile pour abstraire ces changements. Si un code externe interagit directement avec votre contrôleur angulaire, composant ou directive, vous couplez fortement votre application avec votre code d'héritage, ce qui est une mauvaise nouvelle.

ce que j'ai fini par utiliser dans mon cas, c'est une combinaison de globals accessibles par navigateur (c.-à-d. fenêtre ) et de gestion d'événements. Mon code possède un moteur de génération de formulaires intelligent qui nécessite la sortie JSON d'un CMS pour initialiser les formulaires. Voici ce que j'ai fait:

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

Forme de Schéma Service est créé à l'aide angulaire de l'injecteur comme prévu:

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

et dans mes contrôleurs: fonction () { "use strict";

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

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

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

jusqu'à présent, il s'agit d'une programmation purement angulaire et axée sur le service javascript. Mais l'intégration de l'héritage vient ici :

<script type="text/javascript">

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

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

 })(window);
</script>

Évidemment, chaque approche a ses avantages et inconvénients. Les avantages et l'utilisation de cette approche dépend de votre INTERFACE utilisateur. Les approches suggérées précédemment ne fonctionnent pas dans mon cas puisque mon schéma de forme et le code d'héritage ont aucune maîtrise et connaissance des lunettes angulaires. D'où la configuration de mon application basée sur angular.element('element-X').scope(); pourrait potentiellement casser l'application si nous changeons les portées autour. Mais si vous êtes du PPA a connaissance de la portée et peut compter sur elle ne change pas souvent, ce qui a été suggéré précédemment est une approche viable.

Espérons que cette aide. Tout commentaire est également le bienvenu.

3
répondu Shakus 2016-03-22 04:25:40