function NgModelController($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $q, $interpolate) {
  this.$viewValue = Number.NaN;
  this.$modelValue = Number.NaN;
  this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
  this.$validators = {};
  this.$asyncValidators = {};
  this.$parsers = [];
  this.$formatters = [];
  this.$viewChangeListeners = [];
  this.$untouched = true;
  this.$touched = false;
  this.$pristine = true;
  this.$dirty = false;
  this.$valid = true;
  this.$invalid = false;
  this.$error = {}; // keep invalid keys here
  this.$$success = {}; // keep valid keys here
  this.$pending = undefined; // keep pending keys here
  this.$name = $interpolate($attr.name || '', false)($scope);
  this.$$parentForm = nullFormCtrl;
  this.$options = defaultModelOptions;

  this.$$parsedNgModel = $parse($attr.ngModel);
  this.$$parsedNgModelAssign = this.$$parsedNgModel.assign;
  this.$$ngModelGet = this.$$parsedNgModel;
  this.$$ngModelSet = this.$$parsedNgModelAssign;
  this.$$pendingDebounce = null;
  this.$$parserValid = undefined;

  this.$$currentValidationRunId = 0;

  this.$$scope = $scope;
  this.$$attr = $attr;
  this.$$element = $element;
  this.$$animate = $animate;
  this.$$timeout = $timeout;
  this.$$parse = $parse;
  this.$$q = $q;
  this.$$exceptionHandler = $exceptionHandler;

  setupValidity(this);
  setupModelWatcher(this);
}
@ngdoc type
@name ngModel.NgModelController

@property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
is set.
@property {*} $modelValue The value in the model that the control is bound to.
@property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
       the control reads value from the DOM. The functions are called in array order, each passing
       its return value through to the next. The last return value is forwarded to the
       {@link ngModel.NgModelController#$validators `$validators`} collection.

Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
`$viewValue`}.

Returning `undefined` from a parser means a parse error occurred. In that case,
no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
is set to `true`. The parse error is stored in `ngModel.$error.parse`.

@property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
       the model value changes. The functions are called in reverse array order, each passing the value through to the
       next. The last return value is used as the actual DOM value.
       Used to format / convert values for display in the control.
```js
function formatter(value) {
  if (value) {
    return value.toUpperCase();
  }
}
ngModel.$formatters.push(formatter);
```

@property {Object.<string, function>} $validators A collection of validators that are applied
     whenever the model value changes. The key value within the object refers to the name of the
     validator while the function refers to the validation operation. The validation operation is
     provided with the model value as an argument and must return a true or false value depending
     on the response of that validation.

```js
ngModel.$validators.validCharacters = function(modelValue, viewValue) {
  var value = modelValue || viewValue;
  return /[0-9]+/.test(value) &&
         /[a-z]+/.test(value) &&
         /[A-Z]+/.test(value) &&
         /\W+/.test(value);
};
```

@property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
     perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
     is expected to return a promise when it is run during the model validation process. Once the promise
     is delivered then the validation status will be set to true when fulfilled and false when rejected.
     When the asynchronous validators are triggered, each of the validators will run in parallel and the model
     value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
     is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
     will only run once all synchronous validators have passed.

Please note that if $http is used then it is important that the server returns a success HTTP response code
in order to fulfill the validation and a status level of `4xx` in order to reject the validation.

```js
ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
  var value = modelValue || viewValue;

  // Lookup user by username
  return $http.get('/api/users/' + value).
     then(function resolved() {
       //username exists, this means validation fails
       return $q.reject('exists');
     }, function rejected() {
       //username does not exist, therefore this validation passes
       return true;
     });
};
```

@property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
    view value has changed. It is called with no arguments, and its return value is ignored.
    This can be used in place of additional $watches against the model value.

@property {Object} $error An object hash with all failing validator ids as keys.
@property {Object} $pending An object hash with all pending validator ids as keys.

@property {boolean} $untouched True if control has not lost focus yet.
@property {boolean} $touched True if control has lost focus.
@property {boolean} $pristine True if user has not interacted with the control yet.
@property {boolean} $dirty True if user has already interacted with the control.
@property {boolean} $valid True if there is no error.
@property {boolean} $invalid True if at least one error on the control.
@property {string} $name The name attribute of the control.

@description

`NgModelController` provides API for the {@link ngModel `ngModel`} directive.
The controller contains services for data-binding, validation, CSS updates, and value formatting
and parsing. It purposefully does not contain any logic which deals with DOM rendering or
listening to DOM events.
Such DOM related logic should be provided by other directives which make use of
`NgModelController` for data-binding to control elements.
Angular provides this DOM logic for most {@link input `input`} elements.
At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
custom control example} that uses `ngModelController` to bind to `contenteditable` elements.

@example
### Custom Control Example
This example shows how to use `NgModelController` with a custom control to achieve
data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
collaborate together to achieve the desired result.

`contenteditable` is an HTML5 attribute, which tells the browser to let the element
contents be edited in place by the user.

We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
that content using the `$sce` service.

<example name="NgModelController" module="customControl" deps="angular-sanitize.js">
    <file name="style.css">
      [contenteditable] {
        border: 1px solid black;
        background-color: white;
        min-height: 20px;
      }

      .ng-invalid {
        border: 1px solid red;
      }

    </file>
    <file name="script.js">
      angular.module('customControl', ['ngSanitize']).
        directive('contenteditable', ['$sce', function($sce) {
          return {
            restrict: 'A', // only activate on element attribute
            require: '?ngModel', // get a hold of NgModelController
            link: function(scope, element, attrs, ngModel) {
              if (!ngModel) return; // do nothing if no ng-model

              // Specify how UI should be updated
              ngModel.$render = function() {
                element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
              };

              // Listen for change events to enable binding
              element.on('blur keyup change', function() {
                scope.$evalAsync(read);
              });
              read(); // initialize

              // Write data to the model
              function read() {
                var html = element.html();
                // When we clear the content editable the browser leaves a <br> behind
                // If strip-br attribute is provided then we strip this out
                if (attrs.stripBr && html === '<br>') {
                  html = '';
                }
                ngModel.$setViewValue(html);
              }
            }
          };
        }]);
    </file>
    <file name="index.html">
      <form name="myForm">
       <div contenteditable
            name="myWidget" ng-model="userContent"
            strip-br="true"
            required>Change me!</div>
        <span ng-show="myForm.myWidget.$error.required">Required!</span>
       <hr>
       <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
      </form>
    </file>
    <file name="protractor.js" type="protractor">
    it('should data-bind and become invalid', function() {
      if (browser.params.browser === 'safari' || browser.params.browser === 'firefox') {
        // SafariDriver can't handle contenteditable
        // and Firefox driver can't clear contenteditables very well
        return;
      }
      var contentEditable = element(by.css('[contenteditable]'));
      var content = 'Change me!';

      expect(contentEditable.getText()).toEqual(content);

      contentEditable.clear();
      contentEditable.sendKeys(protractor.Key.BACK_SPACE);
      expect(contentEditable.getText()).toEqual('');
      expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
    });
    </file>
</example>