0

In C#, it is recommended to avoid loading data in a constructor. Instead, instantiate a class, and then create a method when you need to do something like load data from the database. In AngularJS with TypeScript, is the only way to run methods on load in a controller through the constructor? I.e., the code below does not compile in TypeScript. Is that because I have to call activate from the constructor?

class BaHomeController{
    entries: any[];    
    static $inject = ["$http"];

    constructor(
        private $http
    ){}

    private loadEntries() {
        this.$http.get("api/blogEntries").then(function(response) {
            this.entries = response.data;
        });
    }

    private activate(): void{
        this.loadEntries();
    }

    activate();   
}
Hoppe
  • 6,508
  • 17
  • 60
  • 114
  • I am not seeing the C# analog to your current code... you wouldn't call activate() *from within the class* in C# either, right? – Paul-Jan Jan 05 '16 at 18:11
  • Don't know enough about the context's OO model, but... That's a silly recommendation from C#. What is *parameter sniffing* for 1000, Trebek. – abluejelly Jan 05 '16 at 18:11
  • Ok, forget about C#. Can you call activate on load without putting it in the constructor? Or is the best practice just to put it in the constructor? – Hoppe Jan 05 '16 at 18:13
  • @abluejelly http://stackoverflow.com/a/32466382/846844 – Hoppe Jan 06 '16 at 11:51
  • @Hoppe Async is a decent argument to which there's not much of a counter (other than specificity of the problem case). Failures in the constructor are not "harder to reason about"- the constructor is a specially-invoked method with malloc overhead, not some magical incantation. Unit testing is a decent point, though that falls under "keep it light" more than "don't load data". You don't need to load all data, but it doesn't always make sense to *not* load some- especially if that data has a major affect on how the class should be initialized (in its most obvious form: the length of an array). – abluejelly Jan 06 '16 at 16:32
  • @Hoppe A much better recommendation would be to only load data you can sniff, as in: "If you can't modify the object's internal setup based on the data you're loading, don't." – abluejelly Jan 06 '16 at 16:34

3 Answers3

2

Your formatting is all messed up for me perhaps this format will make things more clear. Also you cannot call activate in your class declaration like you had it which is why you didn't compile.

class BaHomeController{
    // private field
    entries: any[];

    static $inject = ["$http"];    
    constructor(private $http)
    {
        //This is calling the private activate method in the constructor
        this.activate();  
    }
    //private method
    private loadEntries() 
    {
        this.$http.get("api/blogEntries").then(function(response) {
            this.entries = response.data;
        });
    }
    //private method that return void
    private activate(): void
    {
        this.loadEntries();
    }     
}
Delta
  • 869
  • 6
  • 12
1

You could also resolve the data in the route:

angular.module('app.something')
    .config(routing);

routing.$inject = ['$stateProvider'];
function routing(stateProvider: angular.ui.IStateProvider) {
    stateProvider
        .state('home', {
            url: '/home',
            templateUrl: 'scripts/home/home.html',
            controller: 'app.something.BaHomeController',
            controllerAs: 'vm',
            resolve: {
                entries: loadEntries
            }
        })
}
function loadEntries() {
    //Make call to entries service
    return entriesService.getEntries(); //Make the http call in the service and also deal with the promise there.
}

Your controller

class BaHomeController{

static $inject = ["$http"];    
constructor(private $http: ng.IHttpService,
            public entries: any){ }

}

An alternative is to have an init function in the controller and call it from the html so it gets invoked when the page loads

class LayoutController {

    dataModel: app.layout.TopSearchHtmlDataModel;
    vehicleSearchModel: app.layout.VehicleSearchModel;

    static $inject = [
        'app.services.SlideService',
        'app.services.VehicleService',
        'app.services.CommonUtils',
        '$rootScope',
        '$filter',
        '$state'
    ];

    constructor(
        private slideService: app.services.SlideService,
        private vehicleService: app.services.VehicleService,
        private commonUtils: app.services.CommonUtils,
        private $rootScope: ng.IRootScopeService,
        private $filter: any,
        public $state: ng.ui.IStateService) {

        this.dataModel = new app.layout.TopSearchHtmlDataModel();
        this.vehicleSearchModel = new app.layout.VehicleSearchModel();
    }

    init(): void {
        this.getAllMakeAndModelData();
        this.getIrishCounties();
        this.getMinYearArray();
        this.getLogoSlides();
    }
    //Private methods omitted
}

html:
    <div ng-init="vm.init()"></div> //assuming you use controllerAs vm
AngularBoy
  • 1,715
  • 2
  • 25
  • 35
0

The call to the data will be asynchronous anyway, so putting the call in the constructor doesn't seem that big of a deal.

Have you tried this?

   constructor(){
        this.loadEntries();
   }

Be aware that the async nature of the callback inside of loadEntries means that your controller will exist with this.entries undefined until the $http.get call returns successfully

stujo
  • 2,089
  • 24
  • 29
  • This is not valid, you are calling a method in the parameter section of the constructor. your method parameters are in the () your body is in the {} just like java script/c# – Delta Jan 05 '16 at 19:09
  • Thanks @Delta, I think I've fixed that now – stujo Jan 05 '16 at 23:24