web-dev-qa-db-ja.com

AngularJSコントローラーを拡張する推奨方法は何ですか?

よく似た3つのコントローラーがあります。これら3つが機能を拡張および共有するコントローラーが必要です。

188
vladexologija

おそらくyoはコントローラーを拡張しませんが、コントローラーを拡張したり、単一のコントローラーを複数のコントローラーのミックスインにすることは可能です。

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

親コントローラーが作成されると、その中に含まれるロジックも実行されます。詳細については、$ controller()を参照してください。ただし、$scope値のみを渡す必要があります。他のすべての値は通常どおり注入されます。

@ mwarren、あなたの懸念はAngular依存性注入によって自動的に魔法のように処理されます。必要なのは$ scopeを注入することだけですが、必要に応じて他の注入された値をオーバーライドできます。次の例をご覧ください。

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

「complexController」によって作成された場合、$ documentは「simpleController」に渡されませんが、$ documentが注入されます。

297
Enzey

継承には、標準のJavaScript継承パターンを使用できます。 $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);

controllerAs構文(これを強くお勧めします)を使用する場合、従来の継承パターンを使用する方が簡単です。

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

別の方法は、基本コントローラーとなる「抽象」コンストラクター関数を作成することです。

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

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

このトピックに関するブログ投稿

52
Minko Gechev

まあ、私はあなたが何を達成したいのか正確にはわかりませんが、通常はサービスが道です。 Angularのスコープ継承特性を使用して、コントローラー間でコードを共有することもできます。

<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

コントローラを拡張しません。同じ基本機能を実行する場合、それらの機能をサービスに移動する必要があります。そのサービスをコントローラーに注入できます。

15
Bart

これから取られたさらに別の良い解決策 記事

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

サービスを作成し、それを注入するだけで、任意のコントローラーでその動作を継承できます。

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

    var reusableCode = {};

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

    return reusableCode;
});

次に、上記のreusableCodeサービスから拡張するコントローラーで:

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

    angular.extend($scope, reusableCode);

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

});

デモプランカー: http://plnkr.co/edit/EQtj6I0X08xprE8D0n5b?p=preview

7
Raghavendra

次のようなものを試すことができます(テストしていません):

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

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

}));
5
karaxuna

servicesfactories、またはproviders。それらは同じですが、柔軟性の程度が異なります。

ここにファクトリを使用した例: 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
});

そして、ここでストア関数も使用できます:

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

$ 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});
}]);
4
Anand Gargate

これを行う関数を作成しました。

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

次のように使用できます。

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

長所:

  1. ベースコントローラーを登録する必要はありません。
  2. どのコントローラーも、$ controllerまたは$ injectorサービスについて知る必要はありません。
  3. これは、角度の配列インジェクション構文でうまく機能します-これは、javascriptを縮小する場合に不可欠です。
  4. 追加のインジェクタブルサービスをベースコントローラーに簡単に追加できます。withoutまた、すべての子コントローラーに追加してパススルーすることを忘れないでください。

短所:

  1. ベースコントローラーは変数として定義する必要があり、グローバルスコープを汚染するリスクがあります。私の使用例では、匿名の自己実行関数ですべてをラップすることでこれを回避しましたが、これはすべての子コントローラーを同じファイルで宣言する必要があることを意味します。
  2. このパターンは、htmlから直接インスタンス化されるコントローラーには適していますが、$ controller()サービスを介してコードから作成するコントローラーにはあまり適していません。インジェクターに依存しているため、余分な非-呼び出しコードからのサービスパラメータ。
1
Dan King

Angular "as"構文を単純なJavaScript継承と組み合わせて使用​​できます

詳細はこちらをご覧ください http://blogs.Microsoft.co.il/oric/2015/01/01/base-controller-angularjs/

1
Ori Calvo

コントローラを拡張するのは悪い習慣だと考えています。むしろ、共有ロジックをサービスに入れてください。 JavaScriptの拡張オブジェクトはかなり複雑になる傾向があります。継承を使用する場合は、TypeScriptをお勧めします。それでも、私の考えでは、薄いコントローラーの方が良い方法です。

1
Brecht Billiet