AngularJS. Cara memanggil fungsi pengontrol dari luar komponen pengontrol

190

Bagaimana saya bisa memanggil fungsi yang didefinisikan di bawah pengontrol dari setiap tempat halaman web (di luar komponen pengontrol)?

Ini berfungsi dengan baik ketika saya menekan tombol "get". Tapi saya perlu menyebutnya dari luar div controller. Logikanya adalah: secara default div saya disembunyikan. Di suatu tempat di menu navigasi saya menekan tombol dan itu akan menunjukkan () div saya dan menjalankan fungsi "get". Bagaimana saya bisa mencapai ini?

Halaman web saya adalah:

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

   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){ }
      });
    };
   }
Pavel Zdarov
sumber
2
Ketika Anda mengatakan "... di suatu tempat di menu navigasi Anda menekan tombol ...", apakah Anda bermaksud mengatakan bahwa navigasi ini adalah bagian dari pengontrol lain dan Anda ingin memanggil get()MyController dari pengontrol lain?
callmekatootie
1
Untuk saat ini menu navigasi bukan pengontrol. Hanya html. Tidak yakin apakah mungkin untuk memanggil fungsi pengontrol dari html / javascript, itu sebabnya saya memposting pertanyaan ini. Tapi ya, logis untuk menjadikan menu navigasi sebagai pengontrol terpisah. Bagaimana saya bisa memanggil fungsi MyController.get () dari NavigationMenu.Controller?
Pavel Zdarov

Jawaban:

331

Berikut adalah cara untuk memanggil fungsi pengontrol dari luarnya:

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

di mana get()fungsi dari controller Anda.

Anda bisa beralih

