RiccardoNovaglia
RiccardoNovaglia

Reputation: 33

Why declaring a controller as variable within itself or using $scope

I have recently started to learn and use AngularJS and I'm still unclear about some concept.

My question is this: in my app I have a controller which uses $http to retrieve data from my back-end as soon as it is initialised. Following the excellent tutorial from CodeSchool on Angular I came up with this:

app.controller("agentController", function ($http) {
    var agentsCtrl = this;
    agentsCtrl.agents = [];
    $http.get("getAgents").success(function (data) {
        agentsCtrl.agents = data;
    });
...

HTML:

<div ng-controller="agentController as agentCtrl">
    <div ng-repeat="agent in agentCtrl.agents">
...

This works fine, but I'm unclear on why I would need to declare the controller as a variable within itself using this. From what I understand it's because by doing this I can then call it within the $http service, where the this keyword would return the wrong scope. Is this correct?

I found that this also works:

app.controller("agentController", function ($http, $scope) {
    $scope.agents = [];
    $http.get("getAgents").success(function (data) {
        $scope.agents = data;
    });

HTML:

<div ng-controller="agentController as agentCtrl">
    <div ng-repeat="agent in agents">
...

I guess this works because I explicitly inject the scope and use it within the service. Also, the HTML is now slightly different. In the first case I need to explicitly call agentCtrl.agents, while with scope simply call on agents. This is still because the variable is now declared on the scope rather than the controller? Is this correct?

What is the best practice to use in similar cases?

Thank you!

Upvotes: 3

Views: 171

Answers (4)

U r s u s
U r s u s

Reputation: 6968

This is for answering your comment to Ben Diamant's answer.

The less traditional alias syntax (as Alias) simplifies your code and probably makes clearer what you're trying to do to somebody reading your code.

From ng-controller spec:

Using controller as

  • makes it obvious which controller you are accessing in the template when multiple controllers apply to an element.

  • If you are writing your controllers as classes you have easier access to the properties and methods, which will appear on the scope, from inside the controller code.

  • Since there is always a . in the bindings, you don't have to worry about prototypal inheritance masking primitives.

Check out also this awesome explanation of the advantages of alias syntax.

Among those advantages one the most important is that the alias syntax allows you to

refer to the controller we wish to refer to and stop worrying about the subtleties of scope inheritance.

I hope it helps.

Upvotes: 1

Wayne Ellery
Wayne Ellery

Reputation: 7958

The this keyword is contextual and when used within a function inside a controller may change its context. Capturing the context of this avoids encountering this problem.

Eg:

app.controller('MainCtrl', function() {
  this.test = 'test';

  angular.forEach(testArray, function (item) {
    console.debug(this.test);
  });
});

this.test is undefined as the scope of this has changed.

Plunkr

Using a variable fixes it:

app.controller('MainCtrl', function() {
  var vm = this;

  vm.test = 'test';
  var testArray = [
    1, 2, 3, 4
  ];

  angular.forEach(testArray, function (item) {
    console.debug(vm.test);
  });
});

Plunkr

Original statement taken from John Papa's style guide

Upvotes: 1

nanndoj
nanndoj

Reputation: 6770

Question 1:

You have to save the reference to scope var agentsCtrl = this because your are creating a anonymous function to be used as a callback. When you do it the context variable (this) will be window.

app.controller("agentController", function ($http) {
    // this == your controller
    var agentsCtrl = this;

    $http.get("getAgents").success(function (data) {
        // Anonymous function 
        // this == Window Object
    });
});

Question 2:

$scope is the glue between application controller and the view. you can reference it as a global variable that you can have access both from view and from controllers.

$scope.agents = []; // From controller

And

{{ agents }} // From view (you don't need to specify the $scope variable)

I prefer to give an alias for the controler and use it in my HTML as you did in

<div ng-controller="agentController as agentCtrl">
    <div ng-repeat="agent in agents">
...

But both ways are correct and will work fine.

Upvotes: 1

Ben Diamant
Ben Diamant

Reputation: 6206

You are asking regard 2 different approach - "Controller as" or "$scope".

$scope

Original usage for scope binding would be:

<div ng-controller="MainController">  
  {{ someObj.someProp }}
</div>
app.controller('MainController', function ($scope) {  
  $scope.someObj = {
    someProp: 'Some value.'
  };
});

"Controller as"

Using this technique you can use the bind as follows:

<div ng-controller="MainController as main">  
  {{ main.someProp }}
</div>  
app.controller('MainController', function () {  
  this.someProp = 'Some value.'
});

Both work and, both fine. The major difference is preference.

Still, most of the community prefers 'Controller as', why?

  1. Readable - You can always look on a DOM element and understand what scope's model is bound to
  2. Testable - Not having to inject a $scope makes the test code slightly easier
  3. More suited to the vision of angular 2.0 where the controller is much more a constructor function than a function that adds stuff to $scope

Upvotes: 2

Related Questions