ฉันจะฉีดคอนโทรลเลอร์ลงในคอนโทรลเลอร์อื่นใน AngularJS ได้อย่างไร


97

ฉันเพิ่งเริ่มใช้ Angular และพยายามคิดหาวิธีทำสิ่งต่างๆ ...

เมื่อใช้ AngularJS ฉันจะฉีดคอนโทรลเลอร์เพื่อใช้ภายในคอนโทรลเลอร์อื่นได้อย่างไร

ฉันมีตัวอย่างข้อมูลต่อไปนี้:

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

เมื่อฉันดำเนินการสิ่งนี้ฉันได้รับข้อผิดพลาด:

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

ฉันควรพยายามใช้คอนโทรลเลอร์ภายในคอนโทรลเลอร์อื่นหรือไม่หรือฉันควรให้บริการนี้


2
คุณไม่สามารถฉีดตัวควบคุมเข้าด้วยกันได้ ใช่คุณควรเปลี่ยนTestCtrl1เป็นบริการแทน
Sly_cardinal

แน่นอนใช้บริการ
Miguel Mota

3
จะเกิดอะไรขึ้นถ้าฉันต้องอัปเดตคุณสมบัติของคอนโทรลเลอร์ที่เชื่อมโยงกับมุมมอง คุณสมบัตินี้ได้รับผลกระทบจากเหตุการณ์ที่เกิดขึ้นในตัวควบคุมอื่น
Ankit Tanna

คำตอบ:


129

หากคุณตั้งใจที่จะยึดตัวควบคุมอินสแตนซ์ของส่วนประกอบอื่นไว้แล้วและหากคุณกำลังทำตามแนวทางตามองค์ประกอบ / คำสั่งคุณสามารถrequireควบคุม (อินสแตนซ์ของส่วนประกอบ) จากส่วนประกอบอื่นที่เป็นไปตามลำดับชั้นที่กำหนดได้ตลอดเวลา

ตัวอย่างเช่น:

//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.
 },
 ...
});

ตอนนี้การใช้ส่วนประกอบข้างต้นเหล่านี้อาจเป็นดังนี้:

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

มีหลายวิธีที่คุณสามารถตั้งค่ามีความจำเป็นต้องใช้

(ไม่มีคำนำหน้า) - ค้นหาตัวควบคุมที่ต้องการในองค์ประกอบปัจจุบัน โยนข้อผิดพลาดหากไม่พบ

เหรอ? - พยายามค้นหาคอนโทรลเลอร์ที่ต้องการหรือส่งค่า null ไปยังลิงก์ fn หากไม่พบ

^ - ค้นหาตัวควบคุมที่ต้องการโดยค้นหาองค์ประกอบและผู้ปกครอง โยนข้อผิดพลาดหากไม่พบ

^^ - ค้นหาตัวควบคุมที่ต้องการโดยค้นหาผู้ปกครองขององค์ประกอบ โยนข้อผิดพลาดหากไม่พบ

? ^ - พยายามค้นหาคอนโทรลเลอร์ที่ต้องการโดยค้นหาองค์ประกอบและพาเรนต์หรือส่งค่า null ไปยังลิงก์ fn หากไม่พบ

? ^^ - พยายามค้นหาคอนโทรลเลอร์ที่ต้องการโดยค้นหาพาเรนต์ขององค์ประกอบหรือส่งค่า null ไปยังลิงก์ fn หากไม่พบ



คำตอบเก่า:

คุณต้องฉีด$controllerบริการเพื่อสร้างอินสแตนซ์คอนโทรลเลอร์ภายในคอนโทรลเลอร์อื่น แต่โปรดทราบว่าสิ่งนี้อาจนำไปสู่ปัญหาการออกแบบบางอย่าง คุณสามารถสร้างบริการที่ใช้ซ้ำได้ตามความรับผิดชอบเดียวและฉีดเข้าไปในตัวควบคุมตามที่คุณต้องการ

ตัวอย่าง:

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

ไม่ว่าในกรณีใดคุณไม่สามารถโทรได้TestCtrl1.myMethod()เนื่องจากคุณได้แนบเมธอดบน$scopeอินสแตนซ์คอนโทรลเลอร์และไม่ได้แนบ

หากคุณใช้คอนโทรลเลอร์ร่วมกันมันจะดีกว่าเสมอที่จะทำ: -

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

และในขณะที่บริโภคให้ทำ:

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

ในกรณีแรก$scopeคือโมเดลมุมมองของคุณจริงๆและในกรณีที่สองเป็นอินสแตนซ์ของตัวควบคุมเอง


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

ควรvar testCtrl1ViewModel = $scope.$new();จะเป็นvar testCtrl1ViewModel = $rootScope.$new();? อ้างถึง: docs.angularjs.org/guide/controller @PSL
leonsPAPA

ในตัวอย่างด้านบนคุณกำลังเข้าถึงคอนเทนเนอร์บนตัวควบคุมคำสั่ง แต่ฉันไม่สามารถใช้งานได้ ฉันสามารถเข้าถึงตัวควบคุมที่ต้องการผ่านพารามิเตอร์ที่สี่บนฟังก์ชันลิงค์ของฉันบนคำสั่งเอง แต่ไม่ผูกพันกับตัวควบคุมคำสั่งเหมือนในตัวอย่างด้านบน ใครมีปัญหานี้อีกไหม
Sammi

33

ฉันขอแนะนำคำถามที่คุณควรถามคือวิธีการฉีดบริการลงในคอนโทรลเลอร์ บริการไขมันที่มีตัวควบคุมแบบผอมเป็นกฎง่ายๆหรือที่เรียกว่าใช้ตัวควบคุมเพื่อยึดบริการ / โรงงานของคุณ (ด้วยตรรกะทางธุรกิจ) ในมุมมองของคุณ

ตัวควบคุมได้รับขยะที่รวบรวมจากการเปลี่ยนแปลงเส้นทางตัวอย่างเช่นหากคุณใช้ตัวควบคุมเพื่อยึดตรรกะทางธุรกิจที่แสดงค่าคุณจะสูญเสียสถานะในสองหน้าหากผู้ใช้แอปคลิกปุ่มย้อนกลับของเบราว์เซอร์

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

นี่คือตัวอย่างการทำงานของโรงงานที่ฉีดเข้าไปในคอนโทรลเลอร์สองตัว

นอกจากนี้ฉันขอแนะนำให้อ่านบทแนะนำเกี่ยวกับบริการ / โรงงานนี้


13

ไม่จำเป็นต้องนำเข้า / ใส่คอนโทรลเลอร์ของคุณใน JS คุณสามารถฉีดคอนโทรลเลอร์ / คอนโทรลเลอร์ที่ซ้อนกันผ่าน HTML ของคุณมันใช้ได้ผลสำหรับฉัน ชอบ :

<div ng-controller="TestCtrl1">
    <div ng-controller="TestCtrl2">
      <!-- your code--> 
    </div> 
</div>

2
จริง ... แต่ฉันยังรู้สึกดีกว่าที่จะใส่องค์ประกอบทั่วไปทั้งหมดลงในบริการและฉีดบริการไปยังตัวควบคุมที่เกี่ยวข้อง
Neel

-1
<div ng-controller="TestCtrl1">
    <div ng-controller="TestCtrl2">
      <!-- your code--> 
    </div> 
</div>

วิธีนี้ใช้ได้ดีที่สุดในกรณีของฉันโดยที่ TestCtrl2 มีคำสั่งของตัวเอง

var testCtrl2 = $controller('TestCtrl2')

สิ่งนี้ทำให้ฉันมีข้อผิดพลาดที่แจ้งว่ามีข้อผิดพลาดในการฉีด scopeProvider

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

สิ่งนี้ใช้ไม่ได้จริงๆหากคุณมีคำสั่งใน 'TestCtrl1' คำสั่งนั้นมีขอบเขตที่แตกต่างจากที่สร้างขึ้นที่นี่ คุณจะได้สองอินสแตนซ์ของ 'TestCtrl1'


-1

ทางออกที่ดีที่สุด: -

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

// ที่นี่คุณได้รับการเรียกตัวควบคุมครั้งแรกโดยไม่ต้องดำเนินการ


-1

คุณยังสามารถใช้$rootScopeเพื่อเรียกฟังก์ชัน / วิธีการของคอนโทรลเลอร์ตัวที่ 1 จากคอนโทรลเลอร์ตัวที่สองเช่นนี้

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

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

1
Downvote: นี่เป็นเพียงการเข้ารหัสที่ไม่ดี: คุณกำลังทำให้ฟังก์ชันของคุณเป็นสากล ควรวาง Angular โดยสมบูรณ์หากเป็นวิธีที่คุณต้องการเขียนโค้ด ... ใช้บริการตามที่คำตอบอื่น ๆ ส่วนใหญ่แนะนำ
HammerNL

ไม่แนะนำ $ rootScope ทำให้โค้ดงุ่มง่ามและนำไปสู่ปัญหาในระยะยาว
Harshit Pant

-2

ใช้ typescript สำหรับการเข้ารหัสของคุณเนื่องจากเป็นเชิงวัตถุพิมพ์อย่างเคร่งครัดและดูแลรักษารหัสได้ง่าย ...

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ typescipt คลิกที่นี่

นี่คือตัวอย่างง่ายๆที่ฉันสร้างขึ้นเพื่อแบ่งปันข้อมูลระหว่างคอนโทรลเลอร์สองตัวโดยใช้ typescript ...

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

}

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