I have two variables that are related by a function, and the user should be able to change one or the other in an input field which should automatically change the other.
How can I do that, right now I'm just using $watch
for both.
Here's some sample code and a fiddle.
JS,
angular.module("test", [])
.controller("MyController", function ($scope) {
$scope.letter = 'A';
$scope.number = 1;
$scope.map = { 'A': 1, 'B': 2, 'C': 3, 'D': 4 };
$scope.$watch('letter', function(new_val, old_val){
if(new_val != old_val){
$scope.number = $scope.map[new_val];
}
});
$scope.$watch('number', function(new_val, old_val){
...
});
});
HTML,
<div ng-app="test">
<div ng-controller="MyController">
<input ng-model="letter" />
<input type="number" ng-model="number" />
</div>
</div>
Angular's two-way binding syntax is a combination of square brackets and parentheses, [()] . The [()] syntax combines the brackets of property binding, [] , with the parentheses of event binding, () , as follows.
[()] = [] + () where [] binds attribute, and () binds an event. The [(ngModel)] syntax is the recommended way of two-way data binding. The ngModel directive with [] syntax is used for one-way data binding. [ngModel] binds a value to a property to UI control.
Two-way data binding in Angular will help users to exchange data from the component to view and from view to the component. It will help users to establish communication bi-directionally. Two-way data binding can be achieved using a ngModel directive in Angular.
Two-way data binding combines the input and output binding into a single notation using the ngModel directive. To create your own component that supports two-way binding, you must define an @Output property to match an @Input , but suffix it with the Change .
There are a number of ways you can do this, and using $watch
is certainly one of them. As mentioned by Matt, you could also use the ng-change
directive to fire a method on your controller.
The third way that I would like to offer up, is to make use of ES5 properties, and the Controller 'as' syntax that Angular introduced in 1.2+
If you define your controller as a JS object instead of using an anonymous function, you can add properties and methods to the prototype:
myController = function () {
this.map = {'A': 1,'B': 2,'C': 3,'D': 4};
this._letter = 'A';
this._number = 1;
};
Now we can extract the work you have already done for getting your letter
and number
values into functions:
myController.prototype.getLetterValue = function (num) {
for (var key in this.map) {
if (this.map.hasOwnProperty(key)) {
if (this.map[key] === num) {
return key;
}
}
}
};
myController.prototype.getNumberValue = function (letter) {
return this.map[letter];
};
Lastly, we are going to declare a couple of properties on your controller that encapsulate the desired functionality using Object.defineProperty
.
Object.defineProperty(
myController.prototype,
"letter", {
get: function () {
return this._letter;
},
set: function (newValue) {
this._letter = newValue;
this._number = this.getNumberValue(this._letter);
},
enumerable: true,
configurable: true
});
Object.defineProperty(
myController.prototype,
"number", {
get: function () {
return this._number;
},
set: function (newValue) {
this._number = newValue;
this._letter = this.getLetterValue(this._number);
},
enumerable: true,
configurable: true
});
Add this controller to your module:
angular.module("test", [])
.controller("MyController", myController);
And lastly, you just need to modify your binding syntax slightly in order to use the new Controller 'as' syntax. This will allow you to bind directly to properties and methods on your controller instead of having to use $scope
<div ng-app="test">
<div ng-controller="MyController as ctrl">
<input ng-model="ctrl.letter" />
<input type="number" ng-model="ctrl.number" />
</div>
</div>
Live Demo
This isn't exactly less code, but does have several advantages.
$scope
and $watch
making it more portable$scope
and the $digest
loop altogether by using native observables.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With