Dave Long
Dave Long

Reputation: 9759

Using $scope.$watch when using `this` scope in controller

I have a controller in my Angular app:

(function (angular) {
    function MyController() {
        this.name = 'Dave';

        // I want to have code like this:
        /*
          $scope.$watch('name', function (newValue, oldValue) {
              console.log(oldValue, "changed to", newValue);
          });
        */
    }

    window.myApp = angular.module('myApp', [])
        .controller('MyController', [MyController]);
})(angular);

Is there a way to use the features of $scope.$watch when attaching values to the MyController prototype?

I did notice that in my code, if I add something like ng-controller="MyController as myCtrl", and change my $scope.$watch statement to $scope.$watch('myCtrl.name', ...), it'll work after I add the $scope dependency, but that feels like tying my controller to my views, which feels wrong.

Edit

To attempt to clarify on what I'm asking. My HTML is something like this:

<div ng-app="myApp">
  <div ng-controller="MyController as myCtrl">
    <input type="text" ng-model="myCtrl.name" />
    <p>{{myCtrl.helloMessage}}</p>
  </div>
</div>

My controller is something like this:

angular.module('myApp', [])
  .controller('MyController', ['$scope', function ($scope) {
    this.name = 'World';
    this.helloMessage = "Hello, " + this.name;

    var self = this;
    $scope.$watch('myCtrl.name', function () {
      self.helloMessage = "Hello, " + self.name;
    });
  }]);

That currently works, but as you can see, in the $watch call, I have to reference my controller by the controllerAs name from my view, which is less than ideal.

I've setup an example on Plunkr

Upvotes: 5

Views: 3643

Answers (3)

Krzysztof Safjanowski
Krzysztof Safjanowski

Reputation: 7438

$watch

Expression that is evaluated on each $digest cycle. A change in the return value triggers a call to the listener. Watch expression can be a sting or function.

  • string: Evaluated as expression
  • function(scope): called with current scope as a parameter.

example

angular.module('app', []).controller('MainCtrl', function($scope) {
  this.name = 'World'
  this.helloMsg = ''
  
  $scope.$watch(function() {
    return this.name
  }.bind(this), function(newName) {
    this.helloMsg = "Hello, " + newName
  }.bind(this))
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app='app'>
  <div ng-controller='MainCtrl as ctrl'>
    <input type='text' ng-model='ctrl.name' />
    {{ ctrl.helloMsg }}
  </div>
</div>

Upvotes: 4

Claies
Claies

Reputation: 22323

You can avoid being bound to the view by using angular.bind in your watch, i.e.

$scope.$watch(angular.bind(this, function () {
    self.helloMessage = "Hello, " + self.name;
}));

https://docs.angularjs.org/api/ng/function/angular.bind

Upvotes: 3

TchiYuan
TchiYuan

Reputation: 4278

I might be wrong but I believe the $scope is automatically injected by angular.

Here's an example of a controller being declared:

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

myApp.controller('GreetingController', ['$scope', function($scope) {
      $scope.greeting = 'Hola!';
}]);

Notice how $scope dependency is declared '$scope' and injected function($scope)

In other words, should yours look something like this ?

function MyController($scope) {}

window.myApp = angular.module('myApp', [])
        .controller('MyController', ['$scope', MyController($scope)]);

EDIT:

I understand now. I've never had a need to use "controller as" but why not do it like this ?

<div ng-app="myApp">
  <div ng-controller="MyController">
    <input type="text" ng-model="name" />
    <p>{{helloMessage}}</p>
  </div>
</div>

angular.module('myApp', [])
  .controller('MyController', ['$scope', function ($scope) {
    this.name = 'World';
    this.helloMessage = "Hello, " + this.name;

    var self = this;
    $scope.$watch('name', function () {
      self.helloMessage = "Hello, " + self.name;
    });
  }])

Upvotes: 0

Related Questions