0

Check out this fiddle: http://jsfiddle.net/bwjbz/6/

I'm trying to strictly enforce positive whole numbers. I have tried several avenues, and I can't seem to get what I'm looking for. (If you try a couple of times you can end up getting a non-number or negative number to stick in the input).

In addition to the above fiddle, I have also tried using the number filter and also tried creating my own directive.

Number filter:

<input type="number" min="0" max="1000" value="{{itm.qty | number:0}}" required data-ng-model="itm.qty" data-ng-change="setQty()" data-whole-number>

You'll notice the data-whole-number directive. I'm not fully comfortable with directives yet, but this is it:

app.directive('wholeNumber', function() {
  return function(scope, elem, attrs) {
    elem.on("blur", function() {
      var num;
      num = parseInt(elem.val(), 10);
      num = Math.abs(num);
      scope.$apply(elem.val(num));
    });
  };
});

The directive itself does the correct DOM manipulation, but the model doesn't update with the new value.

So there are two aspects to this questions:

  1. The fiddle works to set the correct value on the model, but the model text doesn't update. However, in another method, I set the qty of the model itm to 1 and it changes both the model and the visible value. You'll notice it does in fact change the model correctly (notice the bindings, number of footballs = 1, when value is set to 1.56, for example).

  2. Why isn't the directive propagating the changes to the model?

Thanks so much in advance, -Brian

bfricka
  • 312
  • 5
  • 13

2 Answers2

3

Update: Working fiddle using a directive: http://jsfiddle.net/mrajcok/U7Je2/

Salient points:

<input type="number" min="0" max="{{maxValue}}" data-ng-model="itm.qty" whole-number>

link: function(scope, elem, attrs) {
        elem.on("blur", function() {
            var num = Math.abs(parseInt(elem.val(), 10));
            num = num > scope.maxValue ? 0 : num;
            scope.itm.qty = num
            scope.$apply();

            // or, the above two lines can be rewritten as
            scope.$apply(scope.itm.qty = num);

        // or, the blur function can be rewritten as
        elem.on("blur", function() {
            scope.$apply(function() {
                var num = Math.abs(parseInt(elem.val(), 10));
                num = num > scope.maxValue ? 0 : num;
                scope.itm.qty = num;
            });
        });

Original "answer":

Regarding your first fiddle, a similar issue came up on SO recently, see AngularJS - reset of $scope.value doesn't change value in template (random behavior)

If we apply the $timeout solution from that post to your issue, wrap the setQty() logic in a $timeout function, and it will work, sort of. See this fiddle. I say "sort of" because a "number" like "2-2-3-4" can be entered, as can "abc". Those bogus values do clear when you change focus. It seems like Angular is not calling setQty() for these bogus entries... I don't know why.

Community
  • 1
  • 1
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • I'm happy with that answer. It does what I need it to do. If you are feeling particularly generous perhaps you could shed some light on why my directive is failing to update the model? I don't have enough points to up vote, but I'll be thankful :) – bfricka Sep 08 '12 at 02:56
  • @bfricka, now that I've had some time to start learning more about directives, see my updated answer. The scope/model needs to be updated in the link function, and $apply doesn't require any arguments for this scenario. – Mark Rajcok Sep 19 '12 at 21:54
  • 1
    Thanks Mark, I too have been working more with directives. It'll be a great thing when the AngularJS documentation digs deeper into these powerful but complex areas. Directives are silly convoluted to learn due to the many ways they can be constructed, and the lack of clear explanation of what the DOM element/event life cycle looks like for each step (and therefore how to effectively use things like $apply, $defer, $q, etc. within them). Anyways, thanks for the update. – bfricka Sep 25 '12 at 15:46
  • Thank you Mark. I tried to sort a list using the `orderBy: sorting` filter and changing the variable from a directive. I found out that it was not possible to resort the list by updating the filter variable, `sorting` when each item in the list is produced from a template. I guess this is a scope thing.. – mlunoe Feb 27 '13 at 14:14
0

Mark,

The problem with your solution is that the directive needs to know the name of the bound variable. A more useful solution is to update it anonymously (and call any events associated) so that the directive can be used on any partial html bound to any controller:

// update the bound scope variable and if exists call the change function
var evalStr = attrs.ngModel + '=' + num.toString();
if (typeof attrs.ngChange != 'undefined')
{
  evalStr += ';'+attrs.ngChange;
}
scope.$apply(evalStr);

Note that the advantage of evaluating 'evalStr' as an argument of scope.$apply means that AngularJS reports any exceptions to the console and the application can continue.

Nick
  • 358
  • 2
  • 13