2

Angular seems to get tripped up by the 'open' state of Chrome's (possibly other browsers too) XMLHttpRequest during the $digest phase while copying it. When the XMLHttpRequest.status field is accessed it raises an exception. My actual use-case doesn't bind to the status field; the XMLHttpRequest object is just one member of another object in an array which I $watch.

So,

a) Should Angular be smarter about such cases and ignore the exception?
b) Is there a way to have Angular ignore fields or objects when it is digesting containing objects?
c) Other workaround suggestions?

Here's an example. This HTML+Javascript (JSFiddle):

<div ng-app ng-controller="TestCtrl">
  <h2>XMLHttpRequest.status: {{ xhr.status }}</h2>
</div>

function TestCtrl($scope) {
  $scope.xhr = new XMLHttpRequest();
  $scope.xhr.open('POST', 'http://localhost:8000/api/medias', true);
}

Results in this error:

Error: INVALID_STATE_ERR: DOM Exception 11
Error: An attempt was made to use an object that is not, or is no longer, usable.
at eval (eval at Jb (http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.3/angular.min.js:74:389), <anonymous>:16:4)
at d.fn.x.assign (http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.3/angular.min.js:63:120)
at Object.j (http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.3/angular.min.js:52:126)
at Object.e.$digest (http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.3/angular.min.js:84:286)
at Object.e.$apply (http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.3/angular.min.js:86:469)
at http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.3/angular.min.js:15:396
at Object.d [as invoke] (http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.3/angular.min.js:26:401)
at pb (http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.3/angular.min.js:15:317)
at jc (http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.3/angular.min.js:15:178)
at http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.3/angular.min.js:159:424 angular.min.js:60

2 Answers 2

1

You need to use the $http service. (Example 99% lifted from the angular documentation example)

Controller:

function TestCtrl($http, $scope) {
  $scope.xhr = {status : undefined};

  $http.post('http://localhost:8000/api/medias', {params:{'go':'here'}})
    .success(function(data, status) {
      $scope.xhr.status = status;
    })
    .error(function(data, status) {
      $scope.xhr.status = status;
    });
}

EDIT(not tested): Controller using XMLHttpRequest: using event listeners to call $apply

function TestCtrl($http, $scope) {
   $scope.xhr = {status : undefined};

   var xhr = new XMLHttpRequest();

   xhr.addEventListener('progress', function(event) {
     var progress = event['something']//get what you need from the event..
      $scope.$apply(function() {
        $scope.xhr.progress = progress;
      });
   }, false);

   xhr.open('POST', 'http://localhost:8000/api/medias', true);
}

Template:

<div ng-app ng-controller="TestCtrl">
  <h2>XMLHttpRequest.status: {{ xhr.status }}</h2>
  <h2>XMLHttpRequest.status: {{ xhr.progress}}</h2>
</div>

Example:

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks that's what I tried first but I need to get progress events from my request (it's an upload) which $http does not provide.
Thanks again. Unfortunately, as mentioned in my question, my actual case is more complex than the sample code here. I have an array of objects, each with several properties, one of which is the XMLHttpRequest object. I reference the XMLHttpRequest so I can not only get its status but call, e.g. abort() on it. The direction you're going adds increasing complexity as more state and functionality are bridged from the XMLHttpRequest to the watchable object. I would prefer a general solution to be able to safely include a reference to XMLHttpRequest in watched object(s).
Unfortunately it seems we're kind of stuck with angular needing to implement the desired properties in the $http service
0

To part b of my question:

b) Is there a way to have Angular ignore fields or objects when it is digesting containing objects?

The answer is yes, wrap the field/object with a function, e.g.:

$scope.getXhr = function () { return xhr; }

And Angular will ignore it when $digesting. Of course this presumes you want/need it to be ignored. Changes to that field/object will not trigger watchers.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.