Komponenty AngularJS 1.5+ nie obsługują Watcherów, na czym polega praca?
Uaktualniałem własne dyrektywy do nowejarchitektury komponentów . Czytałem, że komponenty nie obsługują obserwatorów. Czy to prawda? Jeśli tak, jak wykrywasz zmiany na obiekcie? Dla podstawowego przykładu mam własny komponent myBox
, który ma komponent potomny gry z wiązaniem na grę . Jeśli w komponencie gry występuje zmiana gry, Jak mogę wyświetlić komunikat alertu w myBox? Rozumiem, że jest metoda rxJS czy można to zrobić czysto kanciaste? My JSFiddle
JavaScript
var app = angular.module('myApp', []);
app.controller('mainCtrl', function($scope) {
$scope.name = "Tony Danza";
});
app.component("myBox", {
bindings: {},
controller: function($element) {
var myBox = this;
myBox.game = 'World Of warcraft';
//IF myBox.game changes, show alert message 'NAME CHANGE'
},
controllerAs: 'myBox',
templateUrl: "/template",
transclude: true
})
app.component("game", {
bindings: {game:'='},
controller: function($element) {
var game = this;
},
controllerAs: 'game',
templateUrl: "/template2"
})
HTML
<div ng-app="myApp" ng-controller="mainCtrl">
<script type="text/ng-template" id="/template">
<div style='width:40%;border:2px solid black;background-color:yellow'>
Your Favourite game is: {{myBox.game}}
<game game='myBox.game'></game>
</div>
</script>
<script type="text/ng-template" id="/template2">
<div>
</br>
Change Game
<textarea ng-model='game.game'></textarea>
</div>
</script>
Hi {{name}}
<my-box>
</my-box>
</div><!--end app-->
7 answers
Pisanie komponentów bez obserwatorów
Ta odpowiedź przedstawia pięć technik, których można użyć do napisania komponentów AngularJS 1.5 bez użycia obserwatorów.
- użyj
ng-change
Dyrektywa - użyj
$onChanges
Life-cycle Hook - użyj
$doCheck
Life-cycle Hook - Komunikacja Międzykomponentowa z wymaga
- wypychanie wartości z usługi z RxJS
Użyj ng-change
dyrektywy
Jakie dostępne metody alt do obserwacji zmian stanu obj bez użycia zegarka w przygotowaniu do AngularJs2?
Możesz użyć dyrektywy ng-change
, aby reagować na zmiany danych wejściowych.
<textarea ng-model='game.game'
ng-change="game.textChange(game.game)">
</textarea>
I aby propagować Zdarzenie do komponentu nadrzędnego, należy dodać obsługę zdarzenia jako atrybut komponentu podrzędnego.
<game game='myBox.game' game-change='myBox.gameChange($value)'></game>
JS
app.component("game", {
bindings: {game:'=',
gameChange: '&'},
controller: function() {
var game = this;
game.textChange = function (value) {
game.gameChange({$value: value});
});
},
controllerAs: 'game',
templateUrl: "/template2"
});
I w rodzicu Składnik:
myBox.gameChange = function(newValue) {
console.log(newValue);
});
Jest to preferowana metoda w przyszłości. Strategia AngularJS użycia $watch
nie jest skalowalna, ponieważ jest to strategia ankietowa. Gdy liczba słuchaczy $watch
osiągnie około 2000, interfejs użytkownika staje się powolny. Strategia w grze Angular 2 polega na tym, aby framework był bardziej reaktywny i unikał umieszczania $watch
na $scope
.
Użyj haka $onChanges
Life-cycle Hook
Z Wersja 1.5.3 , AngularJS dodał $onChanges
life-cycle hook do $compile
obsługa.
From the Docs:
[[42]}kontroler może dostarczyć następujące metody, które działają jak haki cyklu życia:
- $onChanges (changesObj)-wywoływane za każdym razem, gdy aktualizowane są wiązania jednokierunkowe (
<
) lub interpolacyjne (@
).changesObj
jest skrótem, którego klucze są nazwami zmienionych właściwości, a wartości są obiektem w postaci{ currentValue: ..., previousValue: ... }
. Użyj tego Hooka, aby wywołać aktualizacje wewnątrz komponentu, takie jak klonowanie wartości związanej z zapobiec przypadkowej mutacji wartości zewnętrznej.- AngularJS Comprehensive Directive API Reference -- life-cycle hooks
Hook $onChanges
jest używany do reagowania na zewnętrzne zmiany w komponencie za pomocą <
wiązań jednokierunkowych. Dyrektywa ng-change
służy do propogatowania zmian ze sterownika ng-model
poza komponentem za pomocą wiązań &
.
Użyj haka $doCheck
Life-cycle Hook
Z wersja 1.5.8 , AngularJS dodano hak cyklu życia $doCheck
do usługi $compile
.
From the Docs:
[[42]}kontroler może dostarczyć następujące metody, które działają jak haki cyklu życia:
$doCheck()
- wywoływane na każdym zakręcie cyklu trawienia. Umożliwia wykrywanie zmian i reagowanie na nie. Wszelkie działania, które chcesz podjąć w odpowiedzi na wykryte zmiany, muszą być wywoływane z tego Hooka; implementacja tego nie ma wpływu na wywołanie$onChanges
. Przykładowo, to hook może być przydatny, jeśli chcesz przeprowadzić głębokie sprawdzenie równości lub sprawdzić obiekt daty, którego zmiany nie zostaną wykryte przez detektor zmiany kąta, a tym samym nie wywołają$onChanges
. Ten hook jest wywoływany bez argumentów; w przypadku wykrycia zmian, musisz zapisać poprzednie wartości w celu porównania z bieżącymi wartościami.- AngularJS Comprehensive Directive API Reference -- life-cycle hooks
Komunikacja Międzykomponentowa z require
Dyrektywy mogąwymagać kontrolerów innych dyrektyw, aby umożliwić komunikację między sobą. Można to osiągnąć w komponencie poprzez mapowanie obiektu dla właściwości require . Klucze obiektów określają nazwy właściwości, pod którymi wymagane Kontrolery (wartości obiektów) będą powiązane z kontrolerem wymagającego komponentu.
app.component('myPane', {
transclude: true,
require: {
tabsCtrl: '^myTabs'
},
bindings: {
title: '@'
},
controller: function() {
this.$onInit = function() {
this.tabsCtrl.addPane(this);
console.log(this);
};
},
templateUrl: 'my-pane.html'
});
Aby uzyskać więcej informacji, zobacz AngularJS Developer Guide-Intercomponent Komunikacja
Wypychanie wartości z usługi za pomocą RxJS
A co w sytuacji, gdy masz usługę, która trzyma stan na przykład. Jak mogę wypchnąć zmiany do tej usługi, a inne losowe komponenty na stronie są świadome takiej zmiany? Ostatnio zmagałem się z tym problemem
Zbuduj usługę z rozszerzeniami RxJS dla Angular .
<script src="//unpkg.com/angular/angular.js"></script>
<script src="//unpkg.com/rx/dist/rx.all.js"></script>
<script src="//unpkg.com/rx-angular/dist/rx.angular.js"></script>
var app = angular.module('myApp', ['rx']);
app.factory("DataService", function(rx) {
var subject = new rx.Subject();
var data = "Initial";
return {
set: function set(d){
data = d;
subject.onNext(d);
},
get: function get() {
return data;
},
subscribe: function (o) {
return subject.subscribe(o);
}
};
});
Następnie wystarczy zapisać się do zmiany.
app.controller('displayCtrl', function(DataService) {
var $ctrl = this;
$ctrl.data = DataService.get();
var subscription = DataService.subscribe(function onNext(d) {
$ctrl.data = d;
});
this.$onDestroy = function() {
subscription.dispose();
};
});
Klienci mogą subskrybować zmiany za pomocą DataService.subscribe
, a producenci za pomocą DataService.set
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-31 19:30:53
$watch
obiekt jest dostępny wewnątrz $scope
obiektu, więc musisz dodać $scope
wewnątrz funkcji fabrycznej kontrolera i umieścić watcher nad zmienną.
$scope.$watch(function(){
return myBox.game;
}, function(newVal){
alert('Value changed to '+ newVal)
});
Demo Tutaj
Uwaga: wiem, że przekonwertowałeś
directive
nacomponent
, aby usunąć zależność$scope
, aby zbliżyć się o krok do Angular2 Ale wygląda na to, że nie został usunięty w tej sprawie.
Update
Zasadniczo angular 1.5 nie dodaje metody .component
rozróżnia dwie różne funkcjonalności. Jak component
.oznacza wykonanie określonego behaviby adding selector
, gdzie as directive
oznacza dodanie określonego zachowania do DOM. Dyrektywa jest tylko metodą wrappera na .directive
DDO (Directive Definition Object). Jedyne, co możesz zobaczyć, to to, że mieli funkcję Usuń link/compile
podczas korzystania z metody .component
, w której miałeś możliwość uzyskania kątowego skompilowanego DOM.
Użyj $onChanges
/$doCheck
lifecycle hook of Angular component lifecycle hook, te będą dostępne po wersji Angular 1.5.3+.
$onChanges (changesObj) - wywoływane zawsze, gdy wiązania są aktualizowane. ChangesObj to hash, którego klucze są nazwami powiązanych właściwości.
$doCheck () - wywoływane na każdym kroku cyklu digest podczas zmiany wiązania. Umożliwia wykrywanie zmian i reagowanie na nie.
Używając tej samej funkcji wewnątrz komponentu zapewni zgodność kodu, aby przejść do kątowej 2.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-25 12:38:59
Dla wszystkich zainteresowanych moim rozwiązaniem, kończę uciekając się do Rxjs Observables, których będziesz musiał użyć, gdy dojdziesz do Angular 2. Oto działający skrzypek do komunikacji między komponentami, daje mi większą kontrolę nad tym, co oglądać.
class BoxCtrl {
constructor(msgService) {
this.msgService = msgService
this.msg = ''
this.subscription = msgService.subscribe((obj) => {
console.log('Subscribed')
this.msg = obj
})
}
unsubscribe() {
console.log('Unsubscribed')
msgService.usubscribe(this.subscription)
}
}
var app = angular
.module('app', ['ngMaterial'])
.controller('MainCtrl', ($scope, msgService) => {
$scope.name = "Observer App Example";
$scope.msg = 'Message';
$scope.broadcast = function() {
msgService.broadcast($scope.msg);
}
})
.component("box", {
bindings: {},
controller: 'BoxCtrl',
template: `Listener: </br>
<strong>{{$ctrl.msg}}</strong></br>
<md-button ng-click='$ctrl.unsubscribe()' class='md-warn'>Unsubscribe A</md-button>`
})
.factory('msgService', ['$http', function($http) {
var subject$ = new Rx.ReplaySubject();
return {
subscribe: function(subscription) {
return subject$.subscribe(subscription);
},
usubscribe: function(subscription) {
subscription.dispose();
},
broadcast: function(msg) {
console.log('success');
subject$.onNext(msg);
}
}
}])
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-19 07:21:24
Małe ostrzeżenie dotyczące stosowania ng-change
, zgodnie z zaleceniami z zaakceptowaną odpowiedzią, wraz ze składnikiem kątowym 1,5.
W przypadku, gdy musisz obserwować komponent, który nie działa ng-model
i ng-change
, możesz przekazać parametry jako:
Znaczniki, w których składnik jest używany:
<my-component on-change="$ctrl.doSth()"
field-value="$ctrl.valueToWatch">
</my-component>
Składnik js:
angular
.module('myComponent')
.component('myComponent', {
bindings: {
onChange: '&',
fieldValue: '='
}
});
Znaczniki komponentu:
<select ng-model="$ctrl.fieldValue"
ng-change="$ctrl.onChange()">
</select>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-03 15:23:57
Dostępny w IE11, MutationObserver https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver . musisz wprowadzić usługę $ element do kontrolera, która częściowo łamie separację DOM/kontroler, ale uważam, że jest to podstawowy wyjątek (np. wada) w angularjs. Ponieważ hide / show jest asynchroniczny, potrzebujemy on-show callback, że angularjs & angular-Bootstrap-tab nie zapewniają. Wymaga również, aby u wiedział, który konkretny element DOM chcesz obserwować. Użyłem poniższego kodu do angularjs kontroler do wyzwalania Highcharts chart reflow on-show.
const myObserver = new MutationObserver(function (mutations) {
const isVisible = $element.is(':visible') // Requires jquery
if (!_.isEqual(isVisible, $element._prevIsVisible)) { // Lodash
if (isVisible) {
$scope.$broadcast('onReflowChart')
}
$element._prevIsVisible = isVisible
}
})
myObserver.observe($element[0], {
attributes: true,
attributeFilter: ['class']
})
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-30 18:10:01
Naprawdę ładna, przyjęta odpowiedź, ale dodam, że można również użyć mocy zdarzeń(trochę jak w Qt signal / slots jeśli chcesz ).
Transmitowane jest Wydarzenie: $rootScope.$broadcast("clickRow", rowId)
przez każdego rodzica ( a nawet dzieci).
Następnie w kontrolerze możesz obsłużyć Zdarzenie w następujący sposób:
$scope.$on("clickRow", function(event, data){
// do a refresh of the view with data == rowId
});
Możesz też dodać trochę logowania w ten sposób ( wzięte stąd : https://stackoverflow.com/a/34903433/3147071 )
var withLogEvent = true; // set to false to avoid events logs
app.config(function($provide) {
if (withLogEvent)
{
$provide.decorator("$rootScope", function($delegate) {
var Scope = $delegate.constructor;
var origBroadcast = Scope.prototype.$broadcast;
var origEmit = Scope.prototype.$emit;
Scope.prototype.$broadcast = function() {
console.log("$broadcast was called on $scope " + this.$id + " with arguments:",
arguments);
return origBroadcast.apply(this, arguments);
};
Scope.prototype.$emit = function() {
console.log("$emit was called on $scope " + this.$id + " with arguments:",
arguments);
return origEmit.apply(this, arguments);
};
return $delegate;
});
}
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-03 08:58:26
Jestem spóźniona. Ale może pomóc innym ludziom.
app.component("headerComponent", {
templateUrl: "templates/header/view.html",
controller: ["$rootScope", function ($rootScope) {
let $ctrl = this;
$rootScope.$watch(() => {
return $ctrl.val;
}, function (newVal, oldVal) {
// do something
});
}]
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-05 16:50:50