Controllers & $scope

Everything up to now used ng-init to fake some starting data, which is a demo-only trick — real AngularJS apps set up their data and behavior in a controller, a plain JavaScript function tied to a section of the page through ng-controller. This lesson is the one everything after it depends on.

Defining a controller

A controller is registered on a module (covered fully next lesson) and receives $scope as an argument — the object that bridges the controller's JavaScript and the template's HTML:

JS app.js
var app = angular.module('myApp', []);

app.controller('ProfileController', function($scope) {
  $scope.name = 'Priya';
  $scope.points = 120;

  $scope.addPoint = function() {
    $scope.points++;
  };
});
HTML profile.html
<div ng-app="myApp" ng-controller="ProfileController">
  <p>{{ name }} has {{ points }} points.</p>
  <button ng-click="addPoint()">+1</button>
</div>
Rendered behavior
Priya has 120 points.  [+1]

Clicking the button runs addPoint(), which increments $scope.points —
the paragraph re-renders to "Priya has 121 points." with no manual DOM
update written anywhere.

Everything the template can see — name, points, addPoint() — was put there by the controller assigning it onto $scope. Think of $scope as the one object both the JavaScript and the HTML agree to read and write through.

ng-click and other event directives

ng-click is the directive equivalent of onclick — it runs an expression (usually a function call, as above) in response to a click, without you attaching an event listener by hand.

Nested controllers and scope inheritance

An ng-controller placed inside another creates a child scope that prototypally inherits from its parent — it can read the parent's properties directly, without any special syntax:

HTML nested.html
<div ng-app="myApp" ng-controller="OuterController">
  <p>Outer sees: {{ company }}</p>
  <div ng-controller="InnerController">
    <p>Inner sees: {{ company }}</p>
  </div>
</div>
Rendered output
Outer sees: Acme Co
Inner sees: Acme Co

Even though InnerController never sets company itself, it can read the value OuterController put on its scope — child scopes fall back to the parent's properties automatically, exactly like prototypal inheritance for plain JavaScript objects.

The classic shadowing bug: that inheritance only works cleanly for reading values, not writing them. If InnerController does $scope.company = 'New Name', it creates a brand-new property on the child scope that shadows the parent's — the outer scope's value never changes, and now the two scopes disagree. The standard fix is binding to a property on an object ($scope.data.company) rather than a bare primitive ($scope.company), since object properties are looked up by reference and don't get shadowed the same way. AngularJS developers call this "the dot rule": if a binding in your template doesn't have at least one dot in it, assignment from a child scope is likely to misbehave.