2

AngularJS Verion: 1.3.8

JSFiddle: http://jsfiddle.net/uYFE9/4/

I've been working on a small AngularJS application, and ran into a bit of a problem. I have an ng-repeat on a page, which fills in the contents of a form. The amount of items in the form is defined by a dropdown bound to a model, and populated using ng-options. Something like:

<select id="testAmount" ng-model="selectedItem" ng-options="item.name for item in items"></select>

<form role="form" name="testForm" ng-if="!complete">
    <div ng-repeat="i in getNumber(selectedItem.number) track by $index">
        {{$index}}
    </div>
</form>

Complete is set to false in the beginning, and hitting a Next button will toggle complete and hide the form and dropdown. A Back button will then toggle complete back, and show the form again.

The problem I'm having is with the ng-if on the select (and previously, I had the form wrapped in a div with the same ng-if - same problem). The ng-repeat no longer updates when the select dropdown is changed. Removing the ng-if on the select restores the ng-repeat to working order.

I'm wondering if there's something strange I'm doing with the nesting here, or if it's actually a bug? You can test it out on the JSFiddle linked above. The $index should be printed the number of times on the dropdown, but isn't.

Interestingly enough - when debugging the problem on my local machine, having FireBug open fixed the issue.

Kenco
  • 683
  • 9
  • 21
  • 1
    Your issue has to do with the child scope created with the ng-if and lack of dot rule. You can fix it by setting `$scope.selected.item = $scope.items[0];` and using `ng-model="selected.item"` on select and on repeat use it accordingly. See http://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs – PSL Feb 02 '15 at 22:40
  • Yup, that was the issue. I don't know why I didn't see it earlier. Thanks. – Kenco Feb 03 '15 at 18:58

3 Answers3

3

This is because of ng-if creating a child scope and how prototypical inheritance works with primitives. In this case, the primitive is selectedItem that you are setting by the <select>, but is actually being set on the child scope and shadows/hides the parent scope property.

In general you should always use a dot (.) with ng-models:

$scope.selection = {selectedItem: undefined};

And in the View:

<div ng-if="!complete">
  <select ng-model="selection.selectedItem" 
          ng-options="item.name for item in items"></select>
</div>
New Dev
  • 48,427
  • 12
  • 87
  • 129
2

ng-if is causing you some scoping issues (which messes with the binding).

Here is an updated jsfiddle that you could use as a work around. Essentially, this example wraps another div around the items that you want to end up hiding. And then adds a next function so that the same scope is affected during the click that sets complete to true.

HTML:

<div ng-app="test">
    <div ng-controller="TestCtrl">
        <div ng-if="!complete">
            <div>
                <label for="testAmount">Amount:</label>
                <select id="testAmount" ng-model="selectedItem" ng-options="item.name for item in items"></select>
            </div>

            <form role="form" name="testForm">
                <div ng-repeat="i in getNumber(selectedItem.number) track by $index">
                    {{$index + 'hi'}}
                </div>
                <button class="btn btn-default" value="Next" title="Next" ng-click="next()">Next</button>
            </form>
        </div>

        <div ng-if="complete">

        </div>
    </div>
</div>

JS:

angular.module('test', [])
.controller('TestCtrl', function($scope) {
    $scope.complete = false;

    $scope.items = [
        { name: '2', number: 2 },
        { name: '3', number: 3 },
        { name: '4', number: 4 }
    ];

    $scope.selectedItem = $scope.items[0];

    $scope.getNumber = function (number) {
        return new Array(number);
    };

    $scope.next = function() {
      $scope.complete = true;  
    };
})
Davin Tryon
  • 66,517
  • 15
  • 143
  • 132
0

I believe the problem is with your select statement inside an ng-if the selectedItem is never getting set. If you just don't want to show that dropdown when !complete change it to an ng-show and it works fine.

    <div ng-show="!complete">

As to WHY the ng-model is not being bound inside the ng-if, I don't really know but it does make some sense in that you are trying to do a conditional bind which is a bit screwy

Scott
  • 1,690
  • 3
  • 13
  • 18
  • 1
    It's not being bound because `ng-if` (unlike `ng-show`) creates a child scope. And `selectedItem` *is* getting set - but on the child scope. – New Dev Feb 02 '15 at 23:10