Scope issues with Angular UI modal

JavascriptAngularjsScopeModal DialogAngularjs Scope

Javascript Problem Overview


I'm having trouble understanding/using the scopes for an angular UI modal.

While not immediately apparent here, I have the modules and everything set up correctly (as far as I can tell), but these code samples in particular are where I'm finding the bug.

index.html (the important part of it)

<div class="btn-group">
    <button class="btn dropdown-toggle btn-mini" data-toggle="dropdown">
        Actions
        <span class="caret"></span>
    </button>
    <ul class="dropdown-menu pull-right text-left">
        <li><a ng-click="addSimpleGroup()">Add Simple</a></li>
        <li><a ng-click="open()">Add Custom</a></li>
        <li class="divider"></li>
        <li><a ng-click="doBulkDelete()">Remove Selected</a></li>
    </ul>
</div>

Controller.js (again, the important part)

MyApp.controller('AppListCtrl', function($scope, $modal){
    $scope.name = 'New Name';
    $scope.groupType = 'New Type';

    $scope.open = function(){
        var modalInstance = $modal.open({
            templateUrl: 'partials/create.html',
            controller: 'AppCreateCtrl'
        });
        modalInstance.result.then(function(response){

            // outputs an object {name: 'Custom Name', groupType: 'Custom Type'}
            // despite the user entering customized values
            console.log('response', response);

            // outputs "New Name", which is fine, makes sense to me.                
            console.log('name', $scope.name);

        });
    };
});

MyApp.controller('AppCreateCtrl', function($scope, $modalInstance){
    $scope.name = 'Custom Name';
    $scope.groupType = 'Custom Type';

    $scope.ok = function(){

        // outputs 'Custom Name' despite user entering "TEST 1"
        console.log('create name', $scope.name);

        // outputs 'Custom Type' despite user entering "TEST 2"
        console.log('create type', $scope.groupType);

        // outputs the $scope for AppCreateCtrl but name and groupType
        // still show as "Custom Name" and "Custom Type"
        // $scope.$id is "007"
        console.log('scope', $scope);

        // outputs what looks like the scope, but in this object the
        // values for name and groupType are "TEST 1" and "TEST 2" as expected.
        // this.$id is set to "009" so this != $scope
        console.log('this', this);
        
        // based on what modalInstance.result.then() is saying,
        // the values that are in this object are the original $scope ones
        // not the ones the user has just entered in the UI. no data binding?
        $modalInstance.close({
            name: $scope.name,
            groupType: $scope.groupType
        });
    };
});

create.html (in its entirety)

<div class="modal-header">
    <button type="button" class="close" ng-click="cancel()">x</button>
    <h3 id="myModalLabel">Add Template Group</h3>
</div>
<div class="modal-body">
    <form>
        <fieldset>
            <label for="name">Group Name:</label>
            <input type="text" name="name" ng-model="name" />			
            <label for="groupType">Group Type:</label>
            <input type="text" name="groupType" ng-model="groupType" />
        </fieldset>
    </form>
</div>
<div class="modal-footer">
    <button class="btn" ng-click="cancel()">Cancel</button>
    <button class="btn btn-primary" ng-click="ok()">Add</button>
</div>

So, my question stands: why is the scope not being double-bound to the UI? and why does this have the customized values, but $scope does not?

I have tried to add ng-controller="AppCreateCtrl" to the body div in create.html, but that threw an error: "Unknown provider: $modalInstanceProvider <- $modalInstance" so no luck there.

At this point, my only option is to pass back an object with this.name and this.groupType instead of using $scope, but that feels wrong.

Javascript Solutions


Solution 1 - Javascript

When nested scopes are involved, do not bind <input>s directly to members of the scope:

<input ng-model="name" /> <!-- NO -->

Bind them to at least a level deeper:

<input ng-model="form.name" /> <!-- YES -->

The reason is that scopes prototypically inherit their parent scope. So when setting 1st level members, these are set directly on the child scope, without affecting the parent. In contrast to that, when binding to nested fields (form.name) the member form is read from the parent scope, so accessing the name property accesses the correct target.

Read a more detailed description here.

Solution 2 - Javascript

I got mine to work like this:

var modalInstance = $modal.open({
  templateUrl: 'partials/create.html',
  controller: 'AppCreateCtrl',
  scope: $scope // <-- I added this
});

No form name, no $parent. I'm using AngularUI Bootstrap version 0.12.1.

I was tipped off to this solution by this.

Solution 3 - Javascript

Update Nov 2014:

Actually your code should work after upgrading to ui-bootstrap 0.12.0. Transcluded scope is merged with controller's scope so no more need for $parent or form. stuff.

Before 0.12.0:

The modal uses transclusion to insert its contents. Thanks to ngForm you can control the scope by name attribute. So to escape transcluded scope just modify the form this way:

<form name="$parent">

or

<form name="$parent.myFormData">

The model data will be available in controller scope.

Solution 4 - Javascript

$scope.open = function () {

		  var modalInstance = $uibModal.open({
		      animation: $scope.animationsEnabled,
		      templateUrl: 'myModalContent.html',
		      controller: 'salespersonReportController',
		      //size: size
		      scope: $scope
		    });

	  };

it works for me scope: $scope thank u Jason Swett

Solution 5 - Javascript

I add scope:$scope then it works .Cool

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioncoblrView Question on Stackoverflow
Solution 1 - JavascriptNikos ParaskevopoulosView Answer on Stackoverflow
Solution 2 - JavascriptJason SwettView Answer on Stackoverflow
Solution 3 - JavascriptgertasView Answer on Stackoverflow
Solution 4 - Javascriptali ShoaibView Answer on Stackoverflow
Solution 5 - JavascriptJBRandriView Answer on Stackoverflow