Opisywałem już na blogu czym jest dwustronne wiązanie (ang. two way binding). Dzięki niemu, dane w widoku są wyświetlane z modelu oraz na bieżąco aktualizują się w modelu na podstawie widoku.
Jest ono realizowane w AngularJS za pomocą dyrektywy ng-model
:
1 |
<input type="text" ng-model="myText"> |
Co jeśli chcemy jakoś przetworzyć dane z modelu zanim trafią do widoku? Oraz chcemy mieć wpływ na dane z widoku, które trafiają do modelu? W innych językach programowania (np. w Javie) używa się getterów i setterów, czyli metod, które odpowiadają za pobieranie i ustawianie danych w obiekcie. Dzięki nim mamy zapewnioną kontrolę nad danymi, które trafiają z zewnątrz do obiektu i są z niego pobierane (hermetyzacja).
Żeby móc w AngularJS napisać gettery i settery, musimy skorzystać z dyrektywy ng-model-options
i ustawić opcję getterSetter
na true
:
1 |
<input type="text" ng-model="myText" ng-model-options="{getterSetter: true}"> |
Teraz w kontrolerze możemy do zmiennej myText
przypisać funkcję, która będzie odpowiadała za getter i setter.
Funkcja taka powinna mieć parametr (który będzie przyjmował wartość dla settera). Jeśli nowa wartość będzie zdefiniowana:
1 |
if(angular.isDefined(newValue)) |
lub jeśli liczba argumentów będzie większa niż zero:
1 |
if(arguments.length > 0) |
co można też zapisać tak:
1 |
if(arguments.length) |
to ma wykonać się część odpowiedzialna za settter. Bo znaczy to tyle, że jest dostępny parametr z nową wartością.
Jeśli parametr nie jest dostępny (czyli nie spełnia wybranego warunku z powyższych – są one wymienne), to ma wykonać się część odpowiedzialna za getter. Bo oznacza to tyle, że funkcja została wywołana bez parametru (czyli w celu pobrania wartości).
1 2 3 4 5 6 7 8 9 |
$scope.myValue = function(newValue) { if(arguments.length) { // lub if(angular.isDefined(newValue)) // operacje settera return text; } else { // operacje gettera return text; } }; |
Przykład użycia
Na potrzeby przykładu chcemy, aby podczas wpisywania liter do pola tekstowego były automatycznie zamieniane na duże litery (nawet jeśli wpisywane są małe). Użyjemy do tego gotowej funkcji z JavaScript .toupperCase
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
app.controller('myCtrl', function($scope) { var myText = "HELLO"; $scope.myText = function(newText) { if(angular.isDefined(newText)) { myText = newText.toUpperCase(); return myText; } else { return myText; } }; }); |
Litery są pobierane, przetwarzane przez setter (a właściwie pierwszą część instrukcji warunkowej), zamieniane na duże litery i przypisywane do zmiennej w kontrolerze. Podczas pobierania tekstu wykonywana jest jedynie druga część warunku (getter) zwracająca zamienione litery.
Oczywiście $scope.myText
(zmienna z funkcją gettera i settera) i myText
(zmienna z tekstem), to co innego i dlatego możemy użyć takich samych nazw (to pierwsze jest w $scope
, a drugie jedynie w kontrolerze).
Podgląd działającego przykładu:
Pingback: Prosty import i eksport danych w AgendaEditor | Marcin Kowalczyk – Blog IT