Why isn't iron-list updated when the list is hidden?

1.2k Views Asked by At

I'm trying to use a hidden <iron-list>, but I need to understand why it's rendering doesn't update.

This is the related issue: https://github.com/PolymerElements/iron-list/issues/263

This is the code: http://jsbin.com/vagumebupe/9/edit?html,console,output

<!doctype html>
<head>
  
  <meta charset="utf-8">

  <base href="https://polygit.org/polymer+:master/components/">

  <script src="webcomponentsjs/webcomponents-lite.min.js"></script>
  
  <link href="polymer/polymer.html" rel="import">

  <link rel="import" href="iron-flex-layout/iron-flex-layout.html">
  <link rel="import" href="iron-ajax/iron-ajax.html">
  <link rel="import" href="paper-icon-button/paper-icon-button.html">
  <link rel="import" href="iron-icon/iron-icon.html">
  <link rel="import" href="iron-icons/iron-icons.html">
  <link rel="import" href="paper-styles/color.html">
  <link rel="import" href="paper-styles/typography.html">
  <link rel="import" href="app-layout/app-toolbar/app-toolbar.html">
  <link rel="import" href="paper-menu/paper-menu.html">
  <link rel="import" href="paper-item/paper-item.html">
  <link rel="import" href="paper-badge/paper-badge.html">
  <link rel="import" href="iron-list/iron-list.html">
  
  <style is="custom-style">
  body {
    @apply(--layout-fullbleed);
  }
</style>

</head>

<body unresolved>

  <x-app></x-app>
  
