Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular, two way binding with two variables

Tags:

angularjs

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>
like image 650
xcorat Avatar asked Feb 25 '14 12:02

xcorat


People also ask

Is two way binding possible in Angular?

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.

What is the correct way for performing two way binding in Angular?

[()] = [] + () 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.

Is angular 2 support two way data binding only?

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.

Is @input two way data binding?

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 .


1 Answers

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

Summary

This isn't exactly less code, but does have several advantages.

  • Your controller is decoupled from $scope and $watch making it more portable
  • The controller code is easier to read because all the functionality isn't nested inside an anonymous function
  • The code is a little more forward looking because future versions of Angular will probably eliminate $scope and the $digest loop altogether by using native observables.
like image 173
Josh Avatar answered Sep 29 '22 09:09

Josh