Apa cara yang disarankan untuk memperluas pengontrol AngularJS?

193

Saya memiliki tiga pengontrol yang sangat mirip. Saya ingin memiliki pengontrol yang diperluas ketiga dan fungsinya.

vladexologija
sumber

Jawaban:

302

Mungkin Anda tidak memperpanjang pengontrol tetapi dimungkinkan untuk memperluas pengontrol atau membuat pengontrol tunggal sebagai campuran dari beberapa pengontrol.

module.controller('CtrlImplAdvanced', ['$scope', '$controller', function ($scope, $controller) {
    // Initialize the super class and extend it.
    angular.extend(this, $controller('CtrlImpl', {$scope: $scope}));
     Additional extensions to create a mixin.
}]);

Ketika pengendali induk dibuat, logika yang terkandung di dalamnya juga dieksekusi. Lihat $ controller () untuk informasi lebih lanjut tentang tetapi hanya $scopenilai yang harus dilewati. Semua nilai lain akan disuntikkan secara normal.

@ mwarren , kekhawatiran Anda diatasi secara otomatis dengan injeksi ketergantungan sudut. Yang Anda butuhkan adalah menyuntikkan $ scope, meskipun Anda bisa mengganti nilai yang disuntikkan lainnya jika diinginkan. Ambil contoh berikut:

(function(angular) {

	var module = angular.module('stackoverflow.example',[]);

	module.controller('simpleController', function($scope, $document) {
		this.getOrigin = function() {
			return $document[0].location.origin;
		};
	});

	module.controller('complexController', function($scope, $controller) {
		angular.extend(this, $controller('simpleController', {$scope: $scope}));
	});

})(angular);
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.15/angular.js"></script>

<div ng-app="stackoverflow.example">
    <div ng-controller="complexController as C">
        <span><b>Origin from Controller:</b> {{C.getOrigin()}}</span>
    </div>
</div>

Meskipun $ document tidak dimasukkan ke dalam 'simpleController' ketika itu dibuat oleh 'complexController' $ document disuntikkan untuk kita.

Enzey
sumber
1
Sejauh ini solusi tercepat, terbersih, dan termudah untuk ini! Terima kasih!
MK
solusi sempurna, luar biasa!
Kamil Lach
8
Saya pikir Anda tidak memerlukan itu $.extend(), Anda cukup menelepon$controller('CtrlImpl', {$scope: $scope});
tomraithel
5
@tomraithel tidak menggunakan angular.extend(atau $.extend) sebenarnya berarti memperluas $scopesatu - satunya, tetapi jika pengendali basis Anda juga mendefinisikan beberapa properti (misalnya this.myVar=5), Anda hanya memiliki akses ke this.myVardalam pengontrol perpanjangan ketika menggunakanangular.extend
schellmax
1
Ini bagus! Pastikan untuk mengingat untuk memperluas semua fungsi yang diperlukan, yaitu saya punya sesuatu seperti: handleSubmitClickyang akan memanggil handleLoginyang pada gilirannya memiliki loginSuccessdan loginFail. Jadi, dalam controller diperpanjang saya kemudian harus membebani handleSubmitClick, handleLogindan loginSucessuntuk yang benar loginSuccessfungsi yang akan digunakan.
Justin Kruse
52

Untuk warisan Anda dapat menggunakan pola warisan JavaScript standar. Berikut ini adalah demo yang digunakan$injector

function Parent($scope) {
  $scope.name = 'Human';
  $scope.clickParent = function() {
    $scope.name = 'Clicked from base controller';
  }    
}

function Child($scope, $injector) {
  $injector.invoke(Parent, this, {$scope: $scope});
  $scope.name = 'Human Child';
  $scope.clickChild = function(){
    $scope.clickParent();
  }       
}

Child.prototype = Object.create(Parent.prototype);

Jika Anda menggunakan controllerAssintaks (yang sangat saya rekomendasikan), akan lebih mudah untuk menggunakan pola pewarisan klasik:

function BaseCtrl() {
  this.name = 'foobar';
}
BaseCtrl.prototype.parentMethod = function () {
  //body
};

function ChildCtrl() {
  BaseCtrl.call(this);
  this.name = 'baz';
}
ChildCtrl.prototype = Object.create(BaseCtrl.prototype);
ChildCtrl.prototype.childMethod = function () {
  this.parentMethod();
  //body
};

app.controller('BaseCtrl', BaseCtrl);
app.controller('ChildCtrl', ChildCtrl);

Cara lain bisa dengan membuat fungsi konstruktor "abstrak" yang akan menjadi basis controller Anda:

function BaseController() {
  this.click = function () {
    //some actions here
  };
}

module.controller('ChildCtrl', ['$scope', function ($scope) {
  BaseController.call($scope);
  $scope.anotherClick = function () {
    //other actions
  };
}]);

Posting blog tentang topik ini

Minko Gechev
sumber
16

Yah, saya tidak begitu yakin apa yang ingin Anda capai, tetapi biasanya Layanan adalah cara untuk pergi. Anda juga dapat menggunakan karakteristik pewarisan Lingkup Angular untuk berbagi kode antara pengontrol:

