AngularJS denetleyicilerini genişletmek için önerilen yol nedir?


193

Benzer olan üç kontrolcüm var. Bu üçünün işlevlerini genişletip paylaştığı bir denetleyiciye sahip olmak istiyorum.

Yanıtlar:


302

Belki sen bir denetleyici uzatmaz ama bir denetleyici uzatmak veya tek bir kontrolör birden denetleyicilerin bir mixin yapmak mümkündür.

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

Üst denetleyici oluşturulduğunda, içindeki mantık da yürütülür. Hakkında daha fazla bilgi için $ controller () işlevine bakın, ancak yalnızca $scopedeğerin geçirilmesi gerekir. Diğer tüm değerler normal şekilde enjekte edilecektir.

mwarren , endişeniz açısal bağımlılık enjeksiyonu ile otomatik olarak halledilir. İhtiyacınız olan tek şey $ kapsamı enjekte etmek, ancak istenirse diğer enjekte edilen değerleri geçersiz kılabilirsiniz. Aşağıdaki örneği alın:

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

Her ne kadar 'complexController' tarafından yaratıldığında $ document 'simpleController' a aktarılmasa da, $ document bizim için enjekte edilir.


1
Bunun için en hızlı, en temiz ve en kolay çözüm! Teşekkürler!
MK

mükemmel, harika bir çözüm!
Kamil Lach

8
Sanırım buna ihtiyacınız yok $.extend(), sadece arayabilirsiniz$controller('CtrlImpl', {$scope: $scope});
tomraithel

5
@tomraithel kullanmamak angular.extend(veya $.extend) aslında $scopesadece genişletmek anlamına gelir , ancak temel denetleyiciniz bazı özellikleri de tanımlıyorsa (örn. this.myVar=5), yalnızca this.myVargenişletme denetleyicisine erişiminiz olurangular.extend
schellmax 23:14

1
Bu harika! Sadece gerekli tüm fonksiyonları uzatmayı hatırladığınızdan emin olun, yani bir şey vardı: sırayla hangi ve ve vardı handleSubmitClickdiyebiliriz . Yani, benim genişletilmiş denetleyicisi Sonra aşırı zorunda , ve doğru için işleve kullanılacak. handleLoginloginSuccessloginFailhandleSubmitClickhandleLoginloginSucessloginSuccess
Justin Kruse

52

Kalıtım için standart JavaScript kalıtım kalıplarını kullanabilirsiniz. İşte bir demo$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);

controllerAsSözdizimini kullanmanız durumunda (ki kesinlikle tavsiye ederim), klasik kalıtım modelini kullanmak daha da kolaydır:

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

Başka bir yol, temel denetleyiciniz olacak sadece "soyut" yapıcı işlevi oluşturmak olabilir:

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

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

Bu konuyla ilgili blog yazısı


16

Neyi başarmak istediğinizden tam olarak emin değilim, ancak genellikle Hizmetler gitmenin yolu. Denetleyicileri arasında kodu paylaşmak için Açısal'ın Kapsam miras özelliklerini de kullanabilirsiniz:

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

Aynı değişkenleri ve işlevleri benzer şeyler yapan denetleyiciler arasında paylaşın (biri düzenleme, diğeri oluşturma vb. İçindir). Bu kesinlikle çözümlerden biridir ...
vladexologija

1
$ scope miras bunu yapmanın en iyi yolu, kabul edilen cevaptan çok daha iyi.
snez

Sonunda bu en açısal yol gibi görünüyordu. Ben sadece childController alabilir bir $ kapsam değeri ayarlamak üç farklı sayfada üç çok kısa parentControllers var. Başlangıçta genişletmeyi düşündüğüm childController, tüm denetleyici mantığını içerir.
mwarren

$scope.$parent.fx( ) gerçekte tanımlandığı yer olduğu için bunu yapmanın daha temiz bir yolu olmaz mıydı ?
Akash,

15

Denetleyicileri genişletmezsiniz. Aynı temel işlevleri yerine getirirlerse, bu işlevlerin bir hizmete taşınması gerekir. Bu hizmet kontrolörlerinize enjekte edilebilir.


3
Teşekkürler, ancak zaten hizmetleri (kaydetme, silme, vb.) Kullanan ve aynı olan her üç denetleyicide bulunan 4 işlevim var. Uzatma bir seçenek değilse, bir 'karıştırma' olasılığı var mı?
vladexologija

4
@vladexologija Bart'a burada katılıyorum. Bence hizmetler mixin. Mantığın çoğunu denetleyiciden olabildiğince uzak tutmaya çalışmalısınız (hizmetlere). Benzer görevleri yapmanız gereken 3 denetleyiciniz varsa, o zaman bir hizmet almak için doğru yaklaşım gibi görünüyor. Açısal denetleyicileri genişletmek Angular'da doğal hissetmez.
ganaraj

