Search code examples
javascriptangularjsdata-bindingangular-ngmodel

AngularJs Avoiding Scoping Issues


I recently spent over 4 hours before figuring out why my ng-model directive used in combination with ng-options was not correctly binding to the property within my controller. The <select> element was being properly initialized - receiving a value from the controller (parent) scope. But the child scope was not correctly updating the parent scope. After checking out the following questions and plunkers, I was able to develop a "work around" for this issue:

Helpful stackoverflow question 1

Helpful stackoverflow question 2

Basic Plunker

I found that the property I was binding to in my <select> element was binding to a property of the same name within a child scope of the controller - therefore not the value was not reflected as expected in the controller's scope. After changing

<select ng-options="asset as asset.Name for asset in allAssets" ng-model="selectedAsset" ng-change="lookupAssetPermissions()"></select>

to

    <select ng-options="asset as asset.Name for asset in allAssets" ng-model="$parent.selectedAsset" ng-change="lookupAssetPermissions()"></select>

The value in selectedAsset was correctly binding to the property in the controller's scope (as seen in the ng-change event handler). The entire context of my element is the following:

<!---outer div has controller level scope----->
<div>
    <!---inner div creates child scope with ng-if----->
    <div ng-if="true condition here">
        <!---select statement from above----->
        <select ng-model="$parent.selectedAsset">...</select>
    </div>
</div>

Do I have any other options in this scenario other than purposefully binding to the parent scope? If I had multiple child scopes (nested ng-if statements), would I need to alter the ng-model to bind to $parent.$parent.$parent....selectedAsset in order to update the value in my controllers scope? Are there any "best practices" on this topic?


Solution

  • Put all variables inside some object i.e.:

    $scope.Model = {
      selectedAsset : 'mySelectedAsset1',
      selectedAsset2 : 'mySelectedAsset2',
      selectedAsset3 : 'mySelectedAsset3'
    }
    

    Then you can:

    <div ng-repeat> //new scope
    <div ng-repeat> // new scope
    <input ng-model="Model.selectedAsset">
    

    This also lows your 'dependency' on $scope, defining such Model object will show everyone who is reading your code what model u have.