Show Polymer indeterminate paper-progress when iron-ajax is loading
Asked Answered
L

5

5

I am trying to show a paper-progress when an iron-ajax request is in progress with no success. Below is the code for a custom element get-products-service which hosts the iron-ajax request, and a products-list which hosts the paper-progress.

This is the whole products-list dom-module:

<dom-module id="product-list">
  <style>
    :host {
      display: block;
      width: 100%;
      text-align: center;
    }
    product-card {
      margin-left: 10px;
      margin-bottom: 30px;
    }
</style>
<template>
  <template is="dom-if" if="{{loading}}">
    <paper-progress value="10" indeterminate="true"  style="width:100%;"></paper-progress>
  </template>
  <paper-button id="previous" on-tap='previousPage'>Previous</paper-button>
  <paper-button id="next" on-tap='nextPage'>Next</paper-button>

  <get-products-service products="{{products}}" id="productservice"   page="{{page}}" loading="{{loading}}"></get-products-service>
 <div>
    <template is="dom-repeat" items="{{products}}">
     <product-card on-cart-tap="handleCart" product="{{item}}">
       <img width="100" height="100">
       <h3>{{item.name}}</h3>
       <h4>{{item.display_price}}</h4>
     </product-card>
    </template>
  </div>
</template>
</dom-module>
<script>
  (function() {
  Polymer({
  is: 'product-list',

  properties: {
    page: {
      type: Number,
      notify: true,
      value: 1
    }
  },
  handleCart: function(event) {

  },
  previousPage: function(event){
   this.page = --this.page;
   console.log("page: "+this.page);
  },
  nextPage: function(event){
    this.page = ++this.page;
    console.log("page: "+this.page);
  }
});
})();
</script>

This is the whole get-products-service

<dom-module id="get-products-service">
  <style>
    :host {
     display: none;
    }
  </style>
<template>
  <iron-ajax id="productsajax"
    url="http://localhost:3003/api/products"
    params='{"token":"mytoken"}'
    method='GET'
    on-response='productsLoaded'
    handleAs="json"
    loading="{{loading}}" >
  </iron-ajax>
  </template>
</dom-module>

<script>
 (function() {

   Polymer({is: 'get-products-service',

     properties: {
       products: {
       type: Array,
       notify: true
     }, 
    page: {
      type: String,
      notify: true,
    },
    perpage: {
      type: String,
      readOnly: true,
      value: "6"
    },
    loading: {
      type: Boolean,
      readOnly: true,
      notify: true,
      value: false
    }
  },

productsLoaded: function(request) {
  console.log(this.$.productsajax.lastResponse);
  responseObject = this.$.productsajax.lastResponse;
  this.products = responseObject.products;
},

ready: function(){
  this.$.productsajax.params.page = this.page;
  this.$.productsajax.params.per_page = this.perpage;
},
observers: [
          'attributesReady(page)'
        ],

attributesReady: function(page) {
    this.page = page;
    this.$.productsajax.params.page = page;
    this.$.productsajax.params.per_page = this.perpage;
    console.log("service page: "+page);
    this.async(function() {
        this.$.productsajax.generateRequest();
      }, 3000);
  }
 });
})();
</script>
Legislate answered 11/6, 2015 at 19:26 Comment(2)
Both your paper-progress and get-products-service have an id of "loading". This may cause unexpected behavior.Marcy
@Marcy I hadn't noticed that, i have changed it but still not working.Legislate
M
7

Your best option is to separate your concerns. First, your get-products-service:

<dom-module id="get-products-service">
  <style>
    :host {
      display: none;
    }
  </style>
  <template>
    <iron-ajax id="productsajax"
      url="http://localhost:3003/api/products"
      method='GET'
      loading="{{loading}}"
      handleAs="json">
    </iron-ajax>
  </template>

  <script>
    Polymer({
      is: 'get-products-service',

      properties: {
        loading: {
          type: Boolean,
          readOnly: true,
          notify: true,
          value: false
        }
      }
    });
  </script>
</dom-module>

Then, in your product-list:

