Angularjs: 'bộ điều khiển như cú pháp' và $ watch


153

Làm thế nào để đăng ký thay đổi thuộc tính khi sử dụng controller ascú pháp?

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>  

Thế còn cái này. $ watch ()? Nó hợp lệ: cái này. $ Watch ('name', ...)
Joao Polo

Câu trả lời:


160

Chỉ cần ràng buộc bối cảnh có liên quan.

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

Ví dụ: http://jsbin.com/yinadoce/1/edit

CẬP NHẬT:

Câu trả lời của Bogdan Gersak thực sự là loại tương đương, cả hai câu trả lời đều cố gắng ràng buộc thisvới đúng ngữ cảnh. Tuy nhiên, tôi thấy câu trả lời của anh sạch hơn.

Điều đó nói rằng, đầu tiên và quan trọng nhất, bạn phải hiểu ý tưởng tiềm ẩn đằng sau nó .

CẬP NHẬT 2:

Đối với những người sử dụng ES6, bằng cách sử dụng, arrow functionbạn có được một hàm với ngữ cảnh OOTB phù hợp.

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

Thí dụ


9
Chúng ta có thể sử dụng nó mà không có phạm vi $ để tránh kết hợp phạm vi này và phạm vi $ không?
Miron

4
Không như tôi biết, nhưng nó hoàn toàn ổn. $scopeđối với bạn là một loại dịch vụ cung cấp các loại phương pháp này.
Roy Miloh

Bạn có thể làm rõ liệu nametrong return this.name;đề cập đến tên của bộ điều khiển hoặc thuộc tính " name" ở đây không?
Jannik Joool

3
@Jannik, angular.bindtrả về một hàm có ngữ cảnh giới hạn (arg # 1). Trong trường hợp của chúng tôi, chúng tôi liên kết this, là thể hiện của bộ điều khiển, với hàm (arg # 2), this.namecó nghĩa là thuộc tính namecủa thể hiện của bộ điều khiển.
Roy Miloh

Tôi nghĩ rằng tôi chỉ hiểu làm thế nào điều này hoạt động. Khi hàm ràng buộc được gọi, nó chỉ đơn giản là đánh giá giá trị đã xem, phải không?
Jannik Joool

138

Tôi thường làm điều này:

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
Tôi đồng ý rằng đây là câu trả lời tốt nhất, mặc dù tôi sẽ nói thêm rằng sự nhầm lẫn về điều này có lẽ là do truyền một hàm làm đối số đầu tiên trong $scope.$watchvà sử dụng hàm đó để trả về giá trị từ bao đóng. Tôi chưa chạy qua một ví dụ khác về điều này, nhưng nó hoạt động và là tốt nhất. Lý do tôi không chọn câu trả lời dưới đây (nghĩa là $scope.$watch('test.name', function (value) {});) là vì tôi yêu cầu tôi phải mã hóa cái mà tôi đặt tên cho bộ điều khiển của mình trong mẫu của tôi hoặc trong $ stateProvider của ui.router và mọi thay đổi sẽ vô tình phá vỡ trình theo dõi.
Ca sĩ Morris

Ngoài ra, sự khác biệt đáng kể duy nhất giữa câu trả lời này và câu trả lời hiện được chấp nhận (sử dụng angular.bind) là bạn có muốn liên kết thishoặc chỉ đơn giản là thêm một tham chiếu khác vào thistrong bao đóng. Đây là những chức năng tương đương, và theo kinh nghiệm của tôi, loại lựa chọn này thường là một cuộc gọi chủ quan và vấn đề ý kiến ​​rất mạnh mẽ.
Ca sĩ Morris

1
một điều tốt đẹp về ES6 sẽ là loại bỏ việc phải thực hiện 2 cách giải quyết đã nói ở trên để có được phạm vi js phù hợp . $scope.$watch( ()=> { return this.name' }, function(){} ) Mũi tên béo để giải cứu
jusopi

1
bạn cũng có thể làm() => this.name
coblr

Bạn có thể làm cho công việc này với $scope.$watchCollectionvà vẫn nhận được thông số oldVal, newVal?
Kraken

23

Bạn có thể dùng:

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

Điều này đang làm việc với JSFiddle với ví dụ của bạn.


25
Vấn đề với cách tiếp cận này là hiện tại, JS đang dựa vào HTML, buộc bộ điều khiển bị ràng buộc như cùng tên (trong trường hợp này là "thử nghiệm") ở mọi nơi để đồng hồ $ hoạt động. Sẽ rất dễ dàng để giới thiệu các lỗi tinh tế.
jsdw

Điều này hóa ra hoạt động tuyệt vời nếu bạn đang viết Angular 1 như Angular 2 trong đó mọi thứ đều là một chỉ thị. Object.observe sẽ là tuyệt vời ngay bây giờ mặc dù.
Langdon

13

Tương tự như sử dụng "kiểm tra" từ "TestCtrl làm kiểm tra", như được mô tả trong câu trả lời khác, bạn có thể chỉ định "tự" phạm vi của mình:

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

Theo cách này, bạn không bị ràng buộc với tên được chỉ định trong DOM ("TestCtrl là thử nghiệm") và bạn cũng tránh được yêu cầu .bind (cái này) cho một hàm.

... Để sử dụng với html gốc được chỉ định:

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

Chỉ muốn biết một điều, tức $scopelà một dịch vụ, vì vậy, nếu chúng ta thêm $scope.self = this, thì trong một bộ điều khiển khác nếu chúng ta làm tương tự, Điều gì sẽ xảy ra ở đó?
Vivek Kumar

12

AngularJs 1.5 hỗ trợ $ ctrl mặc định cho cấu trúc ControllerAs.

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

Không hoạt động với tôi khi sử dụng $ watchgroup, đây có phải là giới hạn đã biết không? bạn có thể chia sẻ một liên kết đến tính năng này vì tôi không thể tìm thấy bất cứ điều gì về nó.
dùng1852503

@ user1852503 Xem docs.angularjs.org/guide/component Bảng so sánh Định nghĩa chỉ thị / Thành phần và kiểm tra bản ghi ' controlAs '.
Niels Steenbeek

Giờ thì tôi đã hiểu. Câu trả lời của bạn là một chút sai lệch. mã định danh $ ctrl không tương quan với bộ điều khiển như một tính năng (ví dụ như $ index chẳng hạn trong lặp lại ng), nó chỉ là tên mặc định cho bộ điều khiển bên trong một thành phần (và câu hỏi thậm chí không phải là về một thành phần).
dùng1852503

@ user1852503 1) $ ctrl tương quan với Bộ điều khiển (Bộ điều khiển như) 2) Câu hỏi là về các thành phần, vì nó đề cập: "<div ng-controller =" TestCtrl là thử nghiệm ">". 3) Tất cả các anwers trên trang này bằng cách nào đó giống như câu trả lời của tôi. 4) Về tài liệu $ watchgroup chỉ nên hoạt động tốt khi sử dụng $ ctrl.name vì nó dựa trên $ watch.
Niels Steenbeek

