Bagaimana cara menyuntikkan pengontrol ke pengontrol lain di AngularJS

97

Saya baru mengenal Angular dan mencoba mencari cara untuk melakukan sesuatu ...

Menggunakan AngularJS, bagaimana saya bisa menyuntikkan pengontrol untuk digunakan dalam pengontrol lain?

Saya memiliki cuplikan berikut:

var app = angular.module("testApp", ['']);

app.controller('TestCtrl1', ['$scope', function ($scope) {
    $scope.myMethod = function () {
        console.log("TestCtrl1 - myMethod");
    }
}]);

app.controller('TestCtrl2', ['$scope', 'TestCtrl1', function ($scope, TestCtrl1) {
    TestCtrl1.myMethod();
}]);

Ketika saya menjalankan ini, saya mendapatkan kesalahan:

Error: [$injector:unpr] Unknown provider: TestCtrl1Provider <- TestCtrl1
http://errors.angularjs.org/1.2.21/$injector/unpr?p0=TestCtrl1Provider%20%3C-%20TestCtrl1

Haruskah saya mencoba menggunakan pengontrol di dalam pengontrol lain, atau haruskah saya menjadikan ini layanan?

Scottie
sumber
2
Anda tidak dapat menyuntikkan pengontrol satu sama lain. Ya, Anda harus beralih TestCtrl1ke layanan.
Sly_cardinal
Tepatnya, gunakan layanan
Miguel Mota
3
bagaimana jika saya harus memperbarui properti pengontrol yang mengikat tampilan. Properti ini dipengaruhi oleh peristiwa yang terjadi di pengontrol lain.
Ankit Tanna

Jawaban:

129

Jika maksud Anda adalah untuk mendapatkan kontroler yang sudah dipakai dari komponen lain dan jika Anda mengikuti pendekatan berbasis komponen / direktif, Anda selalu dapat requiremengontrol (turunan komponen) dari komponen lain yang mengikuti hierarki tertentu.

Sebagai contoh:

//some container component that provides a wizard and transcludes the page components displayed in a wizard
myModule.component('wizardContainer', {
  ...,
  controller : function WizardController() {
    this.disableNext = function() { 
      //disable next step... some implementation to disable the next button hosted by the wizard
    }
  },
  ...
});

//some child component
myModule.component('onboardingStep', {
 ...,
 controller : function OnboadingStepController(){

    this.$onInit = function() {
      //.... you can access this.container.disableNext() function
    }

    this.onChange = function(val) {
      //..say some value has been changed and it is not valid i do not want wizard to enable next button so i call container's disable method i.e
      if(notIsValid(val)){
        this.container.disableNext();
      }
    }
 },
 ...,
 require : {
    container: '^^wizardContainer' //Require a wizard component's controller which exist in its parent hierarchy.
 },
 ...
});

Sekarang penggunaan komponen di atas mungkin seperti ini:

<wizard-container ....>
<!--some stuff-->
...
<!-- some where there is this page that displays initial step via child component -->

<on-boarding-step ...>
 <!--- some stuff-->
</on-boarding-step>
...
<!--some stuff-->
</wizard-container>

Ada banyak cara untuk mengatur keperluan .

(tanpa awalan) - Temukan pengontrol yang diperlukan pada elemen saat ini. Lempar kesalahan jika tidak ditemukan.

? - Mencoba menemukan pengontrol yang diperlukan atau meneruskan null ke tautan fn jika tidak ditemukan.

^ - Temukan pengontrol yang diperlukan dengan mencari elemen dan induknya. Lempar kesalahan jika tidak ditemukan.

^^ - Temukan pengontrol yang diperlukan dengan mencari orang tua elemen. Lempar kesalahan jika tidak ditemukan.

? ^ - Mencoba menemukan pengontrol yang diperlukan dengan mencari elemen dan induknya atau meneruskan null ke link fn jika tidak ditemukan.

? ^^ - Mencoba menemukan pengontrol yang diperlukan dengan mencari orang tua elemen, atau meneruskan null ke link fn jika tidak ditemukan.



Jawaban Lama:

