레거시 코드에서 AngularJS 호출


180

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 외부에서 서비스를 업데이트 할 수 있습니까? 컨트롤러가 서비스의 이벤트를 수신 할 수 있습니까? 한 실험 (피들 클릭) 에서 서비스에 액세스 할 수있는 것처럼 보이지만 서비스 데이터 업데이트는보기에 반영되지 않습니다 (예 :에 <option>추가해야 함 <select>).

감사!


1
위의 jsfiddle에서 인젝터는를 사용하여 앱 내의 요소를 타겟팅하지 않고 얻습니다 var injector = angular.injector(['ng', 'MyApp']);. 이렇게하면 완전히 새로운 컨텍스트와 복제본이 제공 myService됩니다. 즉, 서비스 및 모델의 두 인스턴스로 끝나고 잘못된 위치에 데이터가 추가됩니다. 대신을 사용하여 앱 내의 요소를 타겟팅해야합니다 angular.element('#ng-app').injector(['ng', 'MyApp']). 이 시점에서 $ apply를 사용하여 모델 변경 사항을 래핑 할 수 있습니다.
Thanh Nguyen

답변:


293

각도 외부에서 각도로의 Interop은 각도 응용 프로그램을 디버깅하거나 타사 라이브러리와 통합하는 것과 같습니다.

모든 DOM 요소에 대해 다음을 수행 할 수 있습니다.

  • angular.element(domElement).scope() 요소의 현재 범위를 가져옵니다.
  • angular.element(domElement).injector() 현재 앱 인젝터를 얻으려면
  • angular.element(domElement).controller()ng-controller인스턴스 를 확보 합니다.

인젝터에서 각도 어플리케이션의 모든 서비스를 유지할 수 있습니다. 마찬가지로 범위에서 게시 된 모든 메서드를 호출 할 수 있습니다.

각도 모델에 대한 변경 사항이나 범위에서 메서드 호출은 다음 $apply()과 같이 래핑해야합니다 .

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

1
이것은 작동하지만 모듈에서 직접 범위로 가져올 수있는 방법이 있었으면 좋겠습니다. 가능합니까? [ng-app]내가 이미 모듈에 대한 참조를 가지고있을 때 루트 노드를 선택 해야하는 것은 거꾸로 보인다 ...
mindplay.dk

5
나는 이것을 작동시킬 수 없다 : 나는 호출 angular.element(document.getElementById(divName)).scope()하고 있지만, 어떤 함수도 호출 할 수 없으며 콘솔에서 "정의되지 않음"을 반환한다.
Emil

1
@Emil이 위에서 설명한 것과 동일한 문제에 직면하더라도 정의되지 않은 상태로 반환됩니다. 어떤 도움?
Bibin

5
디버그 데이터가 꺼져 있으면 element (). scope ()가 작동하지 않으므로 프로덕션 환경에 권장됩니다. 이 시나리오에서는 이것이 쓸모 없습니까? 이것은 테스트 / 디버깅에만 해당됩니다.
K. Norbert

4
Angular 1.3에서는이 작업을 원하지 않습니다. Angular 팀은 프로덕션 코드의 요소에 대해 ".scope ()"를 호출하지 않았습니다. 디버그 도구로 사용되었습니다. 따라서 Angular 1.3부터는이 기능을 끌 수 있습니다. Angular는 jQuery의 .data 함수를 사용하여 요소에 범위를 연결하는 것을 중지합니다. 앱 속도가 빨라집니다. 또한 범위를 jquery의 캐싱 기능으로 전달하면 메모리 누수가 발생합니다. 따라서 앱 속도를 높이려면 반드시이 기능을 해제해야합니다. Angular 사이트에는 자세한 정보를 얻기 위해 사용해야하는 제작 가이드가 있습니다.
frosty

86

Misko가 정답을 줬지만, 우리 중 일부는 더 단순화해야 할 수도 있습니다.

레거시 앱 내에서 AngularJS 코드를 호출 할 때는 AngularJS 코드를 레거시 애플리케이션의 보호 된 컨테이너 내에 존재하는 "마이크로 앱"이라고 생각하십시오. 직접 호출 할 수는 없지만 (정당한 이유로) $ scope 객체를 통해 원격 호출을 할 수 있습니다.

$ scope 객체를 사용하려면 $ scope 핸들을 가져와야합니다. 다행히도 이것은 매우 쉽습니다.

AngularJS "micro-app"HTML에서 HTML 요소의 id를 사용하여 AngularJS 앱 $ scope의 핸들을 얻을 수 있습니다.

예를 들어 AngularJS 컨트롤러 내에서 sayHi () 및 sayBye ()와 같은 몇 가지 함수를 호출하려고한다고 가정합니다. AngularJS HTML (보기)에는 ID가 "MySuperAwesomeApp"인 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


