web-dev-qa-db-fra.com

Quelle est la méthode recommandée pour étendre les contrôleurs AngularJS?

J'ai trois contrôleurs qui sont assez similaires. Je veux avoir un contrôleur qui ces trois étendent et partagent ses fonctions.

189
vladexologija

Peut-être que vous n’étendez pas un contrôleur, mais il est possible d’agrandir un contrôleur ou de transformer un seul contrôleur en une combinaison de plusieurs contrôleurs.

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

Lorsque le contrôleur parent est créé, la logique qu'il contient est également exécutée. Voir $ controller () pour plus d'informations sur mais seule la valeur $scope doit être transmise. Toutes les autres valeurs seront injectées normalement.

@ mwarren, votre problème est traité automatiquement par magie par Angular injection de dépendance. Tout ce dont vous avez besoin est d'injecter $ scope, bien que vous puissiez remplacer les autres valeurs injectées si vous le souhaitez. Prenons l'exemple suivant:

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

Bien que $ document ne soit pas passé dans 'simpleController' lorsqu'il est créé par 'complexController', $ document est injecté pour nous.

300
Enzey

Pour l'héritage, vous pouvez utiliser des modèles d'héritage JavaScript standard. Voici une démo qui utilise $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);

Si vous utilisez la syntaxe controllerAs (ce que je recommande vivement), il est encore plus simple d'utiliser le modèle d'héritage classique:

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

Une autre façon pourrait être de créer une fonction constructeur "abstraite" qui sera votre contrôleur de base:

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

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

Blog post sur ce sujet

52
Minko Gechev

Eh bien, je ne suis pas tout à fait sûr de ce que vous voulez réaliser, mais généralement, les services sont la voie à suivre. Vous pouvez également utiliser les caractéristiques d'héritage Scope de Angular pour partager le code entre les contrôleurs:

<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
}
16
Andre Goncalves

Vous n'allongez pas les contrôleurs. S'ils remplissent les mêmes fonctions de base, ces fonctions doivent être déplacées vers un service. Ce service peut être injecté dans vos contrôleurs.

15
Bart

Encore une autre bonne solution tirée de ceci article :

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

Vous pouvez créer un service et hériter de son comportement dans n'importe quel contrôleur simplement en l'injectant.

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

    var reusableCode = {};

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

    return reusableCode;
});

Ensuite, dans votre contrôleur, vous souhaitez étendre le service reusableCode ci-dessus:

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

7
Raghavendra

Vous pouvez essayer quelque chose comme ceci (ne pas avoir testé):

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

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

}));
5
karaxuna

Vous pouvez étendre avec un service , des usines ou fournisseurs . ils sont identiques mais avec un degré de flexibilité différent.

voici un exemple utilisant factory: 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
});

Et ici, vous pouvez également utiliser la fonction de stockage:

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

Vous pouvez directement utiliser $ controller ('ParentController', {$ scope: $ scope}) Exemple

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

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

J'ai écrit une fonction pour faire ceci:

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

Vous pouvez l'utiliser comme ceci:

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

Avantages:

  1. Vous n'êtes pas obligé d'enregistrer le contrôleur de base.
  2. Aucun des contrôleurs n'a besoin de connaître les services $ controller ou $ injector.
  3. Cela fonctionne bien avec la syntaxe d'injection matricielle d'angular - ce qui est essentiel si votre javascript doit être minifié.
  4. Vous pouvez facilement ajouter des services injectables supplémentaires au contrôleur de base, sans également ne pas oublier de les ajouter et de les transmettre à tous les enfants. contrôleurs.

inconvénients:

  1. Le contrôleur de base doit être défini comme une variable, ce qui risque de polluer la portée globale. J'ai évité cela dans mon exemple d'utilisation en encapsulant tout dans une fonction auto-exécutable anonyme, mais cela signifie que tous les contrôleurs enfants doivent être déclarés dans le même fichier.
  2. Ce modèle fonctionne bien pour les contrôleurs qui sont instanciés directement à partir de votre code HTML, mais n'est pas si bon pour les contrôleurs que vous créez à partir de votre code via le service $ controller (), car sa dépendance à l'injecteur vous empêche d'injecter directement, non -service paramètres de votre code d'appel.
1
Dan King

Vous pouvez utiliser la syntaxe Angular "en tant que" associée à un héritage JavaScript simple.

Voir plus de détails ici http://blogs.Microsoft.co.il/oric/2015/01/01/base-controller-angularjs/

1
Ori Calvo

Je considère l'extension des contrôleurs comme une mauvaise pratique. Mettez plutôt votre logique partagée dans un service. Les objets étendus en javascript ont tendance à être plutôt complexes. Si vous souhaitez utiliser l'héritage, je recommanderais TypeScript. Néanmoins, les contrôleurs minces sont un meilleur moyen d’aller de mon point de vue.

1
Brecht Billiet