Anda perlu memasukkan $controllerlayanan untuk membuat instance pengontrol di dalam pengontrol lain. Namun ketahuilah bahwa ini mungkin menyebabkan beberapa masalah desain. Anda selalu dapat membuat layanan yang dapat digunakan kembali yang mengikuti Tanggung Jawab Tunggal dan memasukkannya ke dalam pengontrol sesuai kebutuhan.

Contoh:

app.controller('TestCtrl2', ['$scope', '$controller', function ($scope, $controller) {
   var testCtrl1ViewModel = $scope.$new(); //You need to supply a scope while instantiating.
   //Provide the scope, you can also do $scope.$new(true) in order to create an isolated scope.
   //In this case it is the child scope of this scope.
   $controller('TestCtrl1',{$scope : testCtrl1ViewModel });
   testCtrl1ViewModel.myMethod(); //And call the method on the newScope.
}]);

Bagaimanapun, Anda tidak dapat memanggil TestCtrl1.myMethod()karena Anda telah memasang metode pada $scopedan bukan pada contoh pengontrol.

Jika Anda berbagi pengontrol, maka akan selalu lebih baik untuk melakukan: -

.controller('TestCtrl1', ['$log', function ($log) {
    this.myMethod = function () {
        $log.debug("TestCtrl1 - myMethod");
    }
}]);

dan saat mengkonsumsi lakukan:

.controller('TestCtrl2', ['$scope', '$controller', function ($scope, $controller) {
     var testCtrl1ViewModel = $controller('TestCtrl1');
     testCtrl1ViewModel.myMethod();
}]);

Dalam kasus pertama $scopeadalah model tampilan Anda, dan dalam kasus kedua adalah contoh pengontrol itu sendiri.

PSL
sumber
4
Dan Itu tergantung pada fungsionalitas yang disediakan oleh pengontrol, Jika Anda membuatnya lebih seperti model tampilan yang perlu Anda bagikan ke seluruh komponen, itu bagus tetapi jika itu lebih dari fungsionalitas penyedia layanan maka saya hanya akan membuat layanan .
PSL
Harus var testCtrl1ViewModel = $scope.$new();menjadi var testCtrl1ViewModel = $rootScope.$new();? lihat: docs.angularjs.org/guide/controller @PSL
leonsPAPA
Dalam contoh di atas, Anda mengakses container di pengontrol direktif, tetapi saya tidak bisa menjalankannya. Saya dapat mengakses pengontrol yang diperlukan melalui parameter keempat pada fungsi tautan saya pada direktif itu sendiri. Tetapi mereka tidak terikat pada pengontrol direktif seperti pada contoh di atas. Ada lagi yang mengalami masalah ini?
Sammi
33

Saya menyarankan pertanyaan yang harus Anda tanyakan adalah bagaimana menyuntikkan layanan ke dalam pengontrol. Layanan gemuk dengan pengontrol kurus adalah aturan praktis yang baik, alias cukup gunakan pengontrol untuk merekatkan layanan / pabrik Anda (dengan logika bisnis) ke dalam tampilan Anda.

Pengontrol mendapatkan sampah yang dikumpulkan pada perubahan rute, jadi misalnya, jika Anda menggunakan pengontrol untuk menahan logika bisnis yang memberikan nilai, Anda akan kehilangan status pada dua halaman jika pengguna aplikasi mengklik tombol kembali browser.

var app = angular.module("testApp", ['']);

app.factory('methodFactory', function () {
    return { myMethod: function () {
            console.log("methodFactory - myMethod");
    };
};

app.controller('TestCtrl1', ['$scope', 'methodFactory', function ($scope,methodFactory) {  //Comma was missing here.Now it is corrected.
    $scope.mymethod1 = methodFactory.myMethod();
}]);

app.controller('TestCtrl2', ['$scope', 'methodFactory', function ($scope, methodFactory) {
    $scope.mymethod2 = methodFactory.myMethod();
}]);

Ini adalah pekerjaan demo pabrik yang disuntikkan ke dalam dua pengontrol

Juga, saya sarankan membaca tutorial ini tentang layanan / pabrik.

bajingan
sumber
13

Tidak perlu mengimpor / menyuntikkan pengontrol Anda di JS. Anda cukup menyuntikkan pengontrol / pengontrol bersarang Anda melalui HTML. Ini berhasil untuk saya. Suka :

<div ng-controller="TestCtrl1">
    <div ng-controller="TestCtrl2">
      <!-- your code--> 
    </div> 
</div>
chetanpawar
sumber
2
benar ... tetapi saya masih merasa lebih baik untuk memasukkan semua elemen umum ke dalam layanan dan menyuntikkan layanan ke pengontrol masing-masing.
Neel
-1
<div ng-controller="TestCtrl1">
    <div ng-controller="TestCtrl2">
      <!-- your code--> 
    </div> 
</div>

Ini bekerja paling baik dalam kasus saya, di mana TestCtrl2 memiliki arahannya sendiri.

var testCtrl2 = $controller('TestCtrl2')

Ini memberi saya kesalahan yang mengatakan kesalahan injeksi scopeProvider.

   var testCtrl1ViewModel = $scope.$new();
   $controller('TestCtrl1',{$scope : testCtrl1ViewModel });
   testCtrl1ViewModel.myMethod(); 

Ini tidak benar-benar berfungsi jika Anda memiliki arahan di 'TestCtrl1', arahan itu sebenarnya memiliki cakupan yang berbeda dari yang dibuat di sini. Anda berakhir dengan dua contoh 'TestCtrl1'.

binRAIN
sumber
-1

Solusi terbaik:-

angular.module("myapp").controller("frstCtrl",function($scope){$scope.name="Atul Singh";}).controller("secondCtrl",function($scope){angular.extend(this, $controller('frstCtrl', {$scope:$scope}));console.log($scope);})

// Di sini Anda mendapat panggilan kontroler pertama tanpa menjalankannya

Atul Singh
sumber
-1

Anda juga dapat menggunakan $rootScopeuntuk memanggil fungsi / metode pengontrol pertama dari pengontrol kedua seperti ini,

.controller('ctrl1', function($rootScope, $scope) {
     $rootScope.methodOf2ndCtrl();
     //Your code here. 
})

.controller('ctrl2', function($rootScope, $scope) {
     $rootScope.methodOf2ndCtrl = function() {
     //Your code here. 
}
})
pengguna5943763
sumber
1
Tidak suka: Ini hanya pengkodean yang buruk: Anda baru saja menjadikan fungsi Anda global. Lebih baik hapus Angular sepenuhnya jika ini adalah cara Anda ingin membuat kode ... Gunakan layanan seperti yang disarankan oleh sebagian besar jawaban lain.
HammerNL
Ini tidak disarankan. $ rootScope membuat kode menjadi kikuk dan menyebabkan masalah dalam jangka panjang.
Harshit Pant
-2

gunakan skrip ketikan untuk pengkodean Anda, karena berorientasi objek, diketik dengan ketat, dan mudah memelihara kodenya ...

untuk info lebih lanjut tentang typescipt klik di sini

Di sini satu contoh sederhana yang telah saya buat untuk berbagi data antara dua pengontrol menggunakan Skrip Ketik ...

module Demo {
//create only one module for single Applicaiton
angular.module('app', []);
//Create a searvie to share the data
export class CommonService {
    sharedData: any;
    constructor() {
        this.sharedData = "send this data to Controller";
    }
}
//add Service to module app
angular.module('app').service('CommonService', CommonService);

//Create One controller for one purpose
export class FirstController {
    dataInCtrl1: any;
    //Don't forget to inject service to access data from service
    static $inject = ['CommonService']
    constructor(private commonService: CommonService) { }
    public getDataFromService() {
        this.dataInCtrl1 = this.commonService.sharedData;
    }
}
//add controller to module app
angular.module('app').controller('FirstController', FirstController);
export class SecondController {
    dataInCtrl2: any;
    static $inject = ['CommonService']
    constructor(private commonService: CommonService) { }
    public getDataFromService() {
        this.dataInCtrl2 = this.commonService.sharedData;
    }
}
angular.module('app').controller('SecondController', SecondController);

}

UniCoder
sumber