6
@vladexologija İşte ne demek istediğimin bir örneği: jsfiddle.net/ERGU3 Çok basit ama bir fikir edineceksiniz .
Bart

3
Cevap, herhangi bir tartışma ve açıklama olmadan oldukça işe yaramaz. Ben de OP'nin noktasını biraz özlediğinizi düşünüyorum. Zaten paylaşılan bir hizmet var. Yaptığınız tek şey bu hizmeti doğrudan ortaya koymaktır. Bunun iyi bir fikir olup olmadığını bilmiyorum. Kapsama erişim gerekiyorsa yaklaşımınız da başarısız olur. Ancak, akıl yürütmenizi takiben, kapsamı, bir argüman olarak iletilebilmesi için kapsamın bir özelliği olarak açıkça ortaya koyacağım.
daha iyi bir oliver

6
Klasik bir örnek, sitenizde her biri aynı verilere bağlı olan ve her biri çok sayıda paylaşılan hizmet kullanan iki forma dayalı raporunuz olduğunda olabilir. Teorik olarak tüm ayrı hizmetlerinizi düzinelerce AJAX çağrısı ile büyük bir hizmete koyabilir ve ardından 'getEverythingINeedForReport1' ve 'getEverythingINeedForReport2' gibi genel yöntemlere sahip olabilirsiniz ve hepsini tek bir mamut kapsam nesnesine ayarlayabilirsiniz. gerçekten kontrolör mantığını hizmetinize koyuyor. Bazı durumlarda kontrolörlerin genişletilmesinin kesinlikle bir kullanım durumu vardır.
tobylaroni

10

Bu makaleden alınan başka bir iyi çözüm :

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

1
Bu benim için çok iyi çalıştı. Bir denetleyiciyle başladığınız, çok benzer başka bir denetleyici oluşturduğunuz ve daha sonra DRYer kodunu yapmak istediğiniz bir durumdan kolayca yeniden düzenleme avantajına sahiptir. Kodu değiştirmeniz gerekmez, sadece dışarı çekin ve tamamlayın.
Eli Albert

7

Herhangi bir denetleyicide bir hizmet oluşturabilir ve davranışını yalnızca enjekte ederek devralın.

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

    var reusableCode = {};

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

    return reusableCode;
});

Daha sonra yukarıdaki reusableCode hizmetinden uzatmak istediğiniz kontrol cihazınızda:

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


5

Böyle bir şey deneyebilirsiniz (test etmediniz):

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

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

}));

1
Evet. Uzatmaya gerek yok, sadece bağlamla çağırın
TaylorMac

4

Bir hizmet , fabrika veya sağlayıcılarla genişletebilirsiniz . aynı ama farklı esneklik derecelerine sahipler.

Burada fabrika kullanan bir örnek: 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
});

Ve burada mağaza işlevini de kullanabilirsiniz:

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

4

Doğrudan $ controller ('ParentController', {$ scope: $ scope}) kullanabilirsiniz. Örnek

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

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


1

Bunu yapmak için bir işlev yazdım:

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

Bu şekilde kullanabilirsiniz:

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

Artıları:

  1. Temel denetleyiciyi kaydetmeniz gerekmez.
  2. Hiçbir denetleyicinin $ controller veya $ injector servisleri hakkında bilgi sahibi olması gerekmez.
  3. Javascript küçültülecekse, açısal dizisi enjeksiyon sözdizimi ile iyi çalışır.
  4. Kolayca ana kontrolöre ekstra enjektabl hizmetlerini ekleyebilir olmadan da eklemek için hatırlamak zorunda ve gelen aracılığıyla çocuğunuz denetleyicilerin hepsini geçmektedir.

Eksileri:

  1. Temel kontrolör, küresel kapsamı kirletme riski taşıyan bir değişken olarak tanımlanmalıdır. Kullanım örneğimde her şeyi anonim kendi kendine yürütme işlevine sararak bundan kaçındım, ancak bu, tüm çocuk denetleyicilerinin aynı dosyada bildirilmesi gerektiği anlamına geliyor.
  2. Bu desen doğrudan html'nizden başlatılan denetleyiciler için iyi çalışır, ancak kodunuzdan $ controller () hizmeti aracılığıyla oluşturduğunuz denetleyiciler için o kadar iyi değildir, çünkü enjektöre bağımlı olması, doğrudan ekstra, enjekte etmenizi önler. Arama kodunuzdan -servis parametreleri.

1

Denetleyicileri genişletmeyi kötü uygulama olarak görüyorum. Bunun yerine paylaşılan mantığınızı bir hizmete koyun. Javascript'teki genişletilmiş nesneler oldukça karmaşık olma eğilimindedir. Kalıtım kullanmak istiyorsanız, daktilo yazmanızı tavsiye ederim. Yine de, ince kontrolörler benim görüşüme göre daha iyi bir yol.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.