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


153

Як підписатися на зміну властивості при використанні 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>  

що з цим. $ watch ()? Це дійсно: this. $ Watch ('ім'я', ...)
— Жоао Поло

Відповіді:


160

Просто зв’яжіть відповідний контекст.

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

Приклад: http://jsbin.com/yinadoce/1/edit

ОНОВЛЕННЯ:

Відповідь Богдана Герсака насправді є рівнозначною, обидві відповіді намагаються зв’язати thisз правильним контекстом. Однак я знайшов його відповідь чистішою.

Зважаючи на це, перш за все, ви повинні зрозуміти основну ідею, що стоїть за нею .

ОНОВЛЕННЯ 2:

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

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

Приклад


9
Чи можемо ми використовувати його без $ range, щоб уникнути поєднання цього та $ range?
— Мирон

4
Ні, як я знаю, але це прекрасно. $scopeдля вас - це свого роду послуга, яка надає такі методи.
— Рой Мілох

Ви можете з'ясувати , є чи nameв return this.name;посилається на ім'я контролера або властивості « name» тут?
— Jannik Jochem

3
@Jannik, angular.bindповертає функцію з обмеженим контекстом (арг. №1). У нашому випадку ми прив'язуємо thisдо функції (аргумент №2), this.nameтобто властивість nameекземпляра контролера.
— Рой Мілох

Я думаю, що я просто зрозумів, як це працює. Коли викликається пов'язана функція, вона просто оцінює спостережуване значення, правда?
— Jannik Jochem

138

Я зазвичай роблю це:

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і використання цієї функції для повернення значення із закриття. Мені ще належить наткнутися на інший приклад цього, але він працює і є найкращим. Причина, що я не вибрав відповідь нижче (тобто $scope.$watch('test.name', function (value) {});), полягає в тому, що потрібно, щоб я жорстко кодував те, що я назвав своїм контролером у своєму шаблоні або в $ stateProvider ui.router, і будь-яка зміна там ненавмисно зламала годинника.
— Морріс Сінгер

Крім того, єдина істотна відмінність між цією відповіддю та прийнятою на даний момент відповіддю (яка використовує angular.bind) полягає в тому, чи потрібно ви прив’язуватись thisабо просто додавати інше посилання в thisмежах закриття. Вони є функціонально еквівалентними, і, на мій досвід, такий вибір часто є суб'єктивним закликом і питанням дуже сильної думки.
— Морріс Сінгер

1
одна приємна річ про ES6 - це усунення необхідності виконувати 2 вищезгадані обхідні шляхи, щоб отримати правильну область js . $scope.$watch( ()=> { return this.name' }, function(){} ) Жирна стрілка на допомогу
— jusopi

1
ви також можете просто зробити() => this.name
— coblr

Чи можете ви змусити цю роботу $scope.$watchCollectionі все-таки отримати oldVal, newValпарами?
— Кракен

23

Ви можете використовувати:

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

Це працює JSFiddle з вашим прикладом.


25
Проблема такого підходу полягає в тому, що JS тепер покладається на HTML, змушуючи повсюдно пов'язати контролер як те саме ім'я (в даному випадку "тест"), щоб $ watch працював. Було б дуже легко ввести непомітні помилки.
— jsdw

Це виявляється чудово, якщо ви пишете Angular 1, як Angular 2, де все є директивою. Об'єкт.спостереження був би дивним зараз.
— Ленґдон

13

Аналогічно використанню "test" від "TestCtrl as test", як описано в іншій відповіді, ви можете призначити "self" вашій області застосування:

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 як тест"), і ви також уникаєте необхідності .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>

Просто хочу знати одне, тобто $scopeпослуга, тож якщо ми додамо $scope.self = this, то в іншому контролері, якщо ми зробимо те саме, що буде там?
— Вівек Кумар

12

AngularJs 1.5 підтримує $ ctrl за замовчуванням для структури ControllerAs.

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

Не працює для мене при використанні $ watchGroup, це відомий ліміт? Ви можете поділитися посиланням на цю функцію, оскільки я нічого не можу знайти про це.
— користувач1852503

@ user1852503 Див. docs.angularjs.org/guide/component Таблиця порівняння Директива / Визначення компонента та перевірити запис "контролераА".
— Нільс Стінбек

Я тепер розумію. Ваша відповідь трохи оманлива. ідентифікатор $ ctrl не співвідноситься з контролером як особливістю (як, наприклад, $ index робить, наприклад, у ng-повторенні), це просто відбувається ім'я за замовчуванням для контролера всередині компонента (і питання навіть не в а компонент).
— користувач1852503

@ user1852503 1) $ ctrl співвідносить контролер (Controller as) 2) Питання стосується компонентів, оскільки в ньому зазначається: "<div ng-controller =" TestCtrl як тест ">". 3) Усі відповіді на цій сторінці якось збігаються з моєю відповіддю. 4) Щодо документації $ watchGroup повинен просто працювати добре при використанні $ ctrl.name, оскільки вона заснована на $ watch.
— Нільс Стінбек

2

ви можете передати функцію як перший аргумент $ 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/



0

Написати годинник $ у синтаксисі 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);
    }
  }
}

-1

ПРИМІТКА . Це не працює, коли View і Controller з'єднані в маршруті або через об'єкт визначення директиви. Показане нижче працює лише тоді, коли в HTML є "SomeController as SomeCtrl". Так само, як Марк В. зазначає в коментарі нижче, і так само, як він каже, краще зробити так, як це робить Богдан.

Я використовую: var vm = this;на початку контролер, щоб отримати слово "це" зі свого шляху. Тоді vm.name = 'Max';і на вахті я return vm.name. Я використовую "vm" так само, як @Bogdan використовує "self". Цей var, будь то "vm" або "self", потрібен, оскільки слово "this" набуває іншого контексту всередині функції. (тому повернення цього імені не буде працювати) І так, вам потрібно ввести $ range у вашому прекрасному рішенні "контролер як", щоб досягти $ 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);
    });
}

11
Це працює, доки у вашому HTML "SomeController як vm" є. Однак це вводить в оману: "vm.name" у виразі watch не має нічого спільного з "var vm = this;". Єдиний безпечний спосіб використання $ watch з "контролером як" - це передача функції в якості першого аргументу, як Богдан ілюструє вище.
— Марк Візер

-1

Ось як ви це робите без $ range (і $ watch!) Топ-5 помилок - зловживання годинником

Якщо ви використовуєте синтаксис "контролер як", краще і чіткіше уникати використання $ range.

Ось мій код у JSFiddle . (Я використовую службу для утримання імені, інакше набір і отримання методів ES5 Object.defineProperty викликає нескінченні дзвінки.

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);
        }
    }); 
});

Загадка не працює, і це не буде спостерігати властивість об'єкта.
— Rootical V.

@RooticalV. Загадка справна. (Переконайтесь, що під час запуску AngualrJS ви вказуєте тип навантаження як nowrap-head / nowrap-body
— Binu Jasim,

вибачте, але мені все-таки не вдалося його запустити, така шкода, оскільки ваше рішення дуже настирливе
— happyZZR1400

@happy Переконайтесь, що ви вибрали бібліотеку як Angular 1.4. (Я не впевнений, чи працюватиме 2.0) та введіть Load як No wrap та натисніть Виконати. Це має працювати.
— Біну Ясим
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.