Gọi AngularJS từ mã kế thừa


180

Tôi đang sử dụng AngularJS để xây dựng các điều khiển HTML tương tác với ứng dụng Flex kế thừa. Tất cả các cuộc gọi lại từ ứng dụng Flex phải được đính kèm vào cửa sổ DOM.

Ví dụ (trong AS3)

ExternalInterface.call("save", data);

Sẽ gọi

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

Từ bên trong chức năng thay đổi kích thước JS tôi muốn gửi một sự kiện mà bộ điều khiển có thể nghe thấy. Có vẻ như tạo ra một dịch vụ là cách để đi. Bạn có thể cập nhật một dịch vụ từ bên ngoài AngularJS không? Bộ điều khiển có thể lắng nghe các sự kiện từ một dịch vụ không? Trong một thử nghiệm (nhấp vào fiddle) Tôi đã làm như thể tôi có thể truy cập một dịch vụ nhưng việc cập nhật dữ liệu của dịch vụ không được phản ánh trong chế độ xem (trong ví dụ <option>cần thêm vào <select>).

Cảm ơn!


1
Lưu ý rằng trong jsfiddle phía trên trình tiêm có được mà không nhắm mục tiêu một phần tử trong ứng dụng bằng cách sử dụng var injector = angular.injector(['ng', 'MyApp']);. Làm điều này sẽ cung cấp cho bạn một bối cảnh hoàn toàn mới và một bản sao myService. Điều đó có nghĩa là bạn sẽ kết thúc với hai phiên bản của dịch vụ và mô hình và sẽ thêm dữ liệu vào sai địa điểm. Thay vào đó, bạn nên nhắm mục tiêu một yếu tố trong ứng dụng angular.element('#ng-app').injector(['ng', 'MyApp']). Tại thời điểm này, bạn có thể sử dụng $ áp dụng để bọc các thay đổi mô hình.
Thanh Nguyễn

Câu trả lời:


293

Interop từ bên ngoài góc tới góc giống như gỡ lỗi ứng dụng góc hoặc tích hợp với thư viện bên thứ ba.

Đối với bất kỳ phần tử DOM nào, bạn có thể làm điều này:

  • angular.element(domElement).scope() để có được phạm vi hiện tại cho phần tử
  • angular.element(domElement).injector() để có được trình tiêm ứng dụng hiện tại
  • angular.element(domElement).controller()để có được một ng-controllerví dụ.

Từ kim phun, bạn có thể có được bất kỳ dịch vụ nào trong ứng dụng góc. Tương tự từ phạm vi bạn có thể gọi bất kỳ phương thức nào đã được xuất bản cho nó.

Hãy nhớ rằng bất kỳ thay đổi nào đối với mô hình góc hoặc bất kỳ yêu cầu phương thức nào trên phạm vi cần phải được gói gọn $apply()như sau:

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

1
điều này hoạt động, nhưng tôi ước có một số cách để lấy trực tiếp từ một Mô-đun đến phạm vi của nó - điều đó có thể không? Phải quay lại chọn nút [ng-app]gốc dường như ngược khi tôi đã có tham chiếu đến Mô-đun ...
mindplay.dk

5
Tôi không thể làm việc này: Tôi đang gọi angular.element(document.getElementById(divName)).scope(), nhưng tôi không thể gọi bất kỳ chức năng nào từ nó, nó chỉ trả về "không xác định" trong bảng điều khiển.
Emil

1
Ngay cả khi tôi đối mặt với cùng một vấn đề như được mô tả ở trên bởi @Emil, nó sẽ trở lại không xác định. Có ai giúp đỡ không?
Bibin

5
phần tử (). scope () sẽ không hoạt động nếu dữ liệu gỡ lỗi bị tắt, đây là khuyến nghị cho sản xuất. Điều đó không làm cho nó vô dụng trong kịch bản này? Điều này sẽ chỉ để thử nghiệm / gỡ lỗi.
K. Norbert

4
Bạn sẽ không muốn làm điều này trong Angular 1.3. Nhóm Angular không có ý định yêu cầu chúng tôi gọi ".scope ()" về các yếu tố trong mã sản xuất. Nó có nghĩa là một công cụ gỡ lỗi. Vì vậy, bắt đầu từ Angular 1.3, bạn có thể tắt cái này. Angular sẽ ngừng gắn phạm vi vào phần tử bằng hàm .data của jQuery. Điều này sẽ tăng tốc ứng dụng của bạn lên. Ngoài ra, việc cung cấp phạm vi của bạn cho các tính năng bộ nhớ đệm của jquery sẽ tạo ra rò rỉ bộ nhớ. Vì vậy, bạn chắc chắn nên tắt cái này đi, để tăng tốc ứng dụng của bạn. Trang web của Angular có một hướng dẫn sản xuất mà bạn nên sử dụng để tìm hiểu thêm.
băng giá

