Angularjs: 'controller as syntax' i $ watch


153

Jak zasubskrybować zmianę właściwości przy użyciu controller asskładni?

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>  

co z tym. $ watch ()? To ważne: to. $ Watch ('name', ...)
— Joao Polo

Odpowiedzi:


160

Po prostu połącz odpowiedni kontekst.

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

Przykład: http://jsbin.com/yinadoce/1/edit

AKTUALIZACJA:

Odpowiedź Bogdana Gersaka jest właściwie odpowiednikiem, obie odpowiedzi starają się połączyć z odpowiednim thiskontekstem. Jednak jego odpowiedź była dla mnie czystsza.

Mając to na uwadze, przede wszystkim musisz zrozumieć leżącą u podstaw ideę .

AKTUALIZACJA 2:

Dla tych, którzy używają ES6, używając arrow functiondostaniesz funkcję z odpowiednim kontekstem OOTB.

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

Przykład


9
Czy możemy go użyć bez $ scope, aby uniknąć pomieszania this i $ scope?
— Miron

4
Nie, jak wiem, ale jest w porządku. $scopedla ciebie jest to rodzaj usługi, która dostarcza tego rodzaju metody.
— Roy Miloh,

Czy możesz wyjaśnić, czy namew return this.name;odnosi się tutaj do nazwy administratora lub właściwości „ name”?
— Jannik Jochem

3
@Jannik, angular.bindzwraca funkcję z ograniczonym kontekstem (arg # 1). W naszym przypadku łączymy się this, który jest instancją kontrolera, z funkcją (arg # 2), a więc this.nameoznacza własność nameinstancji kontrolera.
— Roy Miloh

Myślę, że właśnie zrozumiałem, jak to działa. Kiedy wywoływana jest funkcja związana, po prostu zwraca się do obserwowanej wartości, prawda?
— Jannik Jochem

138

Zwykle robię to:

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
Zgadzam się, że to najlepsza odpowiedź, chociaż dodam, że niejasność jest prawdopodobnie związana z przekazaniem funkcji jako pierwszym argumentem $scope.$watchi użyciem tej funkcji do zwrócenia wartości z zamknięcia. Nie spotkałem jeszcze innego przykładu, ale działa i jest najlepszy. Powodem, dla którego nie wybrałem odpowiedzi poniżej (tj. $scope.$watch('test.name', function (value) {});), Jest to, że wymagało to trwałego zakodowania tego, co nazwałem mój kontroler w moim szablonie lub w $ stateProvider ui.router, a każda zmiana spowodowałaby nieumyślne uszkodzenie obserwatora.
— Morris Singer

Jedyną istotną różnicą między tą odpowiedzią a obecnie akceptowaną odpowiedzią (która używa angular.bind) jest to, czy chcesz się powiązać, thisczy po prostu dodać kolejne odniesienie do thiszamknięcia. Są one funkcjonalnie równoważne iz mojego doświadczenia wynika, że ​​tego rodzaju wybór jest często subiektywnym wezwaniem i sprawą bardzo mocnej opinii.
— Morris Singer

1
jedną fajną rzeczą w ES6 będzie wyeliminowanie konieczności wykonywania dwóch wyżej wymienionych obejść, aby uzyskać właściwy zakres js . $scope.$watch( ()=> { return this.name' }, function(){} ) Gruba strzała na ratunek
— jusopi

1
możesz też po prostu zrobić() => this.name
— coblr

Czy możesz sprawić, by to działało $scope.$watchCollectioni nadal mieć parametry oldVal, newVal?
— Kraken

23

Możesz użyć:

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

To działa JSFiddle z twoim przykładem.


25
Problem z tym podejściem polega na tym, że JS polega teraz na kodzie HTML, zmuszając kontroler do powiązania go z tą samą nazwą (w tym przypadku „test”) wszędzie, aby $ watch działał. Bardzo łatwo byłoby wprowadzić subtelne błędy.
— jsdw

Okazuje się, że działa cudownie, jeśli piszesz Angular 1 jak Angular 2, w którym wszystko jest dyrektywą. Jednak Object.observe byłoby teraz niesamowite.
— Langdon

13

Podobnie jak przy użyciu opcji „test” z „TestCtrl jako test”, jak opisano w innej odpowiedzi, możesz przypisać zakres „samodzielnie”:

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

W ten sposób nie jesteś przywiązany do nazwy określonej w DOM („TestCtrl jako test”), a także unikasz konieczności wiązania .bind (this) z funkcją.

... do użytku z określonym oryginalnym kodem HTML:

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

Chcę tylko wiedzieć, że $scopejest to usługa, więc jeśli dodamy $scope.self = this, to w innym kontrolerze, jeśli zrobimy to samo, co się tam stanie?
— Vivek Kumar

12

AngularJs 1.5 obsługuje domyślną $ ctrl dla struktury ControllerAs.

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

Nie działa dla mnie, gdy używam $ watchGroup, czy to znany limit? czy możesz udostępnić link do tej funkcji, ponieważ nie mogę nic znaleźć na jej temat.
— user1852503

@ user1852503 Zobacz docs.angularjs.org/guide/component Tabela porównawcza Dyrektywa / Definicja komponentu i sprawdź rekord „controllerAs”.
— Niels Steenbeek

Teraz rozumiem. Twoja odpowiedź jest nieco myląca. identyfikator $ ctrl nie koreluje ze sterownikiem jako funkcją (tak jak $ index robi to na przykład w ng-repeat), po prostu jest to domyślna nazwa kontrolera wewnątrz komponentu (a pytanie nie dotyczy nawet składnik).
— user1852503

@ user1852503 1) $ ctrl koreluje kontroler (kontroler jako) 2) Pytanie dotyczy komponentów, ponieważ wspomina: "<div ng-controller =" TestCtrl as test ">". 3) Wszystkie odpowiedzi na tej stronie są w pewnym sensie takie same jak moja odpowiedź. 4) Jeśli chodzi o dokumentację, $ watchGroup powinien działać dobrze, gdy używa się $ ctrl.name, ponieważ jest oparty na $ watch.
— Niels Steenbeek

2

faktycznie możesz przekazać funkcję jako pierwszy argument $ watch ():

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

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

Co oznacza, że ​​możemy zwrócić odwołanie 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);
    });
});

Przeczytaj interesujący post o kontrolerze W temacie https://toddmotto.com/digging-into-angulars-controller-as-syntax/



0

Napisanie zegarka $ watch w składni ES6 nie było tak łatwe, jak się spodziewałem. Oto, co możesz zrobić:

// 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

UWAGA : To nie działa, gdy widok i kontroler są połączone w trasie lub za pośrednictwem obiektu definicji dyrektywy. To, co pokazano poniżej, działa tylko wtedy, gdy w kodzie HTML znajduje się „SomeController as SomeCtrl”. Tak jak zaznacza Mark V. w komentarzu poniżej, i tak jak mówi, lepiej robić tak, jak robi to Bogdan.

Używam: var vm = this;na początku kontrolera, aby słowo „to” zniknęło mi z drogi. Wtedy vm.name = 'Max';i na zegarku ja return vm.name. Używam „vm” tak samo, jak @Bogdan używa „self”. Ta zmienna, czy to „vm”, czy „self”, jest potrzebna, ponieważ słowo „this” przyjmuje inny kontekst wewnątrz funkcji. (więc zwrócenie this.name nie zadziałałoby) I tak, musisz wstrzyknąć $ scope do swojego pięknego rozwiązania „controller as”, aby dotrzeć do $ watch. Zobacz przewodnik po stylu Johna Papy: 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
Działa to tak długo, jak długo masz w kodzie HTML „SomeController as vm”. Jest to jednak mylące: „vm.name” w wyrażeniu obserwującym nie ma nic wspólnego z „var vm = this;”. Jedynym bezpiecznym sposobem użycia $ watch z "kontrolerem jako" jest przekazanie funkcji jako pierwszego argumentu, jak to ilustruje Bogdan.
— Mark Visser

-1

Oto jak to zrobić bez $ scope (i $ watch!) Top 5 błędów - nadużywanie zegarka

Jeśli używasz składni „kontroler jako”, lepiej i czyściej unikać używania $ scope.

Oto mój kod w JSFiddle . (Używam usługi do przechowywania nazwy, w przeciwnym razie metody set i get ES5 Object.defineProperty powodują nieskończone wywołania.

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

Skrzypce nie działają i nie spowoduje to zaobserwowania właściwości obiektu.
— Rootical V.

@RooticalV. Skrzypce działa. (Upewnij się, że gdy używasz AngualrJS należy określić rodzaj obciążenia jako nowrap głowicy / nowrap-ciała
— Binu Jasim

przepraszam, ale nadal nie udało mi się go uruchomić, szkoda, ponieważ twoje rozwiązanie jest bardzo insterersting
— happyZZR1400

@happy Upewnij się, że wybrałeś bibliotekę jako Angular 1.4. (Nie jestem pewien, czy 2.0 zadziała) i Załaduj jako Bez zawijania i naciśnij Uruchom. Powinno działać.
— Binu Jasim,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.