<template>
  <template is="dom-if" if="{{loading}}">
    <paper-progress value="10" indeterminate="true" style="width:100%;"></paper-progress>
  </template>
  <get-products-service loading="{{loading}}"></get-products-service>
</template>

This way get-products-service and paper-progress don't have to know anything about each other, and should be more composable elsewhere in your application.

Marcy answered 11/6, 2015 at 20:16 Comment(17)
When I try this, the progress-bar does not appear at all.Legislate
Yes the iron-ajax request works and it returns the expected response. Something else, the get-products-service is not in the dom-bind template but inside of a list template, I'm not sure whether this makes any difference for your answer although i've tried the dom-if template in both the dom-bind and the list template and the progress-bar does not appear in both.Legislate
Can you post a more complete example of where get-products-service is on the page in relation to paper-progress?Marcy
I have edited the question with the location of both the get-products-service and paper-progressLegislate
@OptimusPette I have updated my answer, please give it another look.Marcy
Not working with the updated answer. When I inspect iron-ajax element with the browser, it does not have the loading attribute, which probably means it's null.Legislate
loading property needs notify: true to be able to push values up the tree.Visitation
Gah, can't believe I missed that. I updated the answer to add it. Thanks @ScottMiles!Marcy
@Marcy where is the value of loading property being set to true? I have copied your answer word for word and still cant see the progress-bar.Legislate
@OptimusPette it is from the iron-ajax itself. Did you include the notify:true that @ScottMiles pointed out?Marcy
@Marcy I have edited the question to include everything in the get-products-service and everthing in the products-list. I have also included all your suggestions. Can you please check why it is not working on my end?Legislate
@OptimusPette It looks like you're missing the step that initiates the iron-ajax request. Note the auto="go" attribute on the iron-ajax in @ScottMiles's answer. Alternatively, if you want it to execute immediately you can just put the auto attribute on.Marcy
I am initiating the request in the attributesReady function with this.$.productsajax.generateRequest();Legislate
Let us continue this discussion in chat.Legislate
I am unfortunately unable to access SO chat as it is blocked by my corporate network.Marcy
I can't find any errors in your code that would prevent loading from propagating up to the dom-if containing the progress bar. I suspect that being a localhost call it is simply returning the results so quickly that the progress bar does not have an opportunity to appear. If that is not the case, you may need to arrange a jsbin of your code for us to test in a live environment.Marcy
I think after paper-progress, there was supposed to be a closing template tag, rather than opening a new one (before get-product-service).Moorfowl
L
3

Finally I was able to have this working using a combination of @Zikes and @Scott answers. First I seperated the concerns as suggested by @Zikes so i put this in product-list element.

<template>
  <template is="dom-if" if="{{loading}}">
    <paper-progress value="10" indeterminate="true" style="width:100%;"></paper-progress>
  <template>
  <get-products-service loading="{{loading}}"></get-products-service>
</template>

In the get-products-service element is where i had to make some changes to have it working. For some reason, i wouldn't get the loading property from the iron-ajax set to true. Therefore @Zikes answer wouldn't work as it was. I had to make a few changes:

  1. Explicitly set the value of the loading property to true just before the iron-ajax request is fired.
  2. Explicitly set the value of loading to false when the on-response event is fired.
  3. For this to work I changed @Zikes answer's loading property by removing readOnly: true; line.

Using @Scott suggestion, I slowed things down by using:

this.async(function() {
        this.$.productsajax.generateRequest();
      }, 5000);

to get a chance to see the progress-bar .

The resulting get-products-service element looks like this:

<dom-module id="get-products-service">
  <style>
    :host {
      display: none;
    }
  </style>
  <template>
    <iron-ajax id="productsajax"
      url="http://localhost:3003/api/products"
      method='GET'
      on-response='productsLoaded'
      handleAs="json">
    </iron-ajax>
  </template>
</dom-module>

<script>
  Polymer({is: 'get-products-service',
    properties: {
     loading: {
      type: Boolean,
      notify: true,
      value: false
      }
    },
    //on-response callback
     productsLoaded: function(request) {
         this.loading = false;
     },

   //An observer method where ajax request is generated
    attributesReady: function(page) {
      //set the value of loading to true just before generating request
      this.loading = true;
      //slow things to get a chance to see the progress-bar
      this.async(function() {
        this.$.productsajax.generateRequest();
      }, 5000);
    }

  });
