gbc
gbc

Reputation: 8555

Two-way binding with range and number input in AngularJS

I'm just starting to play around with AngularJS and trying to understand the binding technique. For starters, I tried to make a simple conversion calculator (dozens to pieces, pieces to dozens). That worked well, but when I tried to bind both a range input and a number input to the same model property the number input does not update when the range value is adjusted. I have a jsfiddle showing the behavior:

common javascript for broken and working fiddles:

var myApp = angular.module('myApp', []);

myApp.controller('CalcCtrl', function ($scope) {
    var num = 0.0;
    $scope.qty = new Quantity(12);
    $scope.num = num;
});

function Quantity(numOfPcs) {
    var qty = numOfPcs;
    var dozens = numOfPcs / 12;

    this.__defineGetter__("qty", function () {
        return qty;
    });

    this.__defineSetter__("qty", function (val) {
        qty = val;
        dozens = val / 12;
    });

    this.__defineGetter__("dozens", function () {
        return dozens;
    });

    this.__defineSetter__("dozens", function (val) {
        dozens = val;
        qty = val * 12;
    });
}

BROKEN FIDDLE

html:

<div ng-controller="CalcCtrl">
    <form>
        <label for="pcs">Pieces:</label>
        <input type="number" min="0" ng-model="qty.qty" size="20" id="pcs"
        />
        <input type="range" min="0" max="100" ng-model="qty.qty" />
        <br/>
        <label for="numOfDozens">Dozens</label>
        <input type="number" min="0" ng-model="qty.dozens" size="20"
        id="numOfDozens" />
    </form>
</div>

However, binding two number inputs to the same model property seems to work fine as shown in this fiddle:

WORKING FIDDLE

html:

<div ng-controller="CalcCtrl">
    <form>
        <label for="pcs">Pieces:</label>
        <input type="number" min="0" ng-model="qty.qty" size="20" id="pcs"
        />
        <input type="number" min="0" max="100" ng-model="qty.qty" />
        <br/>
        <label for="numOfDozens">Dozens</label>
        <input type="number" min="0" ng-model="qty.dozens" size="20"
        id="numOfDozens" />
    </form>
</div>

Any ideas how to get a range and number input bound to a single model property in AngularJS? Thanks.

Upvotes: 17

Views: 22889

Answers (3)

Olga Akhmetova
Olga Akhmetova

Reputation: 490

You can solve it using ng-model-options. I changed jsfiddle and here is the code:

html:

<div ng-controller="CalcCtrl">
    <form>
        <label for="pcs">Pieces:</label>
        <input type="number" min="0" ng-model="qty.qty" size="20" id="pcs" ng-model-options="{ getterSetter: true }"/>
        <input type="range" min="0" max="100" ng-model="qty.qty" ng-model-options="{ getterSetter: true }"/>
        <br/>
        <label for="numOfDozens">Dozens</label>
        <input type="number" min="0" ng-model="qty.dozens" size="20"
        id="numOfDozens" ng-model-options="{ getterSetter: true }"/>
    </form>
</div>

js:

var myApp = angular.module('myApp', []);

myApp.controller('CalcCtrl', function ($scope) {
    var num = 1.0;
    $scope.qty = {
    qty:function (val) {
        return arguments.length ? (num = parseFloat(val)) : num;
     }, 
     dozens: function (val) {
            return arguments.length ? (num = val*12) : num/12;
        }
     };
});

Upvotes: 0

kamayd
kamayd

Reputation: 440

I think this solution is more generic.

myApp.directive('input', function() {
return {
    restrict: 'E',
    require: '?ngModel',
    link: function(scope, element, attrs, ngModel) {
        if ('type' in attrs && attrs.type.toLowerCase() === 'range') {
            ngModel.$parsers.push(parseFloat);
        }
    }
};

});

full explanation

Upvotes: 7

bmleite
bmleite

Reputation: 26880

The problem here is that the input type="range" works with Strings and not with Numbers (while input type="number" only works with Numbers).

http://www.w3.org/wiki/HTML/Elements/input/range

The range state represents a control for setting the element's value to a string representing a number.

If you add val = parseInt(val) as your first instruction on the qty setter it should work:

this.__defineSetter__("qty", function (val) {        
    val = parseInt(val);
    qty = val;
    dozens = val / 12;
});

jsfiddle: http://jsfiddle.net/bmleite/2Pk3M/2/

Upvotes: 17

Related Questions