0

I'm having a hard time trying to understand the best way to achieve inheritance with my controllers. I've seen a few other posts here about these but I still don´t get some things.

Here's what I have: - 2 controllers which are 80% similar. I already have a factory which both use to get the data which will be displayed. - I use the controllerAs notation, with var vm = this - there's a mix of vars and functions which will be used in the view and therefore are created inside vm, and some other internal vars and functions which are not. - so I tried to create a single parent controller with all this and then use injection to create these 2 controllers, overwriting only what I need, but this is not working as I expected and also I'm not sure this is the right thing to do

Here is a simplified version of the code.

(function() {

    angular

    .controller('ParentController', ParentController)

    ParentController.$inject = [ '$scope', '$location' ];

    function ParentController($scope, $location) {

        var vm = this; // view model

        var URL = $location.url();      

        var isDataLoaded = false;

        vm.predicate = 'order';
        vm.reverse = false;

        vm.results;     

        vm.isDataReady = isDataReady;
        vm.setOrder = setOrder;

        function isDataReady() {
            return isDataLoaded;
        }

        function setOrder(p) {
            vm.reverse = (p === vm.predicate) ? !vm.reverse : false;
            vm.predicate = p;
        }

        $scope.$on('READ.FINISHED', function() {
            isDataLoaded = true;
        })
    }
})();

-

(function() {

    angular

    .controller('ChildController', ChildController)

    ChildController.$inject = ['$controller', '$scope', 'myFactory'];

    function ChildController($controller, $scope, myFactory) {

        $controller('ParentController', {$scope: $scope});

        var TEMPLATE = 'SCREEN';

        // ************** M A I N **************

        myFactory.getResults(URL, vm);
    }

})();

This is now working as I expected.

When I inject ChildController with ParentController, do I really need to inject the $scope? I'm actually using vm. Also, do I need to inject also $location? In this example when I execute my code I'm forced to use var URL = $location.url(); again in my ChildController, I expected to inherite the value from ParentController.

So the thing is, am I only getting values from $scope if I work like this? what about vm? and what about those vars/functions declared outside vm like var isDataLoaded? I'd appreciate some insight about this. Would this be the right way to do it? Many thanks.

EDIT: Ok, I found out how to use my ControllerAs syntax with this. Code in the child controller would be like this:

function ChildController($controller, $scope, myFactory) {

        $controller('ParentController as vm', {$scope: $scope});

        var vm = $scope.vm;

        var TEMPLATE = 'SCREEN';

        // ************** M A I N **************

        myFactory.getResults(URL, vm);
    }

But I still need to get a way to also recover the regular var/functions inside the parent controller. Any ideas? Can it be done cleanly?

1 Answer 1

0

I recommend you to implement usual javascript's inheritance mechanism between two classes. At ChildController constructor you will execute ParentController constructor and pass to it injected parameters (without using $controller service). I created simple example (very far from your logic but consider it as patten). I have two controllers: ParentController and ChildController, that inherited from first one. I used only ChildController with $scope, $interval and custom service with name "myservice" (all of them needed only for example). You can see that I used methods and fields from parent and child controllers. Logic of my app is very simple: you can add new items to collection (by means of ParentController) and remove them(by means of ChildController) with logging.

At that case I recommend you use ChildController as ctrl for Data Binding instead of $scope, because it more in line inheritance paradigm (we inherite controllers(ctrl) not $scope).

P.S. If you be going to use inheritance very often I recommend you to use TypeScript - it gives very simple and flexible solution of this problem in c# style.

Controllers.js

function ParentController(myservice, $scope, $interval) {
  this.newOne={};
  this.lastacivity={};
  this.$interval = $interval;
  this.items = myservice();
}

ParentController.prototype.Log = function(item){
  var self = this;
  this.$interval(function(){
      console.log(item);
      self.lastacivity = item;
    }, 100, 1);
}
ParentController.prototype.AddNew = function (Name, Age) {
  var newitem = {
      name: this.newOne.Name,
      age: this.newOne.Age
    }
    this.items.push(newitem);
    this.Log(newitem);
}

function ChildController(myservice, $scope, $interval) {
    //Transfering myservice, $scope, $interval from ChildController to ParentController constructor,
    //also you can pass all what you want: $http, $location etc.
    ParentController.apply(this, [myservice, $scope, $interval]);
}        
ChildController.prototype = Object.create(ParentController.prototype)

//your ChildController's own methods
ChildController.prototype.Remove = function (item) {
    this.items.splice(this.items.indexOf(item), 1);
    this.Log(item);
}

script.js

(function(angular) {
  'use strict';
angular.module('scopeExample', []).factory('myservice', function() {
  var items = [
          {name:"Mike",age:21},
          {name:"Kate",age:22},
          {name:"Tom",age:11}
        ];
  return function(){
      return items;
  }
}).controller('ChildController', ['myservice', '$scope', '$interval', ChildController]);
})(window.angular);

HTML

<body ng-app="scopeExample">
  <div ng-controller="ChildController as ctrl">
    <label>Name</label>
    <input type='text' ng-model='ctrl.newOne.Name'/>
    <label>Age</label>
    <input type='text' ng-model='ctrl.newOne.Age'/>
    <input type='button' value='add' ng-click='ctrl.AddNew()' ng-disabled="!(ctrl.newOne.Name && ctrl.newOne.Age)"/>
    <br>
    <br>
    <table>
      <thead>
        <tr>
          <th>Name</th>
          <th>Age</th>
          <th></th>
        </tr>
      </thead>
      <tbody>
        <tr ng-repeat="item in ctrl.items">
          <td>{{item.name}}</td>
          <td>{{item.age}}</td>
          <td>
            <input type='button' value='X' ng-click='ctrl.Remove(item)'/>
          </td>
        </tr>
      </tbody>
    </table>
    <p>{{ctrl.lastacivity | json}}</p>
</div>
</body>
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.