Cách khuyến nghị để mở rộng bộ điều khiển AngularJS là gì?


193

Tôi có ba bộ điều khiển khá giống nhau. Tôi muốn có một bộ điều khiển mà ba cái này mở rộng và chia sẻ các chức năng của nó.

Câu trả lời:


302

Có lẽ bạn không mở rộng bộ điều khiển nhưng có thể mở rộng bộ điều khiển hoặc biến một bộ điều khiển thành một hỗn hợp gồm nhiều bộ điều khiển.

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

Khi bộ điều khiển chính được tạo, logic chứa trong nó cũng được thực thi. Xem $ control () để biết thêm thông tin về nhưng chỉ $scopegiá trị cần được thông qua. Tất cả các giá trị khác sẽ được tiêm bình thường.

@mwarren , mối quan tâm của bạn được chăm sóc tự động một cách kỳ diệu bằng cách tiêm phụ thuộc Angular. Tất cả những gì bạn cần là tiêm $ scope, mặc dù bạn có thể ghi đè các giá trị được tiêm khác nếu muốn. Lấy ví dụ sau:

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

Mặc dù $ document không được truyền vào 'SimpleContoder' khi nó được tạo bởi 'documentContoder' $ document được chèn cho chúng ta.


1
Cho đến nay, giải pháp nhanh nhất, sạch nhất và dễ dàng nhất cho việc này! Cảm ơn!
MK

giải pháp hoàn hảo, tuyệt vời!
Kamil Lach

8
Tôi nghĩ rằng bạn không cần điều đó $.extend(), bạn chỉ cần gọi$controller('CtrlImpl', {$scope: $scope});
tomraithel 16/12/14

5
@tomraithel không sử dụng angular.extend(hoặc $.extend) thực sự có nghĩa là chỉ mở rộng $scope, nhưng nếu bộ điều khiển cơ sở của bạn cũng xác định một số thuộc tính (ví dụ this.myVar=5), bạn chỉ có quyền truy cập this.myVarvào bộ điều khiển mở rộng khi sử dụngangular.extend
schellmax 23/12/14

1
Điều đó thật tuyệt! Chỉ cần đảm bảo nhớ mở rộng tất cả các chức năng cần thiết, tức là tôi có một cái gì đó như: handleSubmitClickcái handleLoginmà lần lượt sẽ có một loginSuccessloginFail. Vì vậy, trong điều khiển mở rộng của tôi, tôi sau đó đã phải quá tải handleSubmitClick, handleLoginloginSucesscho đúng loginSuccesschức năng được sử dụng.
Justin Kruse

52

Để kế thừa, bạn có thể sử dụng các mẫu thừa kế JavaScript tiêu chuẩn. Đây là một bản demo sử dụng$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);

Trong trường hợp bạn sử dụng controllerAscú pháp (mà tôi rất khuyến khích), việc sử dụng mẫu kế thừa cổ điển thậm chí còn dễ dàng hơn:

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

Một cách khác có thể là chỉ tạo hàm xây dựng "trừu tượng" sẽ là bộ điều khiển cơ sở của bạn:

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

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

Blog bài viết về chủ đề này


16

Chà, tôi không chắc chắn chính xác những gì bạn muốn đạt được, nhưng thường thì Dịch vụ là con đường để đi. Bạn cũng có thể sử dụng các đặc điểm kế thừa Phạm vi của Angular để chia sẻ mã giữa các bộ điều khiển:

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

Chia sẻ cùng các biến và chức năng trên các bộ điều khiển thực hiện những điều tương tự (một là để chỉnh sửa, tạo khác, v.v.). Đây chắc chắn là một trong những giải pháp ...
vladexologija

1
Kế thừa $ phạm vi là cách tốt nhất để làm điều đó, tốt hơn nhiều so với câu trả lời được chấp nhận.
snez

Cuối cùng, đây dường như là con đường góc cạnh nhất. Tôi có ba ParentControllers rất ngắn gọn trên ba trang khác nhau, chỉ cần đặt giá trị $ scope mà childContoder có thể nhận. ChildContoder, mà ban đầu tôi nghĩ về việc mở rộng, chứa tất cả logic của bộ điều khiển.
mwarren

sẽ không phải $scope.$parent.fx( ) là một cách sạch sẽ hơn nhiều để làm điều đó, vì đó là nơi nó thực sự được xác định?
Akash

15

Bạn không mở rộng bộ điều khiển. Nếu chúng thực hiện các chức năng cơ bản giống nhau thì các chức năng đó cần được chuyển sang một dịch vụ. Dịch vụ đó có thể được đưa vào bộ điều khiển của bạn.


3
Cảm ơn, nhưng tôi có 4 chức năng đã sử dụng các dịch vụ (lưu, xóa, v.v.) và cùng tồn tại trong cả ba bộ điều khiển. Nếu việc mở rộng không phải là một lựa chọn, liệu có khả năng cho một 'mixin' không?
vladexologija

4
@vladexologija Mình đồng ý với Bart đây. Tôi nghĩ dịch vụ là mixin. Bạn nên cố gắng di chuyển càng nhiều logic ra khỏi bộ điều khiển càng tốt (vào các dịch vụ). Vì vậy, nếu bạn có 3 bộ điều khiển cần thực hiện các tác vụ tương tự thì một dịch vụ dường như là phương pháp phù hợp để thực hiện. Mở rộng bộ điều khiển không cảm thấy tự nhiên trong Angular.
ganaraj