<body ng-controller="ParentCtrl">
 <div ng-controller="FirstChildCtrl"></div>
 <div ng-controller="SecondChildCtrl"></div>
</body>

function ParentCtrl($scope) {
 $scope.fx = function() {
   alert("Hello World");
 });
}

function FirstChildCtrl($scope) {
  // $scope.fx() is available here
}

function SecondChildCtrl($scope) {
  // $scope.fx() is available here
}
Andre Goncalves
sumber
Bagikan variabel dan fungsi yang sama di seluruh pengontrol yang melakukan hal serupa (satu untuk mengedit, lainnya membuat dll ...). Ini jelas salah satu solusinya ...
vladexologija
1
Warisan lingkup $ sejauh ini adalah cara terbaik untuk melakukannya, jauh lebih baik daripada jawaban yang diterima.
Snez
Pada akhirnya ini sepertinya cara yang paling sudut untuk pergi. Saya memiliki tiga parentControllers yang sangat singkat pada tiga halaman berbeda yang hanya menetapkan nilai $ scope yang dapat diambil oleh childController. ChildController, yang awalnya saya pikirkan tentang perluasan, berisi semua logika pengontrol.
mwarren
tidak akan $scope.$parent.fx( ) menjadi cara yang jauh lebih bersih untuk melakukannya, karena di situlah sebenarnya didefinisikan?
Akash
15

Anda tidak memperpanjang pengontrol. Jika mereka melakukan fungsi dasar yang sama maka fungsi-fungsi tersebut perlu dipindahkan ke layanan. Layanan itu dapat disuntikkan ke pengendali Anda.

Bart
sumber
3
Terima kasih, tetapi saya memiliki 4 fungsi yang sudah menggunakan layanan (simpan, hapus, dll.) Dan yang sama ada di ketiga pengontrol. Jika perpanjangan bukan pilihan, apakah ada kemungkinan untuk 'mixin'?
vladexologija
4
@vladexologija Saya setuju dengan Bart di sini. Saya pikir layanannya campuran. Anda harus berusaha untuk memindahkan logika sebanyak mungkin dari pengontrol (ke layanan). Jadi, jika Anda memiliki 3 pengontrol yang perlu melakukan tugas serupa maka layanan tampaknya menjadi pendekatan yang tepat untuk diambil. Memperluas pengendali tidak terasa alami di Angular.
ganaraj
6
@vladexologija Ini adalah contoh dari apa yang saya maksud: jsfiddle.net/ERGU3 Ini sangat mendasar tetapi Anda akan mendapatkan idenya.
Bart
3
Jawabannya sangat tidak berguna tanpa argumen dan penjelasan lebih lanjut. Saya juga berpikir bahwa Anda agak kehilangan poin OP. Sudah ada layanan bersama. Satu-satunya hal yang Anda lakukan adalah langsung mengekspos layanan itu. Saya tidak tahu apakah itu ide yang bagus. Pendekatan Anda juga gagal jika akses ke ruang lingkup diperlukan. Tetapi dengan mengikuti alasan Anda, saya akan secara eksplisit mengekspos lingkup sebagai properti dari cakupan sehingga dapat diajukan sebagai argumen.
oliver yang lebih baik
6
Contoh klasik adalah ketika Anda memiliki dua laporan yang digerakkan oleh formulir di situs Anda, yang masing-masing bergantung pada banyak data yang sama, dan masing-masing menggunakan banyak layanan bersama. Anda secara teoritis dapat mencoba dan menempatkan semua layanan terpisah Anda ke dalam satu layanan besar dengan lusinan panggilan AJAX, dan kemudian memiliki metode publik seperti 'getEverythingINeedForReport1' dan 'getEverythingINeedForReport2', dan mengatur semuanya ke satu objek ruang lingkup raksasa, tapi kemudian Anda benar-benar menempatkan apa yang pada dasarnya kontroler logika ke dalam layanan Anda. Memperluas pengendali benar-benar memiliki kasus penggunaan dalam beberapa keadaan.
tobylaroni
10

Solusi lain yang baik diambil dari artikel ini :

// base controller containing common functions for add/edit controllers
module.controller('Diary.BaseAddEditController', function ($scope, SomeService) {
    $scope.diaryEntry = {};

    $scope.saveDiaryEntry = function () {
        SomeService.SaveDiaryEntry($scope.diaryEntry);
    };

    // add any other shared functionality here.
}])

module.controller('Diary.AddDiaryController', function ($scope, $controller) {
    // instantiate base controller
    $controller('Diary.BaseAddEditController', { $scope: $scope });
}])

module.controller('Diary.EditDiaryController', function ($scope, $routeParams, DiaryService, $controller) {
    // instantiate base controller
    $controller('Diary.BaseAddEditController', { $scope: $scope });

    DiaryService.GetDiaryEntry($routeParams.id).success(function (data) {
        $scope.diaryEntry = data;
    });
}]);
Nikita Koksharov
sumber
1
Ini bekerja sangat baik untuk saya. Ini memiliki keuntungan dari refactoring yang mudah dari situasi di mana Anda mulai dengan satu controller, membuat yang lain sangat mirip, dan kemudian ingin membuat kode KERING. Anda tidak perlu mengubah kode, cukup tarik dan lakukan.
Eli Albert
7

