2

This question is an extension of Angular JS - Update input options with other input options

I have two ng-model inputs (input options), one is inside a ng-view and the other is inside a ng-include. I want to keep both updated when changing, so the value displayed by both is always the same.

The code is working when both inputs are in the same level. When one input is inside ng-include, I manage to get it working by adding $parent to the ng-model scope, but when placing the other input inside the ng-view I cannot manage to get it working. You can check my code in this plunkr

Community
  • 1
  • 1
Joe82
  • 1,357
  • 2
  • 24
  • 40

1 Answers1

1

EDIT: I felt mean with my previous response, since it was basically "You're wrong, do it my way", so here is an explanation of the problem.

You have a div with ng-ctrl set on it. This div now has a scope with an instance of myCtrl.

ng-include creates a child scope, which inherits from its parent scope (the previously created myCtrl scope). This scope will not have direct references to selectedCountry or countries, but angular will automatically search parent scopes for values when it doesn't find them. This is important. (This is how JavaScript prototypical inheritance works. More info here.)

You also have an ng-view, which in your route provider you've told has its controller set to myCtrl. This means it is going to create a seperate myCtrl scope, which is also a child of the outer myCtrl scope. Because it is a child of the outer myCtrl scope, like your ng-include scope, angular would usually search its parent for properties it can't find on the immediate scope.

However this is where it gets complicated. In this case, because it is also another instance of myCtrl, it created new properties for selectedCountry and countries on itself and thus does not need to search its parent for the properties when you reference them. Angular matches objects by reference, and not by a deep object comparison, therefore these objects are considered to be different and selecting something from list A changes selectedCountry to a reference of an object which doesn't exist on list B.

This is why you were forced to use $parent, to try and point to the same references as the scope outside it, which your ng-include is automatically referencing through its parent.

The second problem with your code, is whilst you used $parent for the selectedCountry value, you did not use $parent for the countries array, and so it is using the array of objects that were re-created on the child myCtrl to fill your select box. Again, even though the objects have the same values, angular compares by reference for ng-options. So the Modor on select A is not the same as Modor on select B.

As an additional note, there is a third problem you could have created had you not set $parent on selectedCountry for menu.html and checkout.html. Whilst initially it would search the parent for selectedCountry, if you changed the select, it would create a property on the child scope instead of bubbling up to the parent, and thus from then on it would no longer match the parent's version of selectedCountry.

So, to summise, the way you've coded it you have two choices.

The crap choice:

  • $parent.everything
  • Here is a Plunkr of $parent fixing everything (please don't do this)

The responsible choice:

  • Use controllerAs to prevent scoping problems forever
  • Here is a Plunkr of how I would do it with controllerAs

A short guide for controllerAs is here

TL;DR; if you don't want to read guide

  • In your html -> ng-controller="myCtrl as myCtrl"
  • Also in your html -> myCtrl.countries
  • If it was a route/directive, set this property -> controllerAs: 'myCtrl'
  • In your Controller, instead of $scope.stuff -> this.stuff
    • Note: this is for property/function assignment. If you're using a $scope function like $watch, that is still done on $scope.

Just as an extra tip, this is my bible for coding in angular. Follow these guidelines and your code will be immaculate and prevent you from these kind of pitfalls.

ed'
  • 1,815
  • 16
  • 30
  • Thank you very much for your answer and the take you took to extend it. I am just confused in one step. It seems that the last bit of code is not passing the right info to the directive I use. The information should be passed as something like `ngCart.setshipping(30);`, but with `this.changeShipping = function() { ngCart.setShipping (this.selectedCountry.shipping); };` it doesn't seem to work. I made another plunkr so you can see the directive not updating (http://plnkr.co/edit/iY4Hssm0PTfjGyGRWAwP?p=preview) Thanks! – Joe82 May 31 '16 at 15:10
  • 1
    The only problem I saw was that you didnt add `myCtrl` to the shipping function, like so: `ng-change="myCtrl.changeShipping()"` and then it seemed to work for me, sends 30, 70, 100, etc... – ed' May 31 '16 at 15:21
  • That was it! I mark your answer as correct (and will start now to read carefully all the info you provided). Thanks for all the explanation! – Joe82 May 31 '16 at 16:54