</script>

I believe @Zikes answer should work too and I'm the one who failed to implement it correctly so I will accept it as the correct answer.

Legislate answered 12/6, 2015 at 18:10 Comment(0)
V
2

@Zikes has the right answer. I'm supplementing here with a fully working implementation (only for Chrome, x-platform requires a few tweaks):

<!doctype html>
<html>
<head>

  <meta charset="utf-8">

  <base href="http://milestech.net/components/">

  <link href="iron-ajax/iron-ajax.html" rel="import">
  <link href="paper-progress/paper-progress.html" rel="import">

</head>
<body>

  <use-service></use-service>

  <dom-module id="get-products-service">
    <template>

      <iron-ajax url="iron-ajax/bower.json" auto="{{go}}" handle-as="text" last-response="{{products}}"></iron-ajax>

    </template>
    <script>

      Polymer({

        properties: {
          products: {
            notify: true,
            value: null
          }
        },

        ready: function() {
          // this bit just because otherwise it's too fast to see
          this.async(function() {
            this.go = true;
          }, 1000);
        }
      });

    </script>
  </dom-module>

  <dom-module id="use-service">
    <template>

      <template is="dom-if" if="{{!products}}">
        <paper-progress value="10" indeterminate="true" style="width:100%;"></paper-progress>
      </template>

      <get-products-service products="{{products}}"></get-products-service>

      <pre>{{products}}</pre>

    </template>
    <script>

      Polymer({});

    </script>
  </dom-module>

</body>
</html>
Visitation answered 12/6, 2015 at 1:13 Comment(1)
can you tell me why with Zikes answer the loading property is not being set to true even when the request is loading?Legislate
P
0

I know it's kinda late, but I ran into a similar dilemma and decided to expose the API. This way I could show the exact percent of progress of the XHR request.

Note: Very Hacky

I did this by overriding the default generateRequest() method, with a few additions.

app.$.productsajax.generateRequest = function() {
  var request = (document.createElement('iron-request'));
  var requestOptions = this.toRequestOptions();
  ///**=============================================///
  ///**   Added to expose progress - Super Hacky  **///
  var me = this;
  request.ap_obs = function(a) {
    me.progress = a;
    me._notifyChange("progress")
  };
  request._addObserverEffect("progress", "ap_obs")
    ///**===========================================**///
  this.activeRequests.push(request);
  request.completes.then(
    this._boundHandleResponse
  ).catch(
    this._handleError.bind(this, request)
  ).then(
    this._discardRequest.bind(this, request)
  );
  request.send(requestOptions);
  this._setLastRequest(request);
  this._setLoading(true);
  this.fire('request', {
    request: request,
    options: requestOptions
  });
  return request;
}

So now your element can look like:

<template>
  <iron-ajax url="iron-ajax/bower.json" progress="{{progress}}" loading="{{activePageReq}}" auto="{{go}}" handle-as="text" last-response="{{products}}"></iron-ajax>
  <paper-progress hide$="{{!activePageReq}}" value="{{progress.loaded}}" ,max="{{progress.total}}"></paper-progress>
</template>
Permutation answered 21/9, 2015 at 0:33 Comment(2)
Why is this 'very hacky'? Is it not secure or is it a 'quick and dirty' solution?Sublimity
The problem with this solution is that it assumes that the implementation of generateRequest will never change. This means any updates to the component will be overwritten by this hack here. ( ie. It is a quick and dirty solution)Permutation
M
0

In my case, I wanted to keep the progress bar always visible in a menu area, and start / stop it from different elements when doing some work that might take some time.

The progress is indeterminate by default on loading

<paper-progress id="working" indeterminate ></paper-progress>

We have a global namespace object app and I just add there a function as follow

  app.working = function(flag){
    document.querySelector("#working").indeterminate = flag;
  };

In that case, when the initial loading is done, I just do

app.working(false);

And it can be started / stopped later on from any element in the app, just by doing

app.working(true);
// ... long process
app.working(false);
Math answered 23/6, 2016 at 17:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.