I'm attempting to return a single record from a promise in AngularJs (1.0.7) and bind the result to a form. The form binds correctly, however the input fields are read-only - I cannot edit the values.
If instead I wrap the record in an Array and iterate using ng:repeat, the form binds correctly and I can edit values.
I've created a plnkr that demonstrates the issue clearly:
You can edit the directly bound and list bound input fields, however the field bound to the single promise cannot be edited.
Is it possible to bind ng:model directly to an object returned from a promise, or do I need to use an array to get this to work?
app.controller('MainCtrl', function($scope, $timeout, $q) {
var person = {"name": "Bill Gates"}
var deferList = $q.defer();
var deferSingle = $q.defer();
// Bind the person object directly to the scope. This is editable.
$scope.direct = person;
// Bind a promise to the scope that will return a list of people. This is editable.
$scope.list = deferList.promise;
// Bind ap romise to the scope that will return a single person record. This is *not* editable.
$scope.single = deferSingle.promise;
// Resolve the promises
$timeout( function(){
deferList.resolve( [person] ); // Array
deferSingle.resolve( person ); // Just the record itself
}, 100);
});
<body ng-controller="MainCtrl">
Directly Bound - This field is editable
<input ng:model="direct.name"/>
<hr/>
Singleton Promise - This field is *not* editable.
<input ng:model="single.name"/>
<hr/>
List Promise: - This field is editable
<div ng:repeat="person in list">
<input ng:model="person.name"/>
</div>
</body>
Edit: After some debugging, I've found that the ng:model directive is reading from the value ('$$v') ponent of the promise, but writing directly to the promise object itself.
When attempting to edit the promise, the ViewModel keeps being reverted back to the original value, while storing characters on the promise itself. Thus if the user types 'asdf' into the input field, the result would be the following.
{Name: "Asdf", $$v: {Name: "Bill Gates"}}
Whereas we should instead be expecting
{$$v: {Name: "asdf"}}
Am I doing something wrong, or is this potentially a bug in AngularJS?
(To further clarify, the issue is the difference in behaviour between an Array and an Object returned by a promise. The direct binding is simply there as a control)
I'm attempting to return a single record from a promise in AngularJs (1.0.7) and bind the result to a form. The form binds correctly, however the input fields are read-only - I cannot edit the values.
If instead I wrap the record in an Array and iterate using ng:repeat, the form binds correctly and I can edit values.
I've created a plnkr that demonstrates the issue clearly:
http://embed.plnkr.co/fOWyhVUfekRbKUSRf7ut/preview
You can edit the directly bound and list bound input fields, however the field bound to the single promise cannot be edited.
Is it possible to bind ng:model directly to an object returned from a promise, or do I need to use an array to get this to work?
app.controller('MainCtrl', function($scope, $timeout, $q) {
var person = {"name": "Bill Gates"}
var deferList = $q.defer();
var deferSingle = $q.defer();
// Bind the person object directly to the scope. This is editable.
$scope.direct = person;
// Bind a promise to the scope that will return a list of people. This is editable.
$scope.list = deferList.promise;
// Bind ap romise to the scope that will return a single person record. This is *not* editable.
$scope.single = deferSingle.promise;
// Resolve the promises
$timeout( function(){
deferList.resolve( [person] ); // Array
deferSingle.resolve( person ); // Just the record itself
}, 100);
});
<body ng-controller="MainCtrl">
Directly Bound - This field is editable
<input ng:model="direct.name"/>
<hr/>
Singleton Promise - This field is *not* editable.
<input ng:model="single.name"/>
<hr/>
List Promise: - This field is editable
<div ng:repeat="person in list">
<input ng:model="person.name"/>
</div>
</body>
Edit: After some debugging, I've found that the ng:model directive is reading from the value ('$$v') ponent of the promise, but writing directly to the promise object itself.
When attempting to edit the promise, the ViewModel keeps being reverted back to the original value, while storing characters on the promise itself. Thus if the user types 'asdf' into the input field, the result would be the following.
{Name: "Asdf", $$v: {Name: "Bill Gates"}}
Whereas we should instead be expecting
{$$v: {Name: "asdf"}}
Am I doing something wrong, or is this potentially a bug in AngularJS?
(To further clarify, the issue is the difference in behaviour between an Array and an Object returned by a promise. The direct binding is simply there as a control)
Share Improve this question edited Jun 2, 2013 at 14:03 James Davies asked Jun 2, 2013 at 13:32 James DaviesJames Davies 9,8696 gold badges40 silver badges44 bronze badges 1- I found the issue and have created a pull request. github./angular/angular.js/pull/2854 – James Davies Commented Jun 3, 2013 at 4:52
1 Answer
Reset to default 8UPDATE
Seems that the issue has been introduced with AngularJS 1.0.3: http://jsfiddle/sonicsage/k8W4Y/6/
If you switch to AngularJS 1.0.2, it'll work.
There's an open issue on GitHub: https://github./angular/angular.js/issues/1827
The original thread on Google Groups.
There's also an interesting thread about automatic unwrapping here: https://github./angular/angular.js/pull/1676
By debugging the application in the Chrome console, you can see that single
is a function (the promise):
> $('body.ng-scope').data('$scope').single
Object {then: function, $$v: Object}
$$v: Object
then: function (b,g){var j=e(),h=
__proto__: Object
While direct
is an object:
> $('body.ng-scope').data('$scope').direct
Object {name: "Bill Gates", $$hashKey: "004"}
However, pressing the keys on the read-only input has an effect on the promise
, for example, selecting all the text and erasing it, although has no effect on the UI, has an effect on the property:
> $('body.ng-scope').data('$scope').single.name
""
You can further debug the app here: http://run.plnkr.co/plunks/rDo7bFZlBq4rRH2ZNJn1/
EDIT
IMO directly binding a promise to a field is not supported (is this officially documented?), changing the code as follows will work:
// Bind ap romise to the scope that will return a single person record. This is *not* editable.
deferSingle.promise.then(function(data) {
$scope.single = data;
}, function(data) {
// error
});
Here's the plunker: http://run.plnkr.co/plunks/rDo7bFZlBq4rRH2ZNJn1/