86

Misko đã đưa ra câu trả lời chính xác (rõ ràng), nhưng một số người mới trong chúng ta có thể cần nó được đơn giản hóa hơn nữa.

Khi gọi mã AngularJS từ trong các ứng dụng cũ, hãy nghĩ mã AngularJS là "ứng dụng vi mô" tồn tại trong một thùng chứa được bảo vệ trong ứng dụng cũ của bạn. Bạn không thể thực hiện cuộc gọi trực tiếp (vì lý do rất chính đáng), nhưng bạn có thể thực hiện cuộc gọi từ xa bằng đối tượng $ scope.

Để sử dụng đối tượng $ scope, bạn cần phải xử lý $ scope. May mắn là điều này rất dễ làm.

Bạn có thể sử dụng id của bất kỳ thành phần HTML nào trong HTML "ứng dụng vi mô" AngularJS của bạn để xử lý phạm vi $ của ứng dụng AngularJS.

Ví dụ: giả sử chúng ta muốn gọi một vài hàm trong bộ điều khiển AngularJS của mình như sayHi () và sayBye (). Trong AngularJS HTML (view), chúng ta có một div với id "MySuperAw đũaApp". Bạn có thể sử dụng mã sau đây, kết hợp với jQuery để xử lý $ scope:

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

Bây giờ bạn có thể gọi các hàm mã AngularJS của mình bằng cách xử lý phạm vi:

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

microappscope.sayHi();

microappscope.sayBye();

Để làm cho mọi thứ thuận tiện hơn, bạn có thể sử dụng một chức năng để lấy phạm vi xử lý bất cứ lúc nào bạn muốn truy cập nó:

function microappscope(){

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

}

Cuộc gọi của bạn sẽ như thế này:

microappscope().sayHi();

microappscope().sayBye();

Bạn có thể xem một ví dụ làm việc ở đây:

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

Tôi cũng đã thể hiện điều này trong một slideshow cho nhóm Ottawa AngularJS (chỉ cần bỏ qua 2 slide cuối cùng)

http://www.sl slideshoware.net/peterdrinnan/angular-for-legacyapps


4
Lưu ý rằng các câu trả lời chỉ liên kết không được khuyến khích, các câu trả lời SO phải là điểm cuối của tìm kiếm giải pháp (so với một điểm dừng khác của tài liệu tham khảo, có xu hướng bị cũ theo thời gian). Vui lòng xem xét việc thêm một bản tóm tắt độc lập ở đây, giữ liên kết làm tài liệu tham khảo.
kleopatra

Đẹp làm rõ thêm. Cảm ơn.
Joe

1
Đẹp giải thích! cho phép tôi phá vỡ xác nhận mẫu bằng cách thực hiện việc này:<input type="button" onclick="angular.element(this).scope().edit.delete();" value="delete">
Purefan

24

Giải thích tuyệt vời nhất về khái niệm tôi tìm thấy được đặt ở đây: https://groups.google.com/forum/#!msg/angular/kqFrwiysgpA/eB9mNbQzcHwJ

Để giúp bạn tiết kiệm khi nhấp:

// 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
Ở trên hoạt động khi ứng dụng và bộ điều khiển cùng tồn tại trong cùng một phần tử. Đối với các ứng dụng phức tạp hơn sử dụng chỉ thị ng-view cho mẫu, bạn phải lấy phần tử đầu tiên trong chế độ xem, không phải phần tử DOM của toàn bộ ứng dụng. Tôi đã phải chọc xung quanh các phần tử bằng document.getElementsByClassName ('ng-scope'); danh sách nút để tìm ra phần tử DOM phạm vi chính xác cần lấy.
goosemanjack

Tôi biết đây là một chủ đề thực sự cũ, nhưng tôi nghĩ rằng tôi đang gặp phải vấn đề này. Có ai có bất kỳ mã nào chỉ ra cách đi qua danh sách để tìm ra phần tử DOM cần lấy không?
JerryKur

Bỏ qua câu hỏi của tôi. Tôi đã có thể có được tác phẩm này chỉ bằng cách sử dụng document.getEuityById ('any-Control-That-Has-An-NG-Directive'). Phạm vi ().
JerryKur

nếu bạn sử dụng ng-view và chia quan điểm của bạn thành các tệp riêng của họ. bạn có thể đặt và idtrong phần tử HTML hàng đầu sau đó thực hiện document.getElementById()id đó. Điều này cho phép bạn truy cập vào phạm vi của bộ điều khiển đó. phương pháp / tính chất, v.v ... chỉ cần đưa ra một điểm tốt trên bình luận của goosemanjack.
ftravers 20/07/2015

