Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS - ng:model - Field is readonly when bound to $q promise?

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)

like image 673
James Davies Avatar asked Jun 02 '13 13:06

James Davies


People also ask

What is nG model in AngularJS?

AngularJS ng-model Directive. The ng-model directive binds the value of HTML controls (input, select, textarea) to application data.

What is ng-readonly directive in AngularJS?

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.

What is the use of ng readonly in HTML?

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.

What if the property in the nG-model attribute does not exist?

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):


1 Answers

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/

like image 170
David Riccitelli Avatar answered Oct 18 '22 08:10

David Riccitelli