Angular.js:'控制器作为语法'和$ 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('name',...)
Joao Polo

Answers:


160

只需绑定相关上下文即可。

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

示例:http//jsbin.com/yinadoce/1/edit

更新:

Bogdan Gersak的答案实际上是等效的,两个答案都尝试this在正确的上下文中进行绑定。但是,我发现他的答案更干净。

话虽如此,首先,您必须了解其背后的基本思想

更新2:

对于那些使用ES6的人,通过使用它们arrow function可以获得具有正确上下文OOTB的功能。

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


9
我们可以在没有$ scope的情况下使用它来避免将它与$ scope混合使用吗?
米龙2014年

4
据我所知,没有,但这很好。$scope对您而言,这是一种提供此类方法的服务。
罗伊·米洛

您能否name在此处澄清in return this.name;是指控制器的名称还是属性“ name”?
Jannik Jochem 2014年

3
@Jannik,angular.bind返回具有有限上下文的函数(arg#1)。在我们的例子中,我们将this控制器的实例绑定到函数(arg#2),因此this.name表示name控制器的实例的属性。
罗伊·米洛

我想我只是了解它是如何工作的。当绑定函数被调用时,它只是求值到观察值,对吧?
Jannik Jochem 2014年

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) {});),是因为它要求我在模板或ui.router的$ stateProvider中对我命名的控制器进行硬编码,并且在那里进行的任何更改都会无意中破坏观察程序。
莫里斯·辛格

另外,此答案与当前接受的答案(使用angular.bind)之间的唯一实质性区别是,您是要绑定到闭包this还是直接this在闭包中添加另一个引用。这些功能在功能上是等效的,以我的经验,这种选择通常是主观的,并且是非常强烈的意见。
莫里斯·辛格

1
ES6的一件好事是无需为了获得正确的js范围而必须执行上述两种解决方法。 $scope.$watch( ()=> { return this.name' }, function(){} )
发动

1
您也可以做() => this.name
coblr '16

您可以继续使用它$scope.$watchCollection并仍然获得oldVal, newVal参数吗?
海妖“

23

您可以使用:

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

这使JSFiddle与您的示例一起工作。


25
这种方法的问题在于,JS现在依赖于HTML,从而迫使控制器必须在各处绑定为相同的名称(在本例中为“ test”),以便$ watch能够正常工作。引入细微的错误将非常容易。
jsdw 2014年

如果您要像Angular 2一样编写Angular 1,而一切都是指令,那么这将非常有用。现在,Object.observe将会是惊人的。
兰登2015年

13

如在另一个答案中所述,类似于使用“ TestCtrl作为测试”中的“测试”,您可以为范围分配“自我”:

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 as test”),并且还避免了将.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,那么如果在另一个控制器中执行相同的操作,那将会发生什么?
Vivek Kumar

12

AngularJs 1.5支持ControllerAs结构的默认$ ctrl。

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

使用$ watchGroup时对我不起作用,这是已知的限制吗?您能否共享此功能的链接,因为我找不到任何有关此功能的信息。
user1852503 16-10-24

@ user1852503请参阅docs.angularjs.org/guide/component比较表指令/组件定义,并检查“ controllerAs”记录。
Niels Steenbeek '16

我现在知道了。您的答案有点误导。标识符$ ctrl与控制器的功能不相关(例如$ index在ng-repeat中确实如此),它恰好是组件内控制器的默认名称(而且问题甚至与零件)。
user1852503 16-10-24

@ user1852503 1)$ ctrl与控制器(Controller as)相关联2)问题与组件有关,因为它提到:“ <div ng-controller =” TestCtrl as test“>”。3)此页面上的所有答案均与我的答案相同。4)关于文档$ watchGroup应该在使用$ ctrl.name时正常工作,因为它基于$ watch。
Niels Steenbeek '16

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

阅读有关controllerAs主题的有趣帖子https://toddmotto.com/digging-into-angulars-controller-as-syntax/



0

用ES6语法编写$ watch并不像我期望的那么容易。您可以执行以下操作:

// 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中包含“ SomeCtrl as SomeCtrl”时,下面显示的内容才起作用。正如Mark V.在下面的评论中指出的那样,就像他说的那样,最好像Bogdan那样做。

我使用:var vm = this;在控制器的开头,将“ this”一词排除在外。然后vm.name = 'Max';在手表里return vm.name。我使用“ vm”,就像@Bogdan使用“ self”一样。这个变量是“ vm”还是“ self”,因为单词“ this”在函数内部具有不同的上下文。(因此,返回this.name无效),是的,您需要在漂亮的“ controller as”解决方案中注入$ scope才能达到$ watch。参见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
只要您的HTML中包含“ SomeController as vm”,此方法就起作用。但是,这具有误导性:监视表达式中的“ vm.name”与“ var vm = this;”无关。如上Bogdan所示,将$ watch与“ controller as”一起使用的唯一安全方法是将函数作为第一个参数传递。
Mark Visser

-1

这是在没有$ scope(和$ watch!)的情况下如何执行此操作的前5个错误-滥用手表

如果使用“ controller as”语法,则最好避免使用$ scope。

这是我在JSFiddle中的代码。(我正在使用服务来保存名称,否则,ES5 Object.defineProperty的set和get方法会导致无限调用。

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头/ NOWRAP体
BINU贾西姆

抱歉,但我仍然没有设法运行它,太可惜了,因为您的解决方案非常麻烦
happyZZR1400

@happy确保您选择的库为Angular 1.4。(我不确定2.0是否会工作)并将“加载类型”设置为“无环绕”,然后按“运行”。它应该工作。
Binu Jasim
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.