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.