6
@vladexologija Đây là một ví dụ về ý tôi muốn nói: jsfiddle.net/ERGU3 Điều đó rất cơ bản nhưng bạn sẽ hiểu ý tưởng.
Bart

3
Câu trả lời là khá vô dụng mà không có bất kỳ tranh luận và giải thích thêm. Tôi cũng nghĩ rằng bạn phần nào bỏ lỡ quan điểm của OP. Đã có một dịch vụ chia sẻ. Điều duy nhất bạn làm là trực tiếp tiếp xúc với dịch vụ đó. Tôi không biết nếu đó là một ý tưởng tốt. Cách tiếp cận của bạn cũng thất bại nếu cần truy cập vào phạm vi. Nhưng theo lý luận của bạn, tôi sẽ phơi bày rõ ràng phạm vi như một thuộc tính của phạm vi cho khung nhìn để nó có thể được chuyển qua làm đối số.
một oliver tốt hơn

6
Một ví dụ kinh điển sẽ là khi bạn có hai báo cáo theo mẫu trên trang web của mình, mỗi báo cáo phụ thuộc vào rất nhiều dữ liệu giống nhau và mỗi báo cáo sử dụng nhiều dịch vụ chia sẻ. Về mặt lý thuyết, bạn có thể thử và đưa tất cả các dịch vụ riêng biệt của mình vào một dịch vụ lớn với hàng chục cuộc gọi AJAX và sau đó có các phương thức công khai như 'getEverythingINeedForReport1' và 'getEverythingINeedForReport2', và sau đó đặt tất cả thành một đối tượng phạm vi ma mút thực sự đặt những gì cơ bản là bộ điều khiển logic vào dịch vụ của bạn. Mở rộng bộ điều khiển hoàn toàn có một trường hợp sử dụng trong một số trường hợp.
tobylaroni

10

Một giải pháp tốt khác được lấy từ bài viết này :

// 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
Điều này làm việc rất tốt cho tôi. Nó có lợi thế là tái cấu trúc dễ dàng từ một tình huống khi bạn bắt đầu với một bộ điều khiển, tạo một bộ điều khiển rất giống nhau và sau đó muốn tạo mã DRYer. Bạn không cần phải thay đổi mã, chỉ cần kéo nó ra và được thực hiện.
Eli Albert

7

Bạn có thể tạo một dịch vụ và kế thừa hành vi của nó trong bất kỳ bộ điều khiển nào chỉ bằng cách tiêm nó.

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

    var reusableCode = {};

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

    return reusableCode;
});

Sau đó, trong bộ điều khiển của bạn mà bạn muốn mở rộng từ dịch vụ tái sử dụng ở trên:

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ạn có thể thử một cái gì đó như thế này (chưa được thử nghiệm):

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

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

}));

1
Vâng Không cần phải gia hạn, chỉ cần gọi với bối cảnh
TaylorMac

4

Bạn có thể gia hạn với một dịch vụ , nhà máy hoặc nhà cung cấp . chúng giống nhau nhưng với mức độ linh hoạt khác nhau.

đây là một ví dụ sử dụng nhà máy: 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
});

Và ở đây bạn cũng có thể sử dụng chức năng lưu trữ:

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

4

Bạn có thể trực tiếp sử dụng bộ điều khiển $ ('ParentContoder', {$ scope: $ scope}) Ví dụ

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

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


1

Tôi đã viết một chức năng để làm điều này:

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

Bạn có thể sử dụng nó như thế này:

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

Ưu điểm:

  1. Bạn không phải đăng ký bộ điều khiển cơ sở.
  2. Không ai trong số các bộ điều khiển cần biết về dịch vụ $ control hoặc $ kim phun.
  3. Nó hoạt động tốt với cú pháp tiêm mảng của angular - điều này rất cần thiết nếu javascript của bạn sẽ được thu nhỏ.
  4. Bạn có thể dễ dàng thêm các dịch vụ tiêm bổ sung vào bộ điều khiển cơ sở mà không cần phải nhớ thêm chúng vào và chuyển chúng từ tất cả các bộ điều khiển con của bạn.

Nhược điểm:

  1. Bộ điều khiển cơ sở phải được định nghĩa là một biến, có nguy cơ gây ô nhiễm phạm vi toàn cầu. Tôi đã tránh điều này trong ví dụ sử dụng của mình bằng cách gói mọi thứ trong một hàm tự thực thi ẩn danh, nhưng điều này có nghĩa là tất cả các bộ điều khiển con phải được khai báo trong cùng một tệp.
  2. Mẫu này hoạt động tốt cho các bộ điều khiển được khởi tạo trực tiếp từ html của bạn, nhưng không tốt cho các bộ điều khiển mà bạn tạo từ mã của mình thông qua dịch vụ $ controller (), bởi vì việc phụ thuộc vào trình tiêm ngăn bạn tiêm trực tiếp, không -Các tham số dịch vụ từ mã gọi của bạn.

1

Tôi coi việc mở rộng bộ điều khiển là thực hành xấu. Thay vì đưa logic chia sẻ của bạn vào một dịch vụ. Các đối tượng mở rộng trong javascript có xu hướng trở nên khá phức tạp. Nếu bạn muốn sử dụng kế thừa, tôi muốn giới thiệu bản thảo. Tuy nhiên, bộ điều khiển mỏng là cách tốt hơn để đi theo quan điểm của tôi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.