Reputation: 51
I've already found a "solution" to this problem; I was just hoping someone might be able to provide a reason why it works.
This jsFiddle demonstrates the problem: http://jsfiddle.net/s1ca0h9x/137/
HTML
<div data-ng-app="myApplication">
<div data-ng-controller="MainController">
<a href="" ng-click="ShowNgDialog()">Click Here</a>
<input type="text" ng-model="accountNum" />
<span>{{accountNum}}</span>
</div>
</div>
ANGULARJS
var myApplication = angular.module('myApplication', ['ngDialog']);
myApplication.controller('MainController', function ($scope, ngDialog) {
$scope.accountNum = 'test';
$scope.ShowNgDialog = function () {
ngDialog.open({
template: '<div><input type="text" ng-model="accountNum"/></div>',
plain: true,
scope:$scope
});
}
});
When I try and manipulate a scope variable (in this case: $scope.accountNum = 'test') from the dialog, it doesn't bind/save it back to the model.
...However, when I change that variable into an object, things just magically work, as shown in this demo: http://jsfiddle.net/s1ca0h9x/138/
HTML
<div data-ng-app="myApplication">
<div data-ng-controller="MainController">
<a href="" ng-click="ShowNgDialog()">Click Here</a>
<input type="text" ng-model="FormData.accountNum" />
<span>{{FormData.accountNum}}</span>
</div>
</div>
ANGULARJS
var myApplication = angular.module('myApplication', ['ngDialog']);
myApplication.controller('MainController', function ($scope, ngDialog) {
$scope.FormData={accountNum: ''};
$scope.ShowNgDialog = function () {
ngDialog.open({
template: '<div><input type="text" ng-model="FormData.accountNum"/></div>',
plain: true,
scope:$scope
});
}
});
I also tested both options using a template linking to a file, and not using plain:true, in addition to trying ngDialog.openConfirm, etc. I essentially rebuilt the solution found here ngDialog $scope variables not being updated by ngModel fields in $dialog when using scope: $scope piece by piece, and finally the only change that seemed to work was using an object instead of a basic scope variable. Am I approaching this wrong, or missing some fundamental aspects of data binding?
Upvotes: 3
Views: 443
Reputation: 715
For me what worked, is to create a function in the base controller, and call the function from ngDialog controller.
Ex:
myApplication.controller('MainController', function ($scope, ngDialog) {
$scope.accountNum = 'test';
$scope.ShowNgDialog = function () {
ngDialog.open({
template: '<div><input type="text" ng-model="accountNum"/></div>',
plain: true,
scope:$scope,
controller: ['$scope',
function ($scope) {
$scope.updateVar();
}]
});
};
$scope.updateVar = function(){
$scope.accountNum = "changed";
}
});
Upvotes: 0
Reputation: 6558
I think this has nothing to do with the binding. I will explain what I did understood when I dig into the code of ngDialog and AngularJS.
I think the first case is not working as you expect, because $scope.accountNum = 'test';
is a simple string which is a primitive type and is not mutable(ref) or in other words is immutable:
Mutable is a type of variable that can be changed. In JavaScript, only objects and arrays are mutable, not primitive values. (You can make a variable name point to a new value, but the previous value is still held in memory. Hence the need for garbage collection.)
A mutable object is an object whose state can be modified after it is created.
Immutables are the objects whose state cannot be changed once the object is created.
String and Numbers are Immutable.
So, in short words, this was the reason why the first variant is not working as you want :)
Now let's have a look on this code of ngDialog, which is a part of open() method:
var scope;
scopes[dialogID] = scope = angular.isObject(options.scope) ? options.scope.$new() : $rootScope.$new();
in your case we are calling options.scope.$new()
, because you specified scope
in options when opening the dialog.
Now let's go and check this angular code:
$new: function (isolate, parent) {
var child;
parent = parent || this;
if (isolate) {
child = new Scope();
child.$root = this.$root;
} else {
if (!this.$$ChildScope) {
this.$$ChildScope = createChildScopeClass(this); // <---- WE ARE COMING HERE NOW
}
child = new this.$$ChildScope();
}
...
function createChildScopeClass looks like:
function createChildScopeClass(parent) {
function ChildScope() {
this.$$watchers = this.$$nextSibling =
this.$$childHead = this.$$childTail = null;
this.$$listeners = {};
this.$$listenerCount = {};
this.$$watchersCount = 0;
this.$id = nextUid();
this.$$ChildScope = null;
}
ChildScope.prototype = parent; /* <--- They simply assign the derived scope
as prototype of the new one (which is going to be the scope of the ngDialog) */
return ChildScope;
}
We can see that function createChildScopeClass()
simply assigns the parent scope's prototype to the new one (which is going to be the scope of the opened ngDialog)
And a sample that is demonstrating mutability and immutability:
var test = 'test';
var test2 = test;
test2 = 'new value';
console.log('test = ' + test + ' // test2 = ' + test2);
var testObj = {test: 'test'};
var test2Obj = testObj;
test2Obj.test = 'new value';
console.log('testObj.test = ' + testObj.test + ' // test2Obj.test = ' + test2Obj.test);
Use objects or arrays in your parent scope if you want binding to work in the derived scope. Sample using AngularJS:
var app = angular.module('sample', []);
app.controller('AppController', ['$scope', function($scope) {
$scope.primitive = 'test';
$scope.obj = {
test: 'test initial'
};
$scope.newScope = $scope.$new();
$scope.newScope.primitive = 'test 2';
$scope.newScope.obj.test = 'updated value';
}]);
app.run();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="sample">
<div ng-controller="AppController">
<table>
<thead><tr><th>Property</th><th>Value</th><th></th></tr></thead>
<tbody>
<tr>
<td>primitive</td>
<td>{{ primitive }}</td>
<td><input type="text" ng-model="primitive"></td>
</tr>
<tr>
<td>obj.test</td>
<td>{{ obj.test }}</td>
<td><input type="text" ng-model="obj.test"></td>
</tr>
<tr>
<td>newScope.primitive</td>
<td>{{ newScope.primitive }}</td>
<td><input type="text" ng-model="newScope.primitive"></td>
</tr>
<tr>
<td>newScope.obj.test</td>
<td>{{ newScope.obj.test }}</td>
<td><input type="text" ng-model="newScope.obj.test"></td>
</tr>
</tbody>
</table>
</div>
</div>
Upvotes: 2