Angularjs: 'controller as syntax' dan $ watch

153

Bagaimana cara berlangganan perubahan properti saat menggunakan controller assintaks?

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>  
Miron
sumber
bagaimana dengan ini. $ watch ()? Itu valid: $ watch ini ('nama', ...)
Joao Polo

Jawaban:

160

Ikat saja konteks yang relevan.

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

Contoh: http://jsbin.com/yinadoce/1/edit

MEMPERBARUI:

Jawaban Bogdan Gersak sebenarnya setara, kedua jawaban itu mencoba mengikat thisdengan konteks yang benar. Namun, saya menemukan jawabannya lebih bersih.

Setelah mengatakan itu, pertama dan terutama, Anda harus memahami ide yang mendasarinya .

PEMBARUAN 2:

Bagi mereka yang menggunakan ES6, dengan menggunakan arrow functionAnda mendapatkan fungsi dengan konteks yang tepat OOTB.

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

Contoh

Roy Miloh
sumber
9
Bisakah kita menggunakannya tanpa $ scope untuk menghindari campuran ini dan $ scope?
Miron
4
Tidak seperti yang saya tahu, tapi tidak apa-apa. $scopeuntuk Anda adalah jenis layanan yang memasok metode semacam ini.
Roy Miloh
Dapatkah Anda menjelaskan apakah namedalam return this.name;mengacu pada nama kontroler atau properti " name" di sini?
Jannik Jochem
3
@Jannik, angular.bindmengembalikan fungsi dengan konteks terbatas (arg # 1). Dalam kasus kami, kami mengikat this, yang merupakan instance dari controller, ke fungsi (arg # 2), jadi this.nameberarti properti nameinstance dari controller.
Roy Miloh
Saya rasa saya baru mengerti cara kerjanya. Ketika fungsi terikat dipanggil, itu hanya mengevaluasi nilai yang ditonton, kan?
Jannik Jochem
138

Saya biasanya melakukan ini:

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)
   });
});
Nico Napoli
sumber
3
Saya setuju bahwa ini adalah jawaban terbaik, meskipun saya akan menambahkan bahwa kebingungan tentang ini mungkin pada melewati fungsi sebagai argumen pertama $scope.$watchdan menggunakan fungsi itu untuk mengembalikan nilai dari penutupan. Saya belum menemukan contoh lain dari ini, tetapi ini berhasil dan merupakan yang terbaik. Alasan saya tidak memilih jawaban di bawah ini (yaitu, $scope.$watch('test.name', function (value) {});) adalah karena diperlukan bahwa saya membuat hard-code apa yang saya beri nama controller saya di template saya atau di $ stateProvider ui.router dan setiap perubahan di sana secara tidak sengaja akan merusak pengamat.
Morris Singer
Selain itu, satu-satunya perbedaan mendasar antara jawaban ini dan jawaban yang saat ini diterima (yang menggunakan angular.bind) adalah apakah Anda ingin mengikat thisatau hanya menambahkan referensi lain ke thisdalam penutupan. Ini secara fungsional setara, dan, dalam pengalaman saya, pilihan semacam ini sering kali merupakan panggilan subjektif dan masalah pendapat yang sangat kuat.
Morris Singer
1
Satu hal yang menyenangkan tentang ES6 adalah penghapusan harus melakukan 2 solusi yang disebutkan di atas untuk mendapatkan cakupan js yang tepat . $scope.$watch( ()=> { return this.name' }, function(){} ) Panah gemuk untuk menyelamatkan
jusopi
1
Anda juga bisa melakukannya() => this.name
coblr
Bisakah Anda membuat ini bekerja dengan $scope.$watchCollectiondan masih mendapatkan oldVal, newValparams?
Kraken
23

Kamu bisa memakai:

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

Ini berfungsi JSFiddle dengan contoh Anda.

Artyom Pranovich
sumber
25
Masalah dengan pendekatan ini adalah bahwa JS sekarang bergantung pada HTML, memaksa controller untuk terikat sebagai nama yang sama (dalam hal ini "test") di mana-mana agar $ watch bekerja. Akan sangat mudah untuk memperkenalkan bug halus.
jsdw
Ini ternyata bekerja sangat baik jika Anda menulis Angular 1 seperti Angular 2 di mana semuanya adalah arahan. Object.observe akan luar biasa sekarang.
Langdon
13

Mirip dengan menggunakan "tes" dari "TestCtrl sebagai tes", seperti yang dijelaskan dalam jawaban lain, Anda dapat menetapkan "diri" lingkup Anda:

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

Dengan cara ini, Anda tidak terikat dengan nama yang ditentukan dalam DOM ("TestCtrl as test") dan Anda juga menghindari keharusan .bind (this) ke suatu fungsi.

... untuk digunakan dengan html asli yang ditentukan:

<div ng-controller="TestCtrl as test">
    <input type="text" ng-model="test.name" />
    <a ng-click="test.changeName()" href="#">Change Name</a>
</div>
pengguna4389
sumber
Hanya ingin tahu satu hal, yaitu $scopelayanan, jadi Jika kita tambahkan $scope.self = this, maka di pengontrol lain jika kita melakukan hal yang sama, Apa yang akan terjadi di sana?
Vivek Kumar
12

AngularJs 1.5 mendukung $ ctrl default untuk struktur ControllerAs.

$scope.$watch("$ctrl.name", (value) => {
    console.log(value)
});
Niels Steenbeek
sumber
Tidak berfungsi untuk saya ketika menggunakan $ watchGroup, apakah ini batas yang diketahui? dapatkah Anda membagikan tautan ke fitur ini karena saya tidak dapat menemukan apa pun tentangnya.
user1852503
@ user1852503 Lihat docs.angularjs.org/guide/component Tabel perbandingan Arahan / Definisi komponen dan periksa catatan 'controllerAs'.
Niels Steenbeek
Saya mengerti sekarang. Jawaban Anda agak menyesatkan. pengidentifikasi $ ctrl tidak berkorelasi dengan pengontrol sebagai fitur (seperti $ index misalnya dalam ng-repeat), itu hanya merupakan nama default untuk pengontrol di dalam suatu komponen (dan pertanyaannya bahkan bukan tentang komponen).
user1852503
@ user1852503 1) $ ctrl mengkorelasikan Pengontrol (Controller as) 2) Pertanyaannya adalah tentang komponen, karena ia menyebutkan: "<div ng-controller =" TestCtrl as test ">". 3) Semua jawaban pada halaman ini entah bagaimana sama dengan jawaban saya. 4) Mengenai dokumentasi $ watchGroup seharusnya berfungsi dengan baik ketika menggunakan $ ctrl.name karena didasarkan pada $ watch.
Niels Steenbeek
2

Anda benar-benar dapat meneruskan fungsi sebagai argumen pertama dari $ watch ():

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

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

Yang berarti kami dapat mengembalikan referensi this.name kami:

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

Baca posting menarik tentang topik controllerAs https://toddmotto.com/digging-into-angulars-controller-as-syntax/

Alexandr
sumber
0

Menulis $ arloji dalam sintaks ES6 tidak semudah yang saya harapkan. Inilah yang dapat Anda lakukan:

// 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);
    }
  }
}
Maciej Gurban
sumber
-1

CATATAN : Ini tidak berfungsi ketika View dan Controller digabungkan dalam rute atau melalui objek definisi direktif. Apa yang ditampilkan di bawah ini hanya berfungsi ketika ada "SomeController sebagai SomeCtrl" di HTML. Seperti Mark V. tunjukkan dalam komentar di bawah, dan seperti yang dia katakan lebih baik dilakukan seperti yang dilakukan Bogdan.

Saya menggunakan: var vm = this;di awal controller untuk mendapatkan kata "ini" keluar dari jalan saya. Lalu vm.name = 'Max';dan di arloji saya return vm.name. Saya menggunakan "vm" sama seperti @Bogdan menggunakan "diri". Var ini, baik itu "vm" atau "diri" diperlukan karena kata "ini" mengambil konteks yang berbeda di dalam fungsi. (jadi mengembalikan this.name tidak berfungsi) Dan ya, Anda perlu menyuntikkan $ scope pada solusi "controller as" Anda yang indah untuk mencapai $ watch. Lihat Panduan Gaya John Papa: 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);
    });
}
wojja
sumber
11
Ini berfungsi selama Anda memiliki "SomeController as vm" di HTML Anda. Itu menyesatkan, meskipun: "vm.name" dalam ekspresi arloji tidak ada hubungannya dengan "var vm = this;". Satu-satunya cara aman untuk menggunakan $ watch dengan "controller as" adalah dengan melewatkan fungsi sebagai argumen pertama, seperti yang digambarkan oleh Bogdan di atas.
Mark Visser
-1

Inilah cara Anda melakukan ini tanpa $ scope (dan $ watch!) Top 5 Kesalahan - Menonton yang menyalahgunakan

Jika Anda menggunakan sintaks "controller as", lebih baik dan bersih untuk menghindari penggunaan $ scope.

Ini kode saya di JSFiddle . (Saya menggunakan layanan untuk menyimpan nama, jika ES5 Object.defineProperty mengatur dan mendapatkan metode menyebabkan panggilan tak terbatas.

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);
        }
    }); 
});
Binu Jasim
sumber
Biola tidak berfungsi dan ini tidak akan mengamati properti objek.
Rootical V.
@RooticalV. Biola bekerja. (Pastikan bahwa ketika Anda menjalankan AngualrJS, Anda menentukan jenis beban sebagai nowrap-head / nowrap-body
Binu Jasim
maaf tapi saya masih belum berhasil menjalankannya, sayang sekali karena solusi Anda sangat insterersting
happyZZR1400
@ bahagia Pastikan Anda memilih perpustakaan sebagai Angular 1.4. (Saya tidak yakin apakah 2,0 akan berfungsi) dan Muat jenis sebagai Tidak ada bungkus, dan tekan Jalankan. Itu harus bekerja.
Binu Jasim