Anda dapat membuat layanan dan mewarisi perilakunya di pengontrol apa pun hanya dengan menyuntikkannya.

app.service("reusableCode", function() {

    var reusableCode = {};

    reusableCode.commonMethod = function() {
        alert('Hello, World!');
    };

    return reusableCode;
});

Kemudian di controller Anda yang ingin Anda rentangkan dari layanan reusableCode di atas:

app.controller('MainCtrl', function($scope, reusableCode) {

    angular.extend($scope, reusableCode);

    // now you can access all the properties of reusableCode in this $scope
    $scope.commonMethod()

});

DEMO PLUNKER: http://plnkr.co/edit/EQtj6I0X08xprE8D0n5b?p=preview

Raghavendra
sumber
5

Anda dapat mencoba sesuatu seperti ini (belum diuji):

function baseController(callback){
    return function($scope){
        $scope.baseMethod = function(){
            console.log('base method');
        }
        callback.apply(this, arguments);
    }
}

app.controller('childController', baseController(function(){

}));
karaxuna
sumber
1
Ya. Tidak perlu memperpanjang, cukup gunakan konteksnya
TaylorMac
4

Anda dapat memperluas dengan layanan , pabrik , atau penyedia . mereka sama tetapi dengan tingkat fleksibilitas yang berbeda.

di sini contoh menggunakan pabrik: http://jsfiddle.net/aaaflyvw/6KVtj/2/

angular.module('myApp',[])

.factory('myFactory', function() {
    var myFactory = {
        save: function () {
            // saving ...
        },
        store: function () {
            // storing ...
        }
    };
    return myFactory;
})

.controller('myController', function($scope, myFactory) {
    $scope.myFactory = myFactory;
    myFactory.save(); // here you can use the save function
});

Dan di sini Anda dapat menggunakan fungsi toko juga:

<div ng-controller="myController">
    <input ng-blur="myFactory.store()" />
</div>
aaafly
sumber
4

Anda dapat langsung menggunakan contoh $ controller ('ParentController', {$ scope: $ scope})

module.controller('Parent', ['$scope', function ($scope) {
    //code
}])

module.controller('CtrlImplAdvanced', ['$scope', '$controller', function ($scope, $controller) {
    //extend parent controller
    $controller('CtrlImpl', {$scope: $scope});
}]);
Anand Gargate
sumber
1

Saya menulis fungsi untuk melakukan ini:

function extendController(baseController, extension) {
    return [
        '$scope', '$injector',
        function($scope, $injector) {
            $injector.invoke(baseController, this, { $scope: $scope });
            $injector.invoke(extension, this, { $scope: $scope });
        }
    ]
}

Anda bisa menggunakannya seperti ini:

function() {
    var BaseController = [
        '$scope', '$http', // etc.
        function($scope, $http, // etc.
            $scope.myFunction = function() {
                //
            }

            // etc.
        }
    ];

    app.controller('myController',
        extendController(BaseController,
            ['$scope', '$filter', // etc.
            function($scope, $filter /* etc. */)
                $scope.myOtherFunction = function() {
                    //
                }

                // etc.
            }]
        )
    );
}();

Pro:

  1. Anda tidak harus mendaftarkan pengontrol dasar.
  2. Tak satu pun dari pengontrol yang perlu tahu tentang layanan $ controller atau $ injector.
  3. Ini bekerja dengan baik dengan sintaks injeksi array angular - yang sangat penting jika javascript Anda akan diperkecil.
  4. Anda dapat dengan mudah menambahkan layanan injeksi tambahan ke pengontrol dasar, tanpa harus ingat untuk menambahkannya, dan meneruskannya dari, semua pengontrol anak Anda.

Cons:

  1. Pengontrol dasar harus didefinisikan sebagai variabel, yang berisiko mencemari ruang lingkup global. Saya telah menghindari ini dalam contoh penggunaan saya dengan membungkus segala sesuatu dalam fungsi self-executing anonim, tetapi ini berarti bahwa semua pengontrol anak harus dinyatakan dalam file yang sama.
  2. Pola ini bekerja dengan baik untuk pengontrol yang dibuat langsung dari html Anda, tetapi tidak begitu baik untuk pengontrol yang Anda buat dari kode Anda melalui layanan $ controller (), karena itu bergantung pada injektor mencegah Anda dari langsung menyuntikkan tambahan, non -Layanan parameter dari kode panggilan Anda.
Dan King
sumber
1

Saya menganggap memperluas pengontrol sebagai praktik buruk. Alih-alih menempatkan logika Anda bersama ke dalam layanan. Objek yang diperluas dalam javascript cenderung menjadi agak rumit. Jika Anda ingin menggunakan warisan, saya akan merekomendasikan naskah. Namun, pengontrol tipis adalah cara yang lebih baik untuk masuk ke sudut pandang saya.

Brecht Billiet
sumber