Using one iron-ajax element for multiple requests

别等时光非礼了梦想. 提交于 2019-12-06 09:33:50

问题


Theoretically, it should be possible to use one iron-ajax element for multiple requests by setting the auto attribute and then repeatedly setting the url property on the element. iron-ajax has a property called activeRequests, which is a read-only array, so it seems like it has supports for queueing up multiple requests simultaneously. However in practice it does not appear to work.

For example, in the JS Bin below, I retrieve a list of book IDs for books that contain the word polymer, and then use a for loop to repeatedly set the value of url.

<!doctype html>
<html>
<head>
  <meta name="description" content="single iron-ajax for multiple requests">
  <meta charset="utf-8">
  <base href="https://polygit.org/components/">
  <script href="webcomponentsjs/webcomponents-lite.min.js"></script>
  <link href="polymer/polymer.html" rel="import">
  <link href="iron-ajax/iron-ajax.html" rel="import">
</head>
<body>
  <dom-module id="my-el">
    <template>
      <iron-ajax id="ajax"
                 url="https://www.googleapis.com/books/v1/volumes?q=polymer"
                 handle-as="json"
                 on-response="onResponse"
                 last-response="{{response}}" auto></iron-ajax>
    </template>
    <script>
      Polymer({
        is: 'my-el',
        properties: {
          response: {
            type: Object,
            notify: true
          }
        },
        onResponse: function(e) {
          var ajax = this.$.ajax;
          var originalUrl = 'https://www.googleapis.com/books/v1/volumes?q=polymer';
          var url = ajax.lastRequest.xhr.responseURL;
          if (url.includes(originalUrl)) {
            console.log('this is the first request');
            for (var i = 0; i < ajax.lastResponse.items.length; i++) {
              ajax.url = this.url(ajax.lastResponse.items[i].id);
            }            
          } else {
            console.log(ajax.lastResponse.selfLink);
          }
        },
        url: function(id) {
          return "https://www.googleapis.com/books/v1/volumes/" + id;
        }
      });
    </script>
  </dom-module>
  <my-el></my-el>
</body>
</html>

回答1:


It's indeed possible to use iron-ajax for multiple requests but not with auto enabled, or else you'll hit iron-ajax's debouncer. From the Polymer docs for iron-ajax:

With auto set to true, the element performs a request whenever its url, params or body properties are changed. Automatically generated requests will be debounced in the case that multiple attributes are changed sequentially.

In your question's code:

// template
<iron-ajax auto ...>

// script
onResponse: function(e) {
  ...
  for (var i = 0; i < ajax.lastResponse.items.length; i++) {
    ajax.url = this.url(ajax.lastResponse.items[i].id);
  }
}

...you're presumably expecting iron-ajax to generate a new request with each URL, but the debouncer collapses the requests into one (taking only the last invocation).

Also worth noting: The response handler's event detail (i.e., e.detail) is the corresponding iron-request, which contains the AJAX response (i.e., e.detail.response). Using the event detail is preferrable because it avoids a race condition in simultaneous requests from iron-ajax, where this.$.ajax.lastResponse or this.$.ajax.lastRequest are overwritten asynchronously.

onResponse: function(e) {
  var request = e.detail;
  var response = request.response;
}

To reuse iron-ajax with a new URL, disable auto (which disables the debouncer) and manually call generateRequest() after updating the URL. This would allow multiple simultaneous async requests (and activeRequests would populate with more than one request).

// template
<iron-ajax ...>   <!-- no 'auto' -->

// script
onResponse: function(e) {
  var request = e.detail;
  var response = request.response;
  ...
  for (var i = 0; i < response.items.length; i++) {
    ajax.url = this.url(response.items[i].id);
    ajax.generateRequest();
  }
},
ready: function() {
  this.$.ajax.generateRequest(); // first request
}

Here's a modified version of your code:

<head>
  <base href="https://polygit.org/polymer+1.5.0/components/">
  <script src="webcomponentsjs/webcomponents-lite.min.js"></script>
  <link rel="import" href="polymer/polymer.html">
  <link rel="import" href="iron-ajax/iron-ajax.html">
