3

I am new to Polymer and currently learning Polymer 3.0. I am trying to make an app which is working as follows:

  1. fetches playlists data from public url (using iron-ajax element)
  2. handle data in handleResponse() func, and assign the value to 'playlistNames' variable.
  3. Uses routing to display playlists and songs

Problem:(where i am stuck)

On refreshing page, http://127.0.0.1:8081/playlist1, the router is loading 404 page and not the playlist page, coz playlistName is empty. However if i click on any playlist (after refreshing page), the router is working.

I cannot access the updated playlistNames array in a different function '_routePageChanged'. Code snippet is below.

In handleResponse() func i am getting correct output (array with list of playlists). But in _routePageChanged() func' the output is [ ], an empty array.

app-console-output-screenshot


Question:

My understanding: app-route element is handled before iron-ajax element.

How can i access the new value of playlistNames in _routePageChanged?


file: my-element.js

class MyApp extends PolymerElement {
    static get template() {
      return html `  
        <!-- make api calls to fetch the playlists -->
        <iron-ajax
                auto
                url=""
                handle-as = "json"
                on-response="handleResponse"
                last-response="{{ajaxResponse}}">
        </iron-ajax>

        <app-location route="{{route}}" url-space-regex="^[[rootPath]]">
        </app-location>

        <app-route route="{{route}}" pattern="[[rootPath]]:page" data="{{routeData}}" tail="{{subroute}}">
        </app-route>
  
        ...
            <iron-pages selected="[[page]]" attr-for-selected="name" role="main">
              <my-view404 name="view404"></my-view404>
              <data-send name="playlist" prop1="[[selectedPlaylist]]"></data-send>
            </iron-pages>
      `;
    }

    static get properties() {
      return {
        page: {
          type: String,
          reflectToAttribute: true,
          observer: '_pageChanged'
        },
        playlists: {
          type: Array,
          reflectToAttribute: true,
          value: 'waiting...',
          reflectToAttribute: true
        },
        playlistNames: {
          type: Array,
          value: function() {
            return [];
          }
        },
        selectedPlaylist: {
          type: String,
          value: 'selectedPlaylist is empty'
        },
        routeData: Object,
        subroute: Object
      };
    }

    static get observers() {
      return [
        '_routePageChanged(routeData.page, this.playlistNames.splices)'
      ];
    }
  
    // this function is called on api call
    handleResponse(event, request) { 
      //  get all the playlists from the api call response
      this.playlists = request.response.playlists;

      // get all the playlist names
      for(let playlist of this.playlists) {
        if(this.playlistNames.length < this.playlists.length) {
          // this.playlistNames.push(playlist.name);
          this.push('playlistNames', playlist.name);
        }  
      }
      console.log('handleResponse playlists', this.playlistNames); // ['playlist1', 'playlist2' ...]
  
      // on refreshing url, if any route is present, then find its id to fetch songs list from api
      if(this.routeData.page){
        for(let playlist of this.playlists){
          if(playlist.name === this.routeData.page){
            this.selectedPlaylist = playlist.id;
          }
        }
      }
    }

    // this func is called on route change or page refresh
    _routePageChanged(page, data) {
      console.log('routeData.page=', page);
      console.log('data=', data); // undefined
      console.log('_routePageChanged playlists.length=', this.playlistNames.length); // 0
      console.log('_routePageChanged playlists=', this.playlistNames); // empty  

      if (!page) {
        this.page = 'view404';
      } else if (["playlist1", "playlist2"].indexOf(page)  !== -1) {
      //playlistNames is empty[],
      // } else if (this.playlistNames.indexOf(page) !== -1) { 
        this.page = 'playlist';
      } else {
        this.page = 'view404';
      }
    }
  
    _pageChanged(page) {
      switch (page) {
        case 'view404':
          import('./my-view404.js');
          break;
        case 'playlist':
          import('./playlist-songs.js');
          break;
      }
    }
  }
Raj
  • 1,100
  • 3
  • 20
  • 33
  • have you tried to console log the `request.response.playlists` what is the output?| – Hyyan Abo Fakher Aug 23 '18 at 07:57
  • @HyyanAboFakher yes i did console log. in `handleResponse()` i am getting the data. but in `_routePageChanged()` i am getting empty array **on first click**. because when i refresh **`_routePageChanged()` is called first then handleResponse()**. – Raj Aug 23 '18 at 12:55

1 Answers1

3

EDIT: Improved answer

As you stated in the comment, when route changes to the view, _routePageChanged() will always run before handleResponse() (because handleResponse() is asynchronous) so whatever handleResponse() does, will only do after _routePageChanged().

If you're gonna follow my solution, you need to first make the changes on the array property playlistNames observable, so:

Instead of:

this.playlistNames.push(playlist.name);

Use:

this.push('playlistNames', playlist.name);

As documented here

Solution - If you really need to check on _routePageChanged():

You can change _routePageChanged to observe two properties, instead of just the one property page. So instead of _routePageChanged(page), you can declare the observer as _routePagePlaylistNamesChanged(page, playlistNames) and have logic in it. This way, the observer will fire anytime any of the two properties change value and you can have logic in it, something like:

_routePagePlaylistNamesChanged(page, playlistNames) {
  if(page == "myelement" && playlistNames.length > 0){
    //This is correct page and the playlistNames are defined
    console.log('_routePagePlaylistNamesChanged playlists', this.playlistNames);
  }
}
AmmoPT
  • 958
  • 1
  • 9
  • 16
  • Polymer's array mutation methods are only to make observable changes to arrays, so this does not answer why `this.playlistNames` is empty – Hyyan Abo Fakher Aug 23 '18 at 07:59
  • @Raj I improved my answer, care to take a look? – AmmoPT Aug 23 '18 at 14:11
  • @AmmoPT Thanks for your time. Yes i checked [Observe array mutations](https://www.polymer-project.org/3.0/docs/devguide/observers#array-observation) and updated my code. But the `data` is undefined. I haved **updated my code above and also added screenshot** for your reference. Can you please have a look and suggest where am i going wrong? – Raj Aug 24 '18 at 16:22
  • @Raj I seen the updated question, however it seems to me the problem remains the same. Have you tried the code I suggested? Observing both properties in the same observer. – AmmoPT Aug 24 '18 at 16:32
  • @AmmoPT yes. 1. changed array property `playlistNames` observable to `this.push('playlistNames', playlist.name);` 2. declared inside `observer() func` as `_routePagePlaylistNamesChanged(page, this.playlistNames)`. Observing both properties. – Raj Aug 24 '18 at 16:52