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