document.getElementById('yourControllerElementID')` 

untuk

$('#yourControllerElementID')

Jika Anda menggunakan jQuery.

Juga, jika fungsi Anda berarti mengubah apa pun pada Tampilan Anda, Anda harus memanggil

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

untuk menerapkan perubahan.

Satu hal lagi, Anda harus perhatikan adalah bahwa lingkup diinisialisasi setelah halaman dimuat, jadi metode panggilan dari luar ruang lingkup harus selalu dilakukan setelah halaman dimuat. Lain Anda tidak akan sampai ke ruang lingkup sama sekali.

MEMPERBARUI:

Dengan versi sudut terbaru, sebaiknya Anda gunakan

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

Dan ya, ini sebenarnya adalah praktik yang buruk , tetapi kadang-kadang Anda hanya perlu melakukan sesuatu dengan cepat dan kotor.

Dmitry Mina
sumber
30
Ini seperti bau kode karena filosofi Angular bukan untuk mencampurkan kode DOM dengan kode Angular ...
JoeCool
8
jadi jika Anda tidak seharusnya mencampurkan kode DOM dengan kode Angular, jika Anda ingin animasi JQuery tertentu terputus sebagai reaksi terhadap perubahan variabel pada pengontrol Angular Anda - bagaimana tepatnya Anda melakukannya? Akan sepele untuk dilakukan dari controller, tapi saya tidak tahu bagaimana melakukannya dengan bersih
Jan
3
Saya tidak bisa mendapatkan $applybagian untuk bekerja untuk saya, sampai saya membungkus kode menjadi suatu fungsi, misalnya..scope.$apply(function() { scope.get() });
surfitscrollit
2
Saya sebenarnya bisa mengakses controller dengan angular.element(document.getElementById('yourControllerElementID')).scope().controller; For ex. jika digunakan: angular.element(document.getElementById('controller')).scope().get() kesalahan melempar dan tidak terdefinisi, tetapi jika saya menggunakannya angular.element(document.getElementById('controller')).scope().controller.get()berfungsi.
vrunoa
2
Apakah mungkin, bahwa solusi ini tidak berfungsi di Angular 1.4.9 lagi? Saya tidak dapat mengakses scope()di angular.element(...), karena itu kembali tidak terdefinisi dan vardump dari elemen / objek sudut mengatakan bahwa fungsi scopeterletak di dalam __proto__-object.
Smamatti
37

Saya telah menemukan contoh di internet.

Beberapa orang menulis kode ini dan bekerja dengan sempurna

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

Demo

* Saya melakukan beberapa modifikasi, lihat yang asli: font JSfiddle

Roger Ramos
sumber
2
Terima kasih Roger, sangat berguna!
Eduardo
Bekerja dengan pustaka validasi jQuery lawas yang HARUS digunakan. Jadi, ini bisa 1: menulis ulang perpustakaan, 2: buat direktif untuk membungkus perpustakaan, 3: 2 baris kode untuk memanggil pengiriman sudut saat valid ...
Michael K
jika saya tidak menggunakan apply maka fungsi yang akan memperbarui tampilan data tidak akan tercermin?
Monojit Sarkar
13

Solusinya angular.element(document.getElementById('ID')).scope().get()berhenti bekerja untuk saya di sudut 1.5.2. Sombody menyebutkan dalam komentar bahwa ini tidak bekerja di 1.4.9 juga. Saya memperbaikinya dengan menyimpan lingkup dalam variabel global:

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

panggilan dari kode khusus:

scopeHolder.bar()

jika Anda ingin membatasi ruang lingkup hanya metode ini. Untuk meminimalkan paparan seluruh ruang lingkup. gunakan teknik berikut.

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

panggilan dari kode khusus:

scopeHolder()
pengguna1121883
sumber
Ini bekerja sangat baik untuk saya (bahkan dari dalam komponen). Apakah ada kerugian untuk melakukan ini selain dari "praktik buruk untuk memanggil hal-hal sudut dari luar sudut" yang tidak dapat saya hindari dalam skenario saya? stackoverflow.com/questions/42123120/…
RichC
Terima kasih Pak atas bantuan Anda, saya sedang mencari solusi untuk sementara waktu
Ibrahim Amer
11

Jawaban Dmitry bekerja dengan baik. Saya baru saja membuat contoh sederhana menggunakan teknik yang sama.

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 ; 
    }
}
Razan Paul
sumber
7

Saya lebih suka memasukkan pabrik sebagai dependensi pada pengontrol daripada menyuntikkannya dengan baris kode mereka sendiri: 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'];

getsetbro
sumber
Hmm. @Anton berkomentar biola di bawah (pada Mei '13) yang melakukan KEDUA.
Jesse Chisholm
5

Mungkin patut dipertimbangkan jika memiliki menu Anda tanpa ruang lingkup terkait adalah cara yang tepat untuk pergi. Ini bukan cara sudut.

Tetapi, jika itu adalah cara yang Anda butuhkan, maka Anda dapat melakukannya dengan menambahkan fungsi ke $ rootScope dan kemudian dalam fungsi-fungsi tersebut menggunakan $ broadcast untuk mengirim acara. controller Anda kemudian menggunakan $ untuk mendengarkan acara-acara tersebut.

Hal lain yang perlu dipertimbangkan jika Anda akhirnya memiliki menu Anda tanpa ruang lingkup adalah bahwa jika Anda memiliki beberapa rute, maka semua pengendali Anda harus memiliki fungsi sendiri dan fungsi yang baru. (ini dengan asumsi Anda memiliki beberapa pengendali)

Anton
sumber
dapatkah Anda memberikan contoh sederhana bagaimana memanggil fungsi .get () dari ControllerOne dari ControllerTwo? logika saya adalah sehingga setiap pengontrol akan memiliki fungsi .get () .update () mereka sendiri. Saya akan memiliki MainMenuController dari mana saya perlu menjalankan (sesuai dengan item menu) .get () dari kontroler yang diperlukan.
Pavel Zdarov
ps, bukan kode saya, tetapi itu menunjukkan bagaimana memiliki beberapa fungsi berbagi pengontrol
Anton
4

Saya menggunakan untuk bekerja dengan $ http, ketika ingin mendapatkan beberapa informasi dari sumber daya saya melakukan hal berikut:

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

Dan kode di controller:

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

Saya mengirim ke layanan fungsi apa yang harus dipanggil di controller

rodrimmb
sumber
3

Saya memiliki beberapa rute dan beberapa pengendali, jadi saya tidak bisa mendapatkan jawaban yang diterima untuk bekerja. Saya menemukan bahwa menambahkan fungsi ke jendela berfungsi:

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;

}

Kemudian di luar controller, ini bisa dilakukan:

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

Panggil fungsi Angular Scope dari luar controller.

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

var scope = angular.element('body').scope();             
scope.$apply(function () {scope.YourAngularJSFunction()});      
sam ruben
sumber
-1

Saya adalah pengguna kerangka kerja Ionic dan yang saya temukan yang akan secara konsisten memberikan $ lingkup pengontrol saat ini adalah:

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

Saya menduga ini dapat dimodifikasi agar sesuai dengan sebagian besar skenario terlepas dari kerangka (atau tidak) dengan menemukan kueri yang akan menargetkan elemen DOM spesifik (s) yang tersedia hanya selama turunan controller yang diberikan.

Matt Ray
sumber