5

I'm checking the Angular Bootstrap UI, especially the service $modal and noticed an interesting thing.

In their sample here 'http://plnkr.co/edit/E5xYKPQwYtsLJUa6FxWt?p=preview' in the controller that gets attached to the popup window they have enclosed the selected item into another inner property

$scope.selected = {
   item: $scope.items[0]
};

instead of having just

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

and indeed their code works as expected while my version does not.

Why is this needed? What's the JavaScript gotcha here?

Thx

Sully
  • 14,672
  • 5
  • 54
  • 79
Eugen
  • 2,934
  • 2
  • 26
  • 47
  • 1
    I'm going to take a stab and guess that it has something to do with `$scope.selected` referencing `$scope.items[0]` in your example and becoming it's own object with a value inside it in their example. Though I'm not 100% so I hope you get a better answer than this. – Shaded Dec 19 '13 at 17:02
  • Because of the prototype inheritance. See [What are the nuances of scope prototypal / prototypical inheritance in AngularJS?](http://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs). – Stewie Dec 19 '13 at 17:06
  • Check [this article](https://github.com/angular/angular.js/wiki/Understanding-Scopes). – raina77ow Dec 19 '13 at 17:06
  • well I can understand this, what I don't get, is why when having a regular controller not attached to a modal window, my version works as expected as well. – Eugen Dec 19 '13 at 17:06
  • I answered a similar question a couple months ago here: http://stackoverflow.com/questions/18991998/ui-bootstrap-modal-scope-bug/18992371#18992371 – Craig Squire Dec 19 '13 at 19:58
  • It is due to inner-workings of `scope` inheritance - it basically works *as expected* with `reference types` but can be *sort of problematic* in case of `primitive types` (number, string, boolean). Look here: [http://plnkr.co/edit/7k7rnCRLHfgjngViQ1bT?p=preview](http://plnkr.co/edit/7k7rnCRLHfgjngViQ1bT?p=preview) - I changed strings (primitive) to Arrays containing strings (Array is reference type / complex object) and it works as: `$scope.selected = $scope.items[0]`. – artur grzesiak Dec 19 '13 at 20:33

2 Answers2

1

They're nesting the property because they want to do this in the modal:

<li ng-repeat="item in items">
    <a ng-click="selected.item = item">{{ item }}</a>
</li>

ng-repeat creates a child scope for each <li> (the modal is creating a child scope as well); if you'd have $scope.selected = $scope.items[0];, setting selected from the ng-click would set the property in the child scope, but not the parent scope (which is what you want in that example). Also see my answer here. In the case of

$scope.selected = {
    item: $scope.items[0]
};

the change will affect the parent scope's selected object.

Community
  • 1
  • 1
Lars Höppner
  • 18,252
  • 2
  • 45
  • 73
  • Well basically if I got it right, anytime you create a controller by code or you have a modal dialog is better to enclose all of the properties of that controller into a new object, something like this $scope.scope = { here we put all new properties } this way we avoid any collisions and prototype inheriting. – Eugen Dec 19 '13 at 19:48
  • it depends - if you want to share an object in your scope with child scopes, then you need to nest it in something; then you can set properties on the inherited object from the parent scope; if you don't nest it and assign a new value, it will simply overwrite the existing object in your child scope instead of modifying the parent's object. If you don't need to share the data with child scopes through inheritance, you don't need to artificially nest it – Lars Höppner Dec 19 '13 at 19:55
0

This has bugged me before so I decided to dig around :)

It seems the issue is with primitives and scope inheritance. Storing the properties in Objects ensures they aren't overwritten within directives like ngRepeat.

More info here: https://github.com/angular/angular.js/wiki/Understanding-Scopes

Also worth noting that while doing ng-click="selectedItem = item" won't work, ng-click="selectItem(item)" will, without having the container object.

imcg
  • 2,601
  • 1
  • 18
  • 13