<dom-module id="x-app">
  <style>
      :host {
        @apply(--layout-fit);
        @apply(--layout-vertical);
        @apply(--paper-font-common-base);
        font-family: sans-serif;
      }
      app-toolbar {
        background: var(--paper-pink-500);
        box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.3);
        color: white;
        z-index: 1;
        color: white;
        --paper-toolbar-title: {
          font-size: 16px;
          line-height: 16px;
          font-weight: bold;
          margin-left: 0;
        };
      }
      app-toolbar paper-icon-button {
        --paper-icon-button-ink-color: white;
      }
      #itemsList,
      #selectedItemsList {
        @apply(--layout-flex);
      }
      .item {
        @apply(--layout-horizontal);
        cursor: pointer;
        padding: 16px 22px;
        border-bottom: 1px solid #DDD;
      }
      .item:focus,
      .item.selected:focus {
        outline: 0;
        background-color: #ddd;
      }
      .item.selected .star {
        color: var(--paper-blue-600);
      }
      .item.selected {
        background-color: var(--google-grey-300);
        border-bottom: 1px solid #ccc;
      }
      .avatar {
        height: 40px;
        width: 40px;
        border-radius: 20px;
        box-sizing: border-box;
        background-color: #ddd;
      }
      .pad {
        @apply(--layout-flex);
        @apply(--layout-vertical);
        padding: 0 16px;
      }
      .primary {
        font-size: 16px;
      }
      .secondary {
        font-size: 14px;
      }
      .dim {
        color: gray;
      }
      .star {
        width: 24px;
        height: 24px;
      }
      paper-badge {
        -webkit-transition: all 0.1s;
        transition: all 0.1s;
        opacity: 1;
        margin-top: 5px;
      }
      paper-badge[label="0"] {
        opacity: 0;
      }
      #starredView {
        width: 200px;
        border-left: 1px solid #ddd;
      }
      paper-item {
        white-space: nowrap;
        cursor: pointer;
        position: relative;
      }
      paper-item:hover::after {
        content: "-";
        width: 16px;
        height: 16px;
        display: block;
        border-radius: 50% 50%;
        background-color: var(--google-red-300);
        margin-left: 10px;
        line-height: 16px;
        text-align: center;
        color: white;
        font-weight: bold;
        text-decoration: none;
        position: absolute;
        right: 15px;
        top: calc(50% - 8px);
      }
      .noSelection {
        color: #999;
        margin-left: 10px;
        line-height: 50px;
      }
      .twoColumns {
        @apply(--layout-flex);
        @apply(--layout-horizontal);
        overflow: hidden;
      }
      #starredView {
        @apply(--layout-vertical);
      }
    </style>
    <template>
      <app-toolbar>
        <div title>Selection using iron-list</div>
        <div>
          <paper-icon-button icon="icons:more-horiz" alt="hidden" on-tap="_toggleHidden"></paper-icon-button>
          <paper-icon-button icon="icons:add" alt="add" on-tap="_changeContactList"></paper-icon-button>
          <paper-badge label$="[[selectedItems.length]]"></paper-badge>
          
        </div>
      </app-toolbar>

        <!-- Main List for the items -->
        <iron-list id="itemsList" items="[[data]]" selected-items="{{selectedItems}}" selection-enabled multi-selection hidden="{{hidden}}">
          <template>
            <div>
              <div tabindex$="[[tabIndex]]" aria-label$="Select/Deselect [[item.name]]" class$="[[_computedClass(selected)]]">
                <img class="avatar" src="[[item.image]]">
                <div class="pad">
                  <div class="primary">
                    [[item.name]]
                  </div>
                  <div class="secondary dim">[[item.shortText]]</div>
                </div>
                <iron-icon icon$="[[iconForItem(selected)]]" class="star"></iron-icon>
              </div>
              <div class="border"></div>
            </div>
          </template>
        </iron-list>
    </template>
  <script>
    
    addEventListener('WebComponentsReady', function() {
      
      Polymer({
        is: "x-app",
        behaviors: [
          Polymer.IronResizableBehavior
        ],
        listeners: {
          'iron-resize': '_onIronResize'
        },
        properties: {
          hidden: {
            type: Object,
            notify: true,
            value: false
          },
          selectedItems: {
            type: Object
          },
          data: {
            type: Object,
            notify: true,
            value : [
                      {
                        "name": "Liz Grimes",
                        "image": "https://s3.amazonaws.com/uifaces/faces/twitter/enda/73.jpg",
                        "shortText": "est ad reprehenderit occaecat consequat"
                      },
                      {
                        "name": "Frazier Lara",
                        "image": "https://s3.amazonaws.com/uifaces/faces/twitter/guillogo/73.jpg",
                        "shortText": "consectetur culpa adipisicing voluptate enim"
                      }
                    ]
          }
        },
        iconForItem: function(isSelected) {
          return isSelected ? 'star' : 'star-border';
        },
        _computedClass: function(isSelected) {
          var classes = 'item';
          if (isSelected) {
            classes += ' selected';
          }
          return classes;
        },
        _unselect: function(e) {
          this.$.itemsList.deselectItem(e.model.item);
        },
        _changeContactList: function() {
          this.data = [
            {
              "name": "Shelley Molina",
              "image": "https://s3.amazonaws.com/uifaces/faces/twitter/smalonso/73.jpg",
              "shortText": "laboris do velit ipsum non"
            }
          ];
          console.log('Replace data !')
        },
        _toggleHidden: function(){
          this.hidden = !this.hidden;
          console.log('Hidden : ' + this.hidden)
        },
        _onIronResize: function() {
           console.log('Resize');
        }

      });
      
    });
    
  </script>
</dom-module>
  
</body>

1

There are 1 best solutions below

5
tony19 On

