Angularjs: "контроллер как синтаксис" и $watch
Как подписаться на изменение собственности при использовании 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>
10 ответов
Просто свяжите соответствующий контекст.
$scope.$watch(angular.bind(this, function () {
return this.name;
}), function (newVal) {
console.log('Name changed to ' + newVal);
});
Пример: http://jsbin.com/yinadoce/1/edit
ОБНОВИТЬ:
Ответ Богдана Герсака фактически эквивалентен, оба ответа пытаются связать this
с правильным контекстом. Однако я нашел его ответ чище.
Сказав это, прежде всего, вы должны понять основную идею, стоящую за этим.
ОБНОВЛЕНИЕ 2:
Для тех, кто использует ES6, используя arrow function
Вы получаете функцию с правильным контекстом OOTB.
$scope.$watch(() => this.name, function (newVal) {
console.log('Name changed to ' + newVal);
});
Я обычно делаю это:
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)
});
});
Ты можешь использовать:
$scope.$watch("test.name",function(value){
console.log(value)
});
Это работает JSFiddle с вашим примером.
Аналогично использованию "test" из "TestCtrl как тест", как описано в другом ответе, вы можете назначить "self" вашу область:
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>
AngularJs 1.5 поддерживает $ctrl по умолчанию для структуры ControllerAs.
$scope.$watch("$ctrl.name", (value) => {
console.log(value)
});
Вы можете фактически передать функцию в качестве первого аргумента $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);
});
});
Прочитайте интересный пост о контроллерах как в теме https://toddmotto.com/digging-into-angulars-controller-as-syntax/
Вы можете использовать $onChanges жизненный цикл углового компонента.
см. документацию здесь: https://docs.angularjs.org/guide/component разделе приложений на основе компонентов.
Написание $watch в синтаксисе ES6 оказалось не так просто, как я ожидал. Вот что вы можете сделать:
// 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);
}
}
}
ПРИМЕЧАНИЕ. Это не работает, когда View и Controller связаны в маршруте или через объект определения директивы. То, что показано ниже, работает только тогда, когда в HTML есть "SomeController as SomeCtrl". Точно так же, как Марк В. указывает в комментарии ниже, так же, как он говорит, что лучше делать, как Богдан.
Я использую: var vm = this;
в начале контроллера, чтобы вывести слово "это" из моего пути. затем vm.name = 'Max';
и в часах я return vm.name
, Я использую "vm" так же, как @Bogdan использует "self". Это var, будь то "vm" или "self", необходимо, так как слово "this" имеет другой контекст внутри функции. (поэтому возвращение this.name не сработает) И да, вам нужно ввести $scope в ваше красивое решение "controller as", чтобы достичь $watch. См. Руководство по стилю Джона Папы: https://github.com/johnpapa/angularjs-styleguide
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);
});
}
Вот как вы делаете это без $scope (и $watch!)
Если вы используете синтаксис "контроллер как", лучше и чище избегать использования $scope.
Вот мой код в JSFiddle. (Я использую сервис для хранения имени, иначе методы set и get объекта ES5 Object.defineProperty вызывают бесконечные вызовы.
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);
}
});
});