AngularJS วิธีการเรียกใช้ฟังก์ชั่นคอนโทรลเลอร์จากด้านนอกของส่วนประกอบคอนโทรลเลอร์


190

ฉันจะเรียกฟังก์ชั่นที่กำหนดภายใต้ตัวควบคุมจากที่ใดก็ได้ของหน้าเว็บ (นอกองค์ประกอบของตัวควบคุม)?

มันทำงานได้อย่างสมบูรณ์แบบเมื่อฉันกดปุ่ม "รับ" แต่ฉันต้องเรียกมันจากตัวควบคุม div ภายนอก ตรรกะคือ: โดยค่าเริ่มต้น div ของฉันถูกซ่อนอยู่ ที่ไหนสักแห่งในเมนูนำทางฉันกดปุ่มและมันควรจะแสดง () div ของฉันและเรียกใช้ฟังก์ชัน "get" ฉันจะทำสิ่งนี้สำเร็จได้อย่างไร?

หน้าเว็บของฉันคือ:

<div ng-controller="MyController">
  <input type="text" ng-model="data.firstname" required>
  <input type='text' ng-model="data.lastname" required>

  <form ng-submit="update()"><input type="submit" value="update"></form>
  <form ng-submit="get()"><input type="submit" value="get"></form>
</div>

js ของฉัน:

   function MyController($scope) {
      // default data and structure
      $scope.data = {
        "firstname" : "Nicolas",
        "lastname" : "Cage"
      };

      $scope.get = function() {
        $.ajax({
           url: "/php/get_data.php?",
           type: "POST",
           timeout: 10000, // 10 seconds for getting result, otherwise error.
           error:function() { alert("Temporary error. Please try again...");},
           complete: function(){ $.unblockUI();},
           beforeSend: function(){ $.blockUI()},
           success: function(data){
            json_answer = eval('(' + data + ')');
            if (json_answer){
                $scope.$apply(function () {
                  $scope.data = json_answer;
            });
            }
        }
    });
  };

  $scope.update = function() {
    $.ajax({
        url: "/php/update_data.php?",
        type: "POST",
        data: $scope.data,
        timeout: 10000, // 10 seconds for getting result, otherwise error.
        error:function() { alert("Temporary error. Please try again...");},
        complete: function(){ $.unblockUI();},
        beforeSend: function(){ $.blockUI()},
        success: function(data){ }
      });
    };
   }

2
เมื่อคุณพูดว่า "... บางแห่งในเมนูการนำทางคุณกดปุ่ม ... " คุณหมายถึงจะบอกว่าการนำทางนี้เป็นส่วนหนึ่งของคอนโทรลเลอร์อื่นและคุณต้องการเรียกget()MyController จากคอนโทรลเลอร์อื่นหรือไม่?
callmekatootie

1
ตอนนี้เมนูนำทางไม่ได้เป็นตัวควบคุม เพียงแค่ html ไม่แน่ใจว่าเป็นไปได้หรือไม่ที่จะเรียกฟังก์ชันควบคุมจาก html / javascript นั่นเป็นสาเหตุที่ฉันโพสต์คำถามนี้ แต่ก็เป็นเหตุผลที่ทำให้เมนูนำทางเป็นตัวควบคุม ฉันจะเรียกใช้ฟังก์ชัน MyController.get () จาก NavigationMenu.Controller ได้อย่างไร
Pavel Zdarov

คำตอบ:


331

นี่เป็นวิธีเรียกฟังก์ชันของคอนโทรลเลอร์จากด้านนอก:

angular.element(document.getElementById('yourControllerElementID')).scope().get();

ซึ่งget()เป็นฟังก์ชั่นจากการควบคุมของคุณ

คุณสามารถสลับ