13

Hơn nữa cho các câu trả lời khác. Nếu bạn không muốn truy cập một phương thức trong bộ điều khiển nhưng muốn truy cập trực tiếp vào dịch vụ, bạn có thể làm một cái gì đó như thế này:

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

Nhờ bài viết trước, tôi có thể cập nhật mô hình của mình với một sự kiện không đồng bộ.

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

Tôi tuyên bố mô hình của tôi

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

Và tôi cập nhật mô hình của mình từ bên ngoài phạm vi của tôi

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

6

Cách an toàn và hiệu quả hơn, đặc biệt là khi dữ liệu gỡ lỗi bị tắt là sử dụng biến được chia sẻ để giữ chức năng gọi lại. Bộ điều khiển góc của bạn thực hiện chức năng này để trả lại phần bên trong của nó cho mã bên ngoài.

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

Tổng quát như một chỉ thị tái sử dụng:

Tôi đã tạo một lệnh 'exposeScope' hoạt động theo cách tương tự nhưng cách sử dụng đơn giản hơn:

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

Điều này lưu trữ phạm vi hiện tại (được trao cho chức năng liên kết của chỉ thị) trong một đối tượng 'phạm vi' toàn cầu, là một chủ sở hữu cho tất cả các phạm vi. Giá trị được cung cấp cho thuộc tính chỉ thị được sử dụng làm tên thuộc tính của phạm vi trong đối tượng toàn cầu này.

Xem bản demo tại đây . Như tôi đã trình bày trong bản demo, bạn có thể kích hoạt các sự kiện jQuery khi phạm vi được lưu trữ và xóa khỏi đối tượng 'phạm vi' toàn cầu.

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

Lưu ý rằng, tôi chưa thử nghiệm trên ('scopeDestroyed') khi phần tử thực tế bị xóa khỏi DOM. Nếu nó không hoạt động, kích hoạt sự kiện trên chính tài liệu thay vì phần tử có thể giúp ích. (xem tập lệnh app.js) trong trình trình diễn demo.


3

Tôi biết đây là một câu hỏi cũ nhưng tôi đã xem xét các lựa chọn để làm điều này gần đây, vì vậy tôi nghĩ rằng tôi đã đặt những phát hiện của mình ở đây trong trường hợp nó hữu ích cho bất cứ ai.

Trong hầu hết các trường hợp, nếu có nhu cầu mã kế thừa bên ngoài để tương tác với trạng thái của giao diện người dùng hoặc hoạt động bên trong của ứng dụng, một dịch vụ có thể hữu ích để loại bỏ những thay đổi đó. Nếu một mã bên ngoài đang tương tác trực tiếp với bộ điều khiển góc, thành phần hoặc chỉ thị của bạn, thì bạn đang ghép ứng dụng của bạn rất nhiều với mã kế thừa của bạn, đó là tin xấu.

Những gì tôi đã kết thúc bằng cách sử dụng trong trường hợp của mình, là sự kết hợp giữa các trình duyệt toàn cầu có thể truy cập (tức là cửa sổ) và xử lý sự kiện. Mã của tôi có một công cụ tạo biểu mẫu thông minh yêu cầu đầu ra JSON từ CMS để khởi tạo các biểu mẫu. Đây là những gì tôi đã là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 ....

Dịch vụ Schema Form được tạo bằng cách sử dụng kim phun góc như mong đợi:

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

Và trong bộ điều khiển của tôi: function () {'sử dụng nghiêm ngặt';

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

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

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

Cho đến nay đây là lập trình hướng dịch vụ góc cạnh và javascript thuần túy. Nhưng sự tích hợp di sản đến đây:

<script type="text/javascript">

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

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

 })(window);
</script>

Rõ ràng mọi cách tiếp cận đều có ưu điểm và nhược điểm. Những lợi thế và việc sử dụng phương pháp này phụ thuộc vào giao diện người dùng của bạn. Các cách tiếp cận được đề xuất trước đây không hoạt động trong trường hợp của tôi vì lược đồ biểu mẫu và mã kế thừa của tôi không có quyền kiểm soát và kiến ​​thức về phạm vi góc. Do đó, việc định cấu hình ứng dụng của tôi dựa trên angular.element('element-X').scope(); có khả năng phá vỡ ứng dụng nếu chúng ta thay đổi phạm vi xung quanh. Nhưng nếu ứng dụng của bạn có kiến ​​thức về phạm vi và có thể dựa vào nó không thay đổi thường xuyên, những gì được đề xuất trước đây là một cách tiếp cận khả thi.

Hi vọng điêu nay co ich. Bất kỳ thông tin phản hồi cũng được chào đón.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.