Angularjs: "контроллер как синтаксис" и $watch

Как подписаться на изменение свойства при использовании синтаксиса controller as?

controller('TestCtrl', function ($scope) {
  this.name = 'Max';
  this.changeName = function () {
    this.name = new Date();
  }
  // not working       
  $scope.$watch("name",function(value){
    console.log(value)
  });
});
<div ng-controller="TestCtrl as test">
  <input type="text" ng-model="test.name" />
  <a ng-click="test.changeName()" href="#">Change Name</a>
</div>  

Ответ 1

Просто привяжите соответствующий контекст.

$scope.$watch(angular.bind(this, function () {
  return this.name;
}), function (newVal) {
  console.log('Name changed to ' + newVal);
});

Пример: http://jsbin.com/yinadoce/1/edit

UPDATE:

Ответ Богдана Герсака на самом деле эквивалентен, оба ответа пытаются привязать this к правильному контексту. Однако я нашел его ответ чистым.

Сказав это, прежде всего, вы должны понять лежащую в основе идею.

ОБНОВЛЕНИЕ 2:

Для тех, кто использует ES6, используя arrow function, вы получаете функцию с правильным контекстом OOTB.

$scope.$watch(() => this.name, function (newVal) {
  console.log('Name changed to ' + newVal);
});

Пример

Ответ 2

Я обычно делаю это:

controller('TestCtrl', function ($scope) {
    var self = this;

    this.name = 'Max';
    this.changeName = function () {
        this.name = new Date();
   }

   $scope.$watch(function () {
       return self.name;
   },function(value){
        console.log(value)
   });
});

Ответ 3

Вы можете использовать:

   $scope.$watch("test.name",function(value){
        console.log(value)
   });

Это работает JSFiddle с вашим примером.

Ответ 4

Аналогично использованию "теста" из "TestCtrl как теста", как описано в другом ответе, вы можете назначить "я" свой объем:

controller('TestCtrl', function($scope){
    var self = this;
    $scope.self = self;

    self.name = 'max';
    self.changeName = function(){
            self.name = new Date();
        }

    $scope.$watch("self.name",function(value){
            console.log(value)
        });
})

Таким образом, вы не привязаны к имени, указанному в DOM ( "TestCtrl as test" ), и вы также избегаете необходимости .bind(this) для функции.

... для использования с указанным исходным html:

<div ng-controller="TestCtrl as test">
    <input type="text" ng-model="test.name" />
    <a ng-click="test.changeName()" href="#">Change Name</a>
</div>

Ответ 5

AngularJs 1.5 поддерживает $ctrl по умолчанию для структуры ControllerAs.

$scope.$watch("$ctrl.name", (value) => {
    console.log(value)
});

Ответ 6

Вот как вы это делаете без $scope (и $watch!) Топ-5 ошибок - Нарушение часов

Если вы используете синтаксис "контроллер как", лучше и чище избегать использования $scope.

Вот мой код в JSFiddle. (Я использую службу для хранения имени, иначе ES5 Object.defineProperty и методы get вызывают бесконечные вызовы.

var app = angular.module('my-module', []);
app.factory('testService', function() {
var name = 'Max';
var getName = function() {
    return name;
}
var setName = function(val) {
    name = val;
} 
return {getName:getName, setName:setName};
});
app.controller('TestCtrl', function (testService) {
var vm = this;

vm.changeName = function () {
    vm.name = new Date();
}

Object.defineProperty(this, "name", {
    enumerable: true,
    configurable: false,
    get: function() {
        return testService.getName();
    },
    set: function (val) {
        testService.setName(val);
        console.log(vm.name);
    }
}); 

});

Ответ 7

Написание $watch в синтаксисе ES6 было не так просто, как я ожидал. Вот что вы можете сделать:

// Assuming
// controllerAs: "ctrl"
// or
// ng-controller="MyCtrl as ctrl"
export class MyCtrl {
  constructor ($scope) {
    'ngInject';
    this.foo = 10;
    // Option 1
    $scope.$watch('ctrl.foo', this.watchChanges());
    // Option 2
    $scope.$watch(() => this.foo, this.watchChanges());
  }

  watchChanges() {
    return (newValue, oldValue) => {
      console.log('new', newValue);
    }
  }
}

Ответ 8

вы можете передать функцию как первый аргумент $watch():

 app.controller('TestCtrl', function ($scope) {
 this.name = 'Max';

// hmmm, a function
 $scope.$watch(function () {}, function (value){ console.log(value) });
 });

Это означает, что мы можем вернуть нашу ссылку this.name:

app.controller('TestCtrl', function ($scope) {
    this.name = 'Max';

    // boom
    $scope.$watch(angular.bind(this, function () {
    return this.name; // `this` IS the `this` above!!
    }), function (value) {
      console.log(value);
    });
});

Прочтите интересный пост о контроллере. В разделе https://toddmotto.com/digging-into-angulars-controller-as-syntax/

Ответ 9

Вы можете использовать $onChanges angular жизненный цикл компонента.

см. документацию здесь: https://docs.angularjs.org/guide/component в разделе Компонентное приложение

Ответ 10

ПРИМЕЧАНИЕ. Это не работает, если View и Controller связаны в маршруте или через объект определения директивы. То, что показано ниже, работает только тогда, когда в HTML есть "SomeController as SomeCtrl". Точно так же, как Марк В. указывает в комментарии ниже, и так же, как он говорит, что лучше делать, как это делает Богдан.

Я использую: var vm = this; в начале контроллера, чтобы получить слово "this" из моего пути. Тогда vm.name = 'Max'; и в часах я return vm.name. Я использую "vm" так же, как @Bogdan использует "я". Этот var, будь то "vm" или "self", требуется, поскольку слово "this" принимает другой контекст внутри функции. (так что возврат этого.имя не будет работать). И да, вам нужно ввести $scope в ваше прекрасное решение "контроллер как", чтобы получить $watch. См. Руководство по стилю Джона Папы: https://github.com/johnpapa/angularjs-styleguide#controllers

function SomeController($scope, $log) {
    var vm = this;
    vm.name = 'Max';

    $scope.$watch('vm.name', function(current, original) {
        $log.info('vm.name was %s', original);
        $log.info('vm.name is now %s', current);
    });
}