4
링크 전용 답변은 사용하지 않는 것이 좋습니다. 따라서 SO 답변은 솔루션 검색의 종점이어야합니다 (단, 시간이 지남에 따라 참조의 또 다른 중단). 링크를 참조로 유지하면서 독립형 시놉시스를 여기에 추가하십시오.
kleopatra

추가 설명이 좋습니다. 감사.
Joe

1
아름다운 설명! 이렇게하면 양식 유효성 검사를 피할 수있었습니다.<input type="button" onclick="angular.element(this).scope().edit.delete();" value="delete">
Purefan

24

내가 찾은 개념에 대한 가장 큰 설명은 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);
}); 

14
위의 내용은 앱과 컨트롤러가 같은 요소에 공존 할 때 작동합니다. 템플릿에 ng-view 지시문을 사용하는보다 복잡한 앱의 경우 전체 앱의 DOM 요소가 아니라보기 내의 첫 번째 요소를 가져와야합니다. 나는 document.getElementsByClassName ( 'ng-scope');을 사용하여 요소를 찔렀다. 노드 목록을 통해 올바른 범위의 DOM 요소를 파악하십시오.
goosemanjack

나는 이것이 정말로 오래된 스레드라는 것을 알고 있지만이 문제가 발생하고 있다고 생각합니다. 누구나 DOM 요소를 파악하기 위해 목록을 걷는 방법을 보여주는 코드가 있습니까?
JerryKur

내 질문을 무시하십시오. 나는 document.getElementById ( 'any-Control-That-Has-An-NG-Directive'). scope ()를 사용 하여이 작업을 얻을 수있었습니다.
JerryKur

ng-view를 사용하고보기를 자체 파일로 분할하면 id상단 HTML 요소에 넣고 document.getElementById()해당 ID 를 수행 할 수 있습니다 . 이를 통해 해당 컨트롤러의 범위에 액세스 할 수 있습니다. 메소드 / 속성 등 ... goosemanjack의 의견을 잘 지적했습니다.
ftravers

13

다른 답변에 더. 컨트롤러의 메소드에 액세스하지 않고 서비스에 직접 액세스하려는 경우 다음과 같이 할 수 있습니다.

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

이전 게시물 덕분에 비동기 이벤트로 모델을 업데이트 할 수 있습니다.

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

6

특히 디버그 데이터가 꺼져있을 때 더 안전하고 성능이 좋은 방법은 공유 변수를 사용하여 콜백 함수를 유지하는 것입니다. 각도 컨트롤러는이 함수를 구현하여 내부 코드를 외부 코드로 반환합니다.

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>

이것은 현재 범위 (지시문의 링크 함수에 제공됨)를 모든 범위의 홀더 인 전역 'scopes'객체에 저장합니다. 지시문 속성에 제공된 값은이 전역 객체에서 범위의 속성 이름으로 사용됩니다.

여기 데모를 참조 하십시오 . 데모에서 알 수 있듯이 범위가 전역 'scopes'객체에서 저장되고 제거되면 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 참조) 스크립트


3

나는 이것이 오래된 질문이라는 것을 알고 있지만 최근에 이것을 할 수있는 옵션을 찾고 있었으므로 누군가에게 도움이 될 수 있도록 여기에 찾은 것을 여기에 넣었다고 생각했습니다.

대부분의 경우 외부 레거시 코드가 UI 상태 또는 응용 프로그램의 내부 작업과 상호 작용해야하는 경우 이러한 변경 사항을 추상화하는 데 서비스가 유용 할 수 있습니다. 외부 코드가 각도 컨트롤러, 구성 요소 또는 지시문과 직접 ​​상호 작용하는 경우 나쁜 소식 인 레거시 코드와 앱이 크게 결합됩니다.

필자의 경우에 사용했던 것은 브라우저 액세스 가능한 전역 (예 : 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.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(); 

지금까지 이것은 순수한 각도 및 자바 스크립트 서비스 지향 프로그래밍입니다. 그러나 레거시 통합은 다음과 같습니다.

<script type="text/javascript">

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

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

 })(window);
</script>

분명히 모든 접근 방식에는 장점과 단점이 있습니다. 이 방법의 장점과 사용은 UI에 따라 다릅니다. 내 양식 스키마와 레거시 코드에는 각도 범위에 대한 제어 및 지식이 없으므로 이전에 제안 된 방법은 작동하지 않습니다. 따라서 angular.element('element-X').scope(); 범위를 변경하면 앱을 기반으로 구성 하면 앱이 중단 될 수 있습니다. 그러나 응용 프로그램이 범위 지정에 대한 지식을 가지고 있으며 자주 변경되지 않는 응용 프로그램에 의존 할 수 있다면 이전에 제안 된 것은 실행 가능한 접근법입니다.

도움이 되었기를 바랍니다. 모든 의견도 환영합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.