The iron-list renders its items when the list is resized or when its items property changes. As an optimization, its _render() exits if the list is not visible. (It could be relatively expensive to render a list item, depending on the item's complexity and especially on mobile devices, so ignoring render when the list is hidden would save CPU cycles.)

When you unhide your list, the iron-resize event does not trigger because the physical dimensions of iron-list (or in this case x-app, which implements IronResizableBehavior) haven't changed, and therefore, the list doesn't update.

Following the advice from the docs (emphasis mine):

Resizing

iron-list lays out the items when it receives a notification via the iron-resize event. This event is fired by any element that implements IronResizableBehavior.

By default, elements such as iron-pages, paper-tabs or paper-dialog will trigger this event automatically. If you hide the list manually (e.g. you use display: none) you might want to implement IronResizableBehavior or fire this event manually right after the list became visible again. For example:

document.querySelector('iron-list').fire('iron-resize');

...you should manually trigger the iron-resize event upon unhiding the iron-list. To do that, we use an observer on your element's hidden property:

Polymer({
  ...
  properties: {
    hidden: {
      type: Object,
      notify: true,
      value: false,
      observer: '_hiddenChanged'
    },
    ...
  },
  _hiddenChanged: function(hidden) {
    if (!hidden) {
      this.$.itemsList.fire('iron-resize');
    }
  },
});

Here's a working demo:

<!doctype html>

<head>
  <meta name="description" content="Polymer iron-list items added while list hidden">

  <meta charset="utf-8">

  <base href="https://polygit.org/polymer+1.5.0/components/">

  <script src="webcomponentsjs/webcomponents-lite.min.js"></script>

  <link href="polymer/polymer.html" rel="import">

  <link rel="import" href="iron-flex-layout/iron-flex-layout.html">
  <link rel="import" href="iron-ajax/iron-ajax.html">
  <link rel="import" href="paper-icon-button/paper-icon-button.html">
  <link rel="import" href="iron-icon/iron-icon.html">
  <link rel="import" href="iron-icons/iron-icons.html">
  <link rel="import" href="paper-styles/color.html">
  <link rel="import" href="paper-styles/typography.html">
  <link rel="import" href="app-layout/app-toolbar/app-toolbar.html">
  <link rel="import" href="paper-menu/paper-menu.html">
  <link rel="import" href="paper-item/paper-item.html">
  <link rel="import" href="paper-badge/paper-badge.html">
  <link rel="import" href="iron-list/iron-list.html">

  <style is="custom-style">
    body {
      @apply(--layout-fullbleed);

    }
  </style>

</head>

<body unresolved>

  <x-app></x-app>

  <dom-module id="x-app">
    <style>
      :host {
        @apply(--layout-fit);

@apply(--layout-vertical);

@apply(--paper-font-common-base);

font-family: sans-serif;
      }
      app-toolbar {
        background: var(--paper-pink-500);
        box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.3);
        color: white;
        z-index: 1;
        color: white;
        --paper-toolbar-title: {
          font-size: 16px;
          line-height: 16px;
          font-weight: bold;
          margin-left: 0;
        }
        ;
      }
      app-toolbar paper-icon-button {
        --paper-icon-button-ink-color: white;
      }
      #itemsList,
      #selectedItemsList {
        @apply(--layout-flex);

      }
      .item {
        @apply(--layout-horizontal);

cursor: pointer;
        padding: 16px 22px;
        border-bottom: 1px solid #DDD;
      }
      .item:focus,
      .item.selected:focus {
        outline: 0;
        background-color: #ddd;
      }
      .item.selected .star {
        color: var(--paper-blue-600);
      }
      .item.selected {
        background-color: var(--google-grey-300);
        border-bottom: 1px solid #ccc;
      }
      .avatar {
        height: 40px;
        width: 40px;
        border-radius: 20px;
        box-sizing: border-box;
        background-color: #ddd;
      }
      .pad {
        @apply(--layout-flex);

@apply(--layout-vertical);

padding: 0 16px;
      }
      .primary {
        font-size: 16px;
      }
      .secondary {
        font-size: 14px;
      }
      .dim {
        color: gray;
      }
      .star {
        width: 24px;
        height: 24px;
      }
      paper-badge {
        -webkit-transition: all 0.1s;
        transition: all 0.1s;
        opacity: 1;
        margin-top: 5px;
      }
      paper-badge[label="0"] {
        opacity: 0;
      }
      #starredView {
        width: 200px;
        border-left: 1px solid #ddd;
      }
      paper-item {
        white-space: nowrap;
        cursor: pointer;
        position: relative;
      }
      paper-item:hover::after {
        content: "-";
        width: 16px;
        height: 16px;
        display: block;
        border-radius: 50% 50%;
        background-color: var(--google-red-300);
        margin-left: 10px;
        line-height: 16px;
        text-align: center;
        color: white;
        font-weight: bold;
        text-decoration: none;
        position: absolute;
        right: 15px;
        top: calc(50% - 8px);
      }
      .noSelection {
        color: #999;
        margin-left: 10px;
        line-height: 50px;
      }
      .twoColumns {
        @apply(--layout-flex);

@apply(--layout-horizontal);

overflow: hidden;
      }
      #starredView {
        @apply(--layout-vertical);

      }
    </style>
    <template>
      <app-toolbar>
        <div title>Selection using iron-list</div>
        <div>
          <paper-icon-button icon="icons:more-horiz" alt="hidden" on-tap="_toggleHidden"></paper-icon-button>
          <paper-icon-button icon="icons:add" alt="add" on-tap="_changeContactList"></paper-icon-button>
          <paper-badge label="[[selectedItems.length]]"></paper-badge>

        </div>
      </app-toolbar>

      <!-- Main List for the items -->
      <iron-list id="itemsList" items="[[data]]" selected-items="{{selectedItems}}" selection-enabled multi-selection hidden="{{hidden}}">
        <template>
          <div>
            <div tabindex$="[[tabIndex]]" aria-label$="Select/Deselect [[item.name]]" class$="[[_computedClass(selected)]]">
              <img class="avatar" src="[[item.image]]">
              <div class="pad">
                <div class="primary">
                  [[item.name]]
                </div>
                <div class="secondary dim">[[item.shortText]]</div>
              </div>
              <iron-icon icon="[[iconForItem(selected)]]" class="star"></iron-icon>
            </div>
            <div class="border"></div>
          </div>
        </template>
      </iron-list>
    </template>
    <script>
      HTMLImports.whenReady(function() {

        Polymer({
          is: "x-app",
          behaviors: [
            Polymer.IronResizableBehavior
          ],
          listeners: {
            'iron-resize': '_onIronResize'
          },
          properties: {
            hidden: {
              type: Object,
              notify: true,
              value: false,
              observer: '_hiddenChanged'
            },
            selectedItems: {
              type: Object
            },
            data: {
              type: Object,
              notify: true,
              value: [{
                "name": "Liz Grimes",
                "image": "https://s3.amazonaws.com/uifaces/faces/twitter/enda/73.jpg",
                "shortText": "est ad reprehenderit occaecat consequat"
              }, {
                "name": "Frazier Lara",
                "image": "https://s3.amazonaws.com/uifaces/faces/twitter/guillogo/73.jpg",
                "shortText": "consectetur culpa adipisicing voluptate enim"
              }]
            }
          },
          iconForItem: function(isSelected) {
            return isSelected ? 'star' : 'star-border';
          },
          _computedClass: function(isSelected) {
            var classes = 'item';
            if (isSelected) {
              classes += ' selected';
            }
            return classes;
          },
          _hiddenChanged: function(hidden) {
            if (!hidden) {
              console.log('firing iron-list.iron-resize');
              this.$.itemsList.fire('iron-resize');
            }
          },
          _unselect: function(e) {
            this.$.itemsList.deselectItem(e.model.item);
          },
          _changeContactList: function() {
            this.push('data', {
              "name": "Shelley Molina",
              "image": "https://s3.amazonaws.com/uifaces/faces/twitter/smalonso/73.jpg",
              "shortText": "laboris do velit ipsum non"
            });
            console.log('Replace data !')
          },
          _toggleHidden: function() {
            this.hidden = !this.hidden;
            console.log('Hidden : ' + this.hidden)
          },
          _onIronResize: function() {
            console.log('Resize');
          }

        });

      });
    </script>
  </dom-module>

</body>

modified jsbin