Eski koddan AngularJS çağrısı


180

Eski bir Flex uygulaması ile etkileşime giren HTML denetimleri oluşturmak için AngularJS kullanıyorum. Flex uygulamasından gelen tüm geri aramalar DOM penceresine eklenmelidir.

Örneğin (AS3'te)

ExternalInterface.call("save", data);

Arayacağım

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

JS yeniden boyutlandırma işlevi içinden bir denetleyicinin duyabileceği bir olay göndermek istiyorum. Görünüşe göre bir hizmet oluşturmanın yolu budur. Bir hizmeti AngularJS dışından güncelleyebilir misiniz? Bir kontrolör bir servisin olaylarını dinleyebilir mi? Bir deneyde (keman için tıklayın) Bir servise erişebileceğim gibi görünüyordum, ancak servisin verilerini güncellemek görünüme yansıtılmıyor (örnekte a <option>eklenmelidir <select>).

Teşekkürler!


1
Yukarıdaki jsfiddle'da, enjektörün, uygulama içindeki bir öğeyi hedeflemeden elde edildiğini unutmayın var injector = angular.injector(['ng', 'MyApp']);. Bunu yapmak size tamamen yeni bir bağlam ve bir kopya verecektir myService. Bu, hizmet ve modelin iki örneğiyle sonuçlanacağınız ve yanlış yere veri ekleyeceğiniz anlamına gelir. Bunun yerine, uygulama içindeki bir öğeyi hedeflemelisiniz angular.element('#ng-app').injector(['ng', 'MyApp']). Bu noktada, model değişikliklerini sarmak için $ Apply kullanabilirsiniz.
Thanh Nguyen

Yanıtlar:


293

Açısaldan dışa doğru birlikte çalışma, açısal uygulamada hata ayıklama veya üçüncü taraf kitaplığıyla entegrasyon ile aynıdır.

Herhangi bir DOM öğesi için bunu yapabilirsiniz:

  • angular.element(domElement).scope() öğenin geçerli kapsamını almak için
  • angular.element(domElement).injector() mevcut uygulama enjektörünü almak için
  • angular.element(domElement).controller()ng-controllerörneği ele geçirmek için .

Enjektörden açısal uygulamada herhangi bir servisi alabilirsiniz. Benzer şekilde, kapsamdan, kendisine yayınlanan herhangi bir yöntemi çağırabilirsiniz.

Açısal modeldeki herhangi bir değişikliğin veya kapsamdaki herhangi bir yöntem çağrısının şu şekilde sarılması gerektiğini unutmayın $apply():

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

1
Bu işe yarıyor, ama keşke bir Modülden doğrudan kapsamına ulaşmanın bir yolu olsaydı - bu mümkün mü? [ng-app]Modüle bir referansım olduğunda bir kök kök düğümüne geri dönmek zorunda kaldığımda geriye doğru görünüyor ...
mindplay.dk

5
Bu işe yarayamıyorum: arıyorum angular.element(document.getElementById(divName)).scope(), ancak herhangi bir işlev çağırmak mümkün değil, sadece konsolda "tanımsız" döndürür.
Emil

1
Yukarıda @Emil tarafından açıklananla aynı sorunla karşılaşsam bile, tanımsız dönüyor. Herhangi bir yardım ?
13'te Bibin

5
üretim için önerilen hata ayıklama verileri kapatılırsa element (). scope () çalışmaz. Bu, bu senaryoda işe yaramaz mı? Bu sadece test / hata ayıklama için olacaktır.
K. Norbert

4
Bunu Açısal 1.3'te yapmak istemezsiniz. Açısal ekip, üretim kodundaki öğeler üzerinde ".scope ()" çağrısında bulunmayı amaçlamıyordu. Bir hata ayıklama aracı olması gerekiyordu. Böylece, Açısal 1.3'ten başlayarak bunu kapatabilirsiniz. Açısal, jQuery'nin .data işlevini kullanarak kapsamı öğeye eklemeyi durduracaktır. Bu, uygulamanızı hızlandıracaktır. Ayrıca, kapsamlarınızı jquery'nin önbellekleme özelliklerine teslim etmek bellek sızıntıları yaratacaktır. Bu nedenle, uygulamanızı hızlandırmak için bunu kesinlikle kapatmalısınız. Angular'ın sitesinde daha fazla bilgi edinmek için kullanmanız gereken bir üretim rehberi vardır.
Ayaz

86

Misko doğru cevabı verdi (açıkçası), ancak bazı yeni başlayanlar daha da basitleştirilmeye ihtiyaç duyabilir.

AngularJS kodunu eski uygulamalardan çağırmaya gelince, AngularJS kodunu eski uygulamanızdaki korumalı bir kapta bulunan bir "mikro uygulama" olarak düşünün. Doğrudan (çok iyi bir nedenle) çağrı yapamazsınız, ancak $ scope nesnesi aracılığıyla uzaktan çağrı yapabilirsiniz.

$ Scope nesnesini kullanmak için $ scope işleyicisini almanız gerekir. Neyse ki bunu yapmak çok kolay.

AngularJS uygulaması $ kapsamının üstesinden gelmek için AngularJS "mikro uygulaması" HTML'nizdeki herhangi bir HTML öğesinin kimliğini kullanabilirsiniz.

Örnek olarak, diyelim ki AngularJS denetleyicimizde sayHi () ve sayBye () gibi birkaç işlevi çağırmak istiyoruz. AngularJS HTML'de (görünüm) "MySuperAwesomeApp" kimliğine sahip bir div var. JQuery ile birlikte şu kapsamı kullanabilirsiniz: $ kapsamı ele almak için:

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

Artık AngularJS kod işlevlerinizi kapsam tanıtıcısı aracılığıyla çağırabilirsiniz:

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

microappscope.sayHi();

microappscope.sayBye();

İşleri daha kolay hale getirmek için, erişmek istediğinizde kapsam tutamacını yakalamak için bir işlev kullanabilirsiniz:

function microappscope(){

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

}

Aramalarınız şöyle görünecektir:

microappscope().sayHi();

microappscope().sayBye();

Burada çalışan bir örnek görebilirsiniz:

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

Bunu Ottawa AngularJS grubu için bir slayt gösterisinde de gösterdim (sadece son 2 slayta atla)

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


4
Yalnızca bağlantı yanıtlarının cesaretinin kırıldığına dikkat edin, SO yanıtları bir çözüm arayışının son noktası olmalıdır (zamanla bayatlama eğilimi gösteren başka bir referans noktasıyla karşılaştırıldığında). Lütfen bağlantıyı referans olarak tutarak bağımsız bir özet eklemeyi düşünün.
kleopatra

Güzel ek açıklama. Teşekkürler.
Joe

1
Güzel açıklama! bunu yaparak bir form doğrulamasını <input type="button" onclick="angular.element(this).scope().edit.delete();" value="delete">
atlatmama

24

Bulduğum konseptin en iyi açıklaması burada yer alıyor: https://groups.google.com/forum/#!msg/angular/kqFrwiysgpA/eB9mNbQzcHwJ

Tıklamayı kaydetmek için:

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

14
Yukarıdakiler, uygulama ve denetleyici aynı öğede birlikte bulunduğunda çalışır. Bir şablona ng-view yönergesi kullanan daha karmaşık uygulamalar için, tüm uygulamanın DOM öğesini değil, görünüm içindeki ilk öğeyi almanız gerekir. Bir document.getElementsByClassName ('ng-scope') öğeleri etrafında poke gerekiyordu; Düğüm listesi kapmak için doğru kapsam DOM elemanını bulmak için.
goosemanjack

Bu gerçekten eski bir iş parçacığı olduğunu biliyorum, ama bence bu sorunla karşılaşıyorum. Herkes kapmak için DOM elemanını bulmak için listede yürümek nasıl gösteren herhangi bir kodu var mı?
JerryKur

Sorumu görmezden gel. Bu işi sadece document.getElementById ('any-Control-That-Has-An-NG-Directive'). Scope () kullanarak alabildim.
JerryKur

ng-view kullanır ve görünümlerinizi kendi dosyalarına bölerseniz. idüst HTML öğesine koyabilir ve ardından document.getElementById()bu kimliği yapabilirsiniz. Bu, söz konusu denetleyicinin kapsamına erişmenizi sağlar. yöntemleri / özellikleri vb ... sadece bektaşi kuşu yorumunda iyi bir noktaya koyarak.
ftravers

13

Diğer cevaplara ek olarak. Bir denetleyicideki bir yönteme erişmek istemiyorsanız ancak hizmete doğrudan erişmek istiyorsanız, şöyle bir şey yapabilirsiniz:

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

Önceki yazı sayesinde, modelimi eşzamansız bir etkinlikle güncelleyebilirim.

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

Modelimi beyan ederim

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

Ve modelimi kapsamımın dışından güncelliyorum

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

6

Özellikle hata ayıklama verileri kapalıyken daha güvenli ve daha etkili bir yol, bir geri arama işlevini tutmak için paylaşılan bir değişken kullanmaktır. Açısal denetleyiciniz, iç işlevlerini harici koda döndürmek için bu işlevi uygular.

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

Yeniden kullanılabilir bir direktif olarak genelleştirildi:

Benzer bir şekilde çalışan bir 'exposeScope' yönergesi oluşturdum ancak kullanımı daha basit:

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

Bu, geçerli kapsamı (yönergenin bağlantı işlevine verilen) tüm kapsamlar için bir tutucu olan genel bir 'kapsamlar' nesnesinde saklar. Directive özniteliğine sağlanan değer, bu genel nesnede kapsamın özellik adı olarak kullanılır.

Demoya buradan bakın . Demoda gösterdiğim gibi, kapsam global 'kapsamlar' nesnesinden saklandığında ve kaldırıldığında jQuery olaylarını tetikleyebilirsiniz.

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

Gerçek öğe DOM'dan kaldırıldığında açık ('scopeDestroyed') test etmedim. Çalışmazsa, öğenin yerine belgenin kendisinde olayı tetiklemek yardımcı olabilir. (bkz. app.js) komut dosyasını demo plunker'da bulabilirsiniz.


3

Bunun eski bir soru olduğunu biliyorum, ancak son zamanlarda bunu yapmak için seçeneklere bakıyordum, bu yüzden bulgularımı herkes için yararlı olması durumunda buraya koyduğumu düşündüm.

Çoğu durumda, UI'nin durumu veya uygulamanın iç işleyişleri ile etkileşim için harici eski kodlara ihtiyaç duyuluyorsa, bir hizmet bu değişiklikleri soyutlamak için yararlı olabilir. Harici bir kod açısal denetleyiciniz, bileşeniniz veya direktifinizle doğrudan etkileşime giriyorsa, uygulamanızı büyük ölçüde kötü haber olan eski kodunuzla eşleştiriyorsunuzdur.

Benim durumumda kullandığım şey, tarayıcı erişilebilir küresellerin (yani pencere) ve olay işlemenin bir kombinasyonudur. Kodum, formları başlatmak için bir CMS'den JSON çıktısı gerektiren akıllı bir form oluşturma motoruna sahiptir. İşte yaptığım:

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

Form Şeması Hizmeti, beklendiği gibi açısal enjektör kullanılarak oluşturulur:

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

Ve denetleyicilerimde: function () {'use strict';

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

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

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

Şimdiye kadar bu saf açısal ve javascript servis odaklı programlama. Ancak eski entegrasyon buraya gelir:

<script type="text/javascript">

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

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

 })(window);
</script>

Açıkçası her yaklaşımın avantajları ve dezavantajları vardır. Bu yaklaşımın avantajları ve kullanımı kullanıcı arayüzünüze bağlıdır. Önceden önerilen yaklaşımlar benim durumumda işe yaramıyor çünkü form şemamın ve eski kodumun açısal kapsamların kontrolü ve bilgisi yok. Uygulamamı temel alarak yapılandırmak angular.element('element-X').scope(); , kapsamları değiştirirsek uygulamayı bozabilir. Ancak uygulamanızın kapsam belirleme bilgisine sahip olması ve sık sık değişmemesine güvenebiliyorsanız, daha önce önerilen şey uygulanabilir bir yaklaşımdır.

Bu yardımcı olur umarım. Herhangi bir geri bildirim de açığız.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.