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') component 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)
AngularJS ng-model Directive. The ng-model directive binds the value of HTML controls (input, select, textarea) to application data.
The ng-readonly Directive in AngularJS is used to specify the readonly attribute of an HTML element. The HTML element will be readonly only if the expression inside ng-readonly directive returns true. Example 1: This example uses ng-readonly Directive to enable readonly property.
The ng-readonly directive sets the readonly attribute of a form field (input or textarea). The form field will be readonly if the expression inside the ng-readonly attribute returns true. The ng-readonly directive is necessary to be able to shift the value between true and false.
If the property in the ng-model attribute does not exist, AngularJS will create one for you. The ng-model directive can provide status for application data (valid, dirty, touched, error):
UPDATE
Seems that the issue has been introduced with AngularJS 1.0.3: http://jsfiddle.net/sonicsage/k8W4Y/6/
If you switch to AngularJS 1.0.2, it'll work.
There's an open issue on GitHub: https://github.com/angular/angular.js/issues/1827
The original thread on Google Groups.
There's also an interesting thread about automatic unwrapping here: https://github.com/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/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With