</head>
<body>
  <x-foo></x-foo>

  <dom-module id="x-foo">
    <template>
      <!-- We're reusing this iron-ajax to fetch more data
         based on the first response, and we don't want
         iron-ajax's debouncer to limit our requests,
         so disable 'auto' (i.e., remove the attribute
         from <iron-ajax>). We'll call generateRequest()
         manually instead.
      -->
    <iron-ajax id="ajax"
               url="https://www.googleapis.com/books/v1/volumes?q=polymer"
               handle-as="json"
               on-response="onResponse"
               on-error="onError">
      </iron-ajax>
    </template>
    <script>
      HTMLImports.whenReady(function() {
        Polymer({
          is: 'x-foo',
          onError: function(e) {
            console.warn('iron-ajax error:', e.detail.error.message, 'url:', e.detail.request.url);
          },
          onResponse: function(e) {
            var ajax = this.$.ajax;
            var originalUrl = 'https://www.googleapis.com/books/v1/volumes?q=polymer';
            var url = e.detail.url;
            if (url.includes(originalUrl)) {
              var books = e.detail.response.items || [];
              console.log('this is the first request');
              for (var i = 0; i < books.length && i < 3; i++) {
                ajax.url = this.url(books[i].id);
                console.log('fetching:', ajax.url);
                ajax.generateRequest();
              }
            } else {
              var book = e.detail.response;
              console.log('received:', e.detail.url, '"' + book.volumeInfo.title + '"');
            }
          },
          url: function(id) {
            return "https://www.googleapis.com/books/v1/volumes/" + id;
          },
          ready: function() {
            // generate first request
            this.$.ajax.generateRequest();
          }
        });
      });
    </script>
  </dom-module>
</body>

https://jsbin.com/qaleda/edit?html,console




回答2:


I don't know what's up with the activeRequests property, but I was able to get it to work by re-structuring my code a little. Basically, just implement a queue, and pop off an item from the queue and set url once the last request has finished.

<!doctype html>
<html>
<head>
  <meta name="description" content="http://stackoverflow.com/questions/37817472">
  <meta charset="utf-8">
  <base href="https://polygit.org/components/">
  <script href="webcomponentsjs/webcomponents-lite.min.js"></script>
  <link href="polymer/polymer.html" rel="import">
  <link href="iron-ajax/iron-ajax.html" rel="import">
</head>
<body>
  <dom-module id="my-el">
    <template>
      <iron-ajax id="ajax"
                 url="https://www.googleapis.com/books/v1/volumes?q=polymer"
                 handle-as="json"
                 on-response="onResponse"
                 last-response="{{response}}" auto></iron-ajax>
    </template>
    <script>
      Polymer({
        is: 'my-el',
        properties: {
          response: {
            type: Object,
            notify: true
          },
          queue: {
            type: Array,
            notify: true,
            value: function() { return []; }
          }
        },
        onResponse: function(e) {
          var ajax = this.$.ajax;
          var originalUrl = 'https://www.googleapis.com/books/v1/volumes?q=polymer';
          var url = ajax.lastRequest.xhr.responseURL;
          if (url.includes(originalUrl)) {
            console.log('this is the first request');
            for (var i = 0; i < ajax.lastResponse.items.length; i++) {
              this.push('queue', ajax.lastResponse.items[i].id);
            }
            ajax.url = this.url(this.pop('queue'));
          } else {
            console.log(ajax.lastResponse.selfLink);
            ajax.url = this.url(this.pop('queue'));
          }
        },
        url: function(id) {
          return "https://www.googleapis.com/books/v1/volumes/" + id;
        }
      });
    </script>
  </dom-module>
  <my-el></my-el>
</body>
</html>

https://jsbin.com/beyawe/edit?html,console




回答3:


You can try out iron-multiple-ajax-behavior. Let me know if this can be useful to you.



来源:https://stackoverflow.com/questions/37846726/using-one-iron-ajax-element-for-multiple-requests

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!