2

bạn thực sự có thể chuyển vào một hàm làm đối số đầu tiên của $ watch ():

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

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

Điều đó có nghĩa là chúng ta có thể trả về tham chiếu 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);
    });
});

Đọc một bài viết thú vị về bộ điều khiểnAs chủ đề https://toddmotto.com/digging-into-angenses-controll-as-syntax/



0

Viết một chiếc đồng hồ $ theo cú pháp ES6 không dễ như tôi mong đợi. Đây là những gì bạn có thể làm:

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

LƯU Ý : Điều này không hoạt động khi Chế độ xem và Bộ điều khiển được ghép nối trong một tuyến hoặc thông qua một đối tượng định nghĩa chỉ thị. Những gì hiển thị bên dưới chỉ hoạt động khi có "Một số Trình điều khiển là Một số Ctrl" trong HTML. Giống như Mark V. chỉ ra trong bình luận bên dưới, và như anh ấy nói rằng tốt hơn là làm như Bogdan làm điều đó.

Tôi sử dụng: var vm = this;vào đầu bộ điều khiển để tránh từ "này" theo cách của tôi. Sau đó vm.name = 'Max';và trong đồng hồ tôi return vm.name. Tôi sử dụng "vm" giống như @Bogdan sử dụng "tự". Var này, có thể là "vm" hoặc "self" là cần thiết vì từ "này" có một ngữ cảnh khác bên trong hàm. (vì vậy, trả lại this.name sẽ không hoạt động) Và vâng, bạn cần đưa $ scope vào giải pháp "điều khiển" đẹp mắt của mình để đạt được $ watch. Xem Hướng dẫn về Phong cách của 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
Điều này hoạt động miễn là bạn có "Một số Trình điều khiển như vm" trong HTML của mình. Tuy nhiên, đó là sự hiểu lầm: "vm.name" trong biểu thức đồng hồ không liên quan gì đến "var vm = this;". Cách an toàn duy nhất để sử dụng $ watch với "control as" là truyền một hàm làm đối số đầu tiên, như Bogdan minh họa ở trên.
Mark Visser

-1

Dưới đây là cách bạn thực hiện việc này mà không có $ scope (và $ watch!) Top 5 sai lầm - Đồng hồ lạm dụng

Nếu bạn đang sử dụng cú pháp "trình điều khiển như", tốt hơn hết là tránh sử dụng $ scope.

Đây là mã của tôi trong JSFiddle . (Tôi đang sử dụng một dịch vụ để giữ tên, nếu không, bộ ES5 Object.defineProperty và các phương thức gây ra các cuộc gọi vô hạn.

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

Fiddle không hoạt động và điều này sẽ không quan sát một thuộc tính đối tượng.
Root V.

@RooticalV. Fiddle đang làm việc. (Đảm bảo rằng khi bạn đang chạy AngualrJS, bạn chỉ định loại tải là nowrap-head / nowrap-body
Binu Jasim

xin lỗi nhưng tôi vẫn không quản lý được nó, thật đáng tiếc vì giải pháp của bạn rất khó hiểu
happyZZR1400

@happy Đảm bảo bạn chọn thư viện là Angular 1.4. (Tôi không chắc liệu 2.0 có hoạt động không) và loại Tải là Không gói và nhấn Run. Nó nên hoạt động.
Binu Jasim
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.