document.getElementById('yourControllerElementID')` 

ถึง

$('#yourControllerElementID')

หากคุณใช้ jQuery

นอกจากนี้หากฟังก์ชั่นของคุณหมายถึงการเปลี่ยนแปลงอะไรในมุมมองของคุณคุณควรโทร

angular.element(document.getElementById('yourControllerElementID')).scope().$apply();

เพื่อใช้การเปลี่ยนแปลง

อีกสิ่งหนึ่งที่คุณควรทราบก็คือขอบเขตจะเริ่มต้นได้หลังจากโหลดหน้าเว็บแล้วดังนั้นการโทรจากนอกขอบเขตควรทำทุกครั้งหลังจากโหลดหน้าเว็บแล้ว มิฉะนั้นคุณจะไม่ได้รับขอบเขตเลย

UPDATE:

ด้วยมุมเชิงมุมรุ่นล่าสุดคุณควรใช้

angular.element(document.getElementById('yourControllerElementID')).injector().‌​get('$rootScope')

และใช่แล้วนี่คือการปฏิบัติที่ไม่ดีแต่บางครั้งคุณต้องการสิ่งที่ทำได้อย่างรวดเร็วและสกปรก


30
ดูเหมือนว่าจะเป็นกลิ่นของโค้ดเนื่องจากปรัชญาของ Angular นั้นไม่ได้ผสม DOM กับ Angular code ...
JoeCool

8
ดังนั้นหากคุณไม่ควรผสมโค้ด DOM กับโค้ด Angular หากคุณต้องการให้ JQuery แอนิเมชันบางตัวดับในการตอบสนองต่อการเปลี่ยนแปลงตัวแปรในแองกูลาร์คอนโทรลเลอร์ของคุณ - คุณทำอย่างนั้นจริงหรือ มันจะเป็นเรื่องเล็กน้อยที่จะทำจากตัวควบคุม แต่ฉันไม่รู้ว่าจะทำอย่างไรให้หมดจด
ม.ค.

3
ฉันไม่สามารถ$applyทำงานให้ฉันได้จนกว่าฉันจะห่อโค้ดให้เป็นฟังก์ชั่นเช่น..scope.$apply(function() { scope.get() });
surfitscrollit

2
ที่จริงฉันสามารถเข้าถึงตัวควบคุมด้วย angular.element(document.getElementById('yourControllerElementID')).scope().controller; สำหรับอดีต ถ้าใช้: angular.element(document.getElementById('controller')).scope().get() พ่นและข้อผิดพลาดที่ไม่ได้กำหนด แต่ถ้าฉันใช้angular.element(document.getElementById('controller')).scope().controller.get()มันใช้งานได้
vrunoa

2
เป็นไปได้ไหมว่าวิธีนี้ใช้ไม่ได้กับ Angular 1.4.9 อีกต่อไป? ฉันไม่สามารถเข้าถึงได้scope()ในangular.element(...)เพราะมันกลับไม่ได้กำหนดและ vardump ขององค์ประกอบ / วัตถุเชิงมุมบอกว่าฟังก์ชั่นscopeตั้งอยู่ภายใน__proto__-object
Smamatti

37

ฉันพบตัวอย่างบนอินเทอร์เน็ต

ผู้ชายบางคนเขียนโค้ดนี้และทำงานได้อย่างสมบูรณ์

HTML

<div ng-cloak ng-app="ManagerApp">
    <div id="MainWrap" class="container" ng-controller="ManagerCtrl">
       <span class="label label-info label-ext">Exposing Controller Function outside the module via onClick function call</span>
       <button onClick='ajaxResultPost("Update:Name:With:JOHN","accept",true);'>click me</button>
       <br/> <span class="label label-warning label-ext" ng-bind="customParams.data"></span>
       <br/> <span class="label label-warning label-ext" ng-bind="customParams.type"></span>
       <br/> <span class="label label-warning label-ext" ng-bind="customParams.res"></span>
       <br/>
       <input type="text" ng-model="sampletext" size="60">
       <br/>
    </div>
</div>

JAVASCRIPT

var angularApp = angular.module('ManagerApp', []);
angularApp.controller('ManagerCtrl', ['$scope', function ($scope) {

$scope.customParams = {};

$scope.updateCustomRequest = function (data, type, res) {
    $scope.customParams.data = data;
    $scope.customParams.type = type;
    $scope.customParams.res = res;
    $scope.sampletext = "input text: " + data;
};



}]);

function ajaxResultPost(data, type, res) {
    var scope = angular.element(document.getElementById("MainWrap")).scope();
    scope.$apply(function () {
    scope.updateCustomRequest(data, type, res);
    });
}

การสาธิต

* ฉันทำการแก้ไขบางอย่างดูต้นฉบับ: ตัวอักษรJSfiddle


2
ขอบคุณ Roger ที่มีประโยชน์มาก ๆ !
Eduardo

การทำงานกับไลบรารี่การตรวจสอบ jQuery ดั้งเดิมที่ต้องใช้ ดังนั้นมันอาจเป็น 1: เขียนไลบรารี่ใหม่ 2: สร้างคำสั่งเพื่อตัด
Michael K

หากฉันไม่ได้ใช้งานฟังก์ชั่นซึ่งจะอัปเดตข้อมูลมุมมองจะไม่แสดง
Monojit Sarkar

13

ทางออก angular.element(document.getElementById('ID')).scope().get()หยุดทำงานสำหรับฉันในเชิงมุม 1.5.2 Sombody พูดถึงในความคิดเห็นว่ามันไม่ทำงานใน 1.4.9 เช่นกัน ฉันแก้ไขมันโดยการเก็บขอบเขตไว้ในตัวแปรโกลบอล:

var scopeHolder;
angular.module('fooApp').controller('appCtrl', function ($scope) {
    $scope = function bar(){
        console.log("foo");        
    };
    scopeHolder = $scope;
})

โทรจากรหัสที่กำหนดเอง:

scopeHolder.bar()

หากคุณต้องการ จำกัด ขอบเขตเฉพาะวิธีนี้ เพื่อลดการรับแสงของขอบเขตทั้งหมด ใช้เทคนิคต่อไปนี้

var scopeHolder;
angular.module('fooApp').controller('appCtrl', function ($scope) {
    $scope.bar = function(){
        console.log("foo");        
    };
    scopeHolder = $scope.bar;
})

โทรจากรหัสที่กำหนดเอง:

scopeHolder()

สิ่งนี้ใช้งานได้ดีสำหรับฉัน (แม้จากภายในส่วนประกอบ) มีข้อเสียในการทำสิ่งนี้นอกเหนือจาก "การฝึกฝนที่ไม่ดีในการเรียกสิ่งที่เป็นมุมฉากจากมุมด้านนอก" ที่ฉันไม่สามารถหลีกเลี่ยงในสถานการณ์ของฉันได้หรือไม่? stackoverflow.com/questions/42123120/…
RichC

ขอบคุณมากสำหรับความช่วยเหลือของคุณฉันกำลังค้นหาวิธีแก้ปัญหานี้จากในขณะที่
Ibrahim Amer

11

คำตอบของ Dmitry ใช้ได้ดี ฉันเพิ่งสร้างตัวอย่างง่ายๆโดยใช้เทคนิคเดียวกัน

jsfiddle: http://jsfiddle.net/o895a8n8/5/

<button onclick="call()">Call Controller's method from outside</button>
<div  id="container" ng-app="" ng-controller="testController">
</div>

.

function call() {
    var scope = angular.element(document.getElementById('container')).scope();
      scope.$apply(function(){
        scope.msg = scope.msg + ' I am the newly addded message from the outside of the controller.';
    })
    alert(scope.returnHello());
}

function testController($scope) {
    $scope.msg = "Hello from a controller method.";
    $scope.returnHello = function() {
        return $scope.msg ; 
    }
}

7

ฉันอยากจะรวมโรงงานเป็นส่วนควบคุมของตัวควบคุมมากกว่าฉีดด้วยบรรทัดของรหัสของตัวเอง: http://jsfiddle.net/XqDxG/550/

myModule.factory('mySharedService', function($rootScope) {
    return sharedService = {thing:"value"};
});

function ControllerZero($scope, mySharedService) {
    $scope.thing = mySharedService.thing;

ControllerZero. $ inject = ['$ scope', 'mySharedService'];


อืมมม @Anton แสดงความคิดเห็นซอด้านล่าง (ในเดือนพฤษภาคม 13) ที่ทั้งสอง
Jesse Chisholm

5

การพิจารณาว่าเมนูของคุณไม่มีขอบเขตที่เกี่ยวข้องเป็นวิธีที่เหมาะสมหรือไม่ มันไม่ใช่วิธีเชิงมุมจริงๆ

แต่ถ้าเป็นวิธีที่คุณต้องไปคุณสามารถทำได้โดยเพิ่มฟังก์ชั่นไปยัง $ rootScope จากนั้นภายในฟังก์ชั่นเหล่านั้นโดยใช้ $ broadcasts เพื่อส่งกิจกรรม คอนโทรลเลอร์ของคุณใช้ $ on เพื่อฟังเหตุการณ์เหล่านั้น

อีกสิ่งที่ควรพิจารณาหากคุณมีเมนูโดยไม่มีขอบเขตคือถ้าคุณมีหลายเส้นทางตัวควบคุมทั้งหมดของคุณจะต้องมี upate และรับฟังก์ชั่นของตัวเอง (นี่ถือว่าคุณมีคอนโทรลเลอร์หลายตัว)


คุณสามารถยกตัวอย่างง่ายๆของวิธีการเรียกใช้ฟังก์ชัน. get () ของ ControllerOne จาก ControllerTwo ได้หรือไม่ ตรรกะของฉันคือดังนั้นผู้ควบคุมแต่ละคนจะมีฟังก์ชั่น. get () .update () ของตัวเอง ฉันจะมี MainMenuController ซึ่งฉันต้องดำเนินการ (ตามรายการเมนู) .get () ของคอนโทรลเลอร์ที่จำเป็น
Pavel Zdarov

ป.ล. ไม่ใช่รหัสของฉัน แต่มันแสดงให้เห็นว่าจะมีฟังก์ชั่นการใช้งานร่วมกันหลายตัวควบคุมได้อย่างไร
Anton

4

ฉันใช้เพื่อทำงานกับ $ http เมื่อต้องการรับข้อมูลบางอย่างจากทรัพยากรฉันทำสิ่งต่อไปนี้:

angular.module('services.value', [])

.service('Value', function($http, $q) {

var URL = "http://localhost:8080/myWeb/rest/";

var valid = false;

return {
    isValid: valid,
    getIsValid: function(callback){
        return $http.get(URL + email+'/'+password, {cache: false})
                    .success(function(data){
            if(data === 'true'){ valid = true; }
        }).then(callback);
    }}
    });

และรหัสในคอนโทรลเลอร์:

angular.module('controllers.value', ['services.value'])

.controller('ValueController', function($scope, Value) {
    $scope.obtainValue = function(){
        Value.getIsValid(function(){$scope.printValue();});
    }

    $scope.printValue = function(){
        console.log("Do it, and value is " Value.isValid);
    }
}

ฉันส่งไปที่บริการฟังก์ชั่นที่ต้องโทรในคอนโทรลเลอร์


3

ฉันมีหลายเส้นทางและคอนโทรลเลอร์หลายตัวดังนั้นฉันจึงไม่สามารถรับคำตอบที่ยอมรับได้ ฉันพบว่าการเพิ่มฟังก์ชั่นในหน้าต่างทำงานได้:

fooModule.controller("fooViewModel", function ($scope, fooService, $http, $q, $routeParams, $window, $location, viewModelHelper, $interval) {
    $scope.initFoo = function () {
        // do angular stuff
    }
    var initialize = function () {
        $scope.initFoo();
    }

    initialize();

    window.fooreinit = initialize;

}

จากนั้นนอกตัวควบคุมสิ่งนี้สามารถทำได้:

function ElsewhereOnThePage() {
    if (typeof(fooreinit) == 'function') { fooreinit(); }
}

0

ฟังก์ชัน Call Angular Scope จากภายนอกคอนโทรลเลอร์

// Simply Use "Body" tag, Don't try/confuse using id/class.

var scope = angular.element('body').scope();             
scope.$apply(function () {scope.YourAngularJSFunction()});      

-1

ฉันเป็นผู้ใช้กรอบ Ionic และสิ่งที่ฉันพบว่าจะให้ขอบเขต $ ของคอนโทรลเลอร์ปัจจุบันอย่างสม่ำเสมอคือ:

angular.element(document.querySelector('ion-view[nav-view="active"]')).scope()

ฉันสงสัยว่าสิ่งนี้สามารถแก้ไขได้เพื่อให้เหมาะสมกับสถานการณ์ส่วนใหญ่โดยไม่คำนึงถึงกรอบงาน (หรือไม่) โดยการค้นหาข้อความค้นหาที่จะกำหนดเป้าหมายองค์ประกอบ DOM เฉพาะที่มีเฉพาะในระหว่างอินสแตนซ์ของตัวควบคุมที่กำหนด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.