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>  

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


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

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.


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>

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

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/



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

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

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

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.