Polymer 1.0: Sorting iron-list

2019-07-09 23:30发布

Google's Polymer creators claim we can manually sort <iron-list> (resolving this issue, perhaps?).

Also see this issue.

Per @emmanuel on the Polymer Slack Site:

You can set list.items = newItems and that will refresh the list.

Here is my code. But it's not working. Please help.

This section of code seems to be the problem.
//this.$.list.items = this.items.sort(this._computeSort(val, ord));
//this.items = this.items.sort(this._computeSort(val, ord));
//this.items.sort(this._computeSort(val, ord));
//this.$.list.items = this.items.sort(this._computeSort(val, ord));

(I commented out everything I tried that failed.)

JSBin link

http://jsbin.com/xoqubecado/edit?html,output
<html>

<head>
  <title>My Element</title>
  <script data-require="polymer@*" data-semver="1.0.0" src="http://www.polymer-project.org/1.0/samples/components/webcomponentsjs/webcomponents-lite.js"></script>
  <script data-require="polymer@*" data-semver="1.0.0" src="http://www.polymer-project.org/1.0/samples/components/polymer/polymer.html"></script>
  <base href="http://element-party.xyz/" />
  <link rel="import" href="all-elements.html" />
</head>

<body>
  <dom-module id="my-element">
    <template>
    <style>
      h3 {
        margin-bottom: 0px;
      }
      iron-list {
        padding-bottom: 16px;
      }
      .item {
        @apply(--layout-horizontal);
        margin: 16px 16px 0 16px;
        padding: 20px;
        border-radius: 8px;
        background-color: white;
        border: 1px solid #ddd;
       }
    </style>
    <firebase-collection location="https://dinosaur-facts.firebaseio.com/dinosaurs" data="{{items}}">
    </firebase-collection>
    <h3>Controls</h3>
    <paper-dropdown-menu label="Sort by">
      <paper-menu class="dropdown-content" selected="{{sortVal}}" attr-for-selected="data-sortby">
        <paper-item data-sortby="order">Order</paper-item>
        <paper-item data-sortby="height">Height</paper-item>
      </paper-menu>
    </paper-dropdown-menu>
    <br>
    <paper-toggle-button checked="{{reverse}}">Reverse</paper-toggle-button>
    <br /><br />
    <br><h3>Monitor Control Values</h3>
    <div>Sort by: [[sortVal]]</div>
    <div>Reverse: [[reverse]]</div>
    <br><h3>Iron-List Output</h3>
    <iron-list id="list" items="[[items]]" as="item">
      <template>
        <div class="item">
          Name: [[item.__firebaseKey__]]<br />
          Order: [[item.order]]<br />
          Height: [[item.height]]
        </div>
      </template>
    </iron-list>
    </template>
    <script>
    (function() {
      Polymer({
        is: "my-element",
        properties: {
          items: {
            type: Array,
          },
          sortVal: {
            type: String,
            value: 'order'
          },
          sortOrder: {
            type: Number,
            value: -1, // High to low
            computed: '_computeSortOrder(reverse)'
          }
        },
        observers: [
          'sortChanged(sortVal, sortOrder)'
        ],
        _computeSortOrder: function(bool) {
          return bool ? 1 : -1;
        },
        sortChanged(val, ord) {
          // Also tried what is commented out
          //this.$.list.items = this.items.sort(this._computeSort(val, ord));
          //this.items = this.items.sort(this._computeSort(val, ord));
          //this.items.sort(this._computeSort(val, ord));
          //this.$.list.items = this.items.sort(this._computeSort(val, ord));
          console.log('ord: ' + ord); // All values logged
          console.log('val: ' + val); // Logs are correct
          console.log('items: ' + this.items); // See console log
        },
        _computeSort: function(val, ord) {
          return function(a, b) {
            if (a[val] === b[val]) {
              return 0;
            }
            return (ord * (a[val] > b[val] ? 1 : -1));
          };
        }
      });
    })();
    </script>
  </dom-module>
  <my-element></my-element>
</body>

</html>

This JSBin proves the sorting logic.

1条回答
相关推荐>>
2楼-- · 2019-07-10 00:01

Per @rob, Polymer Slack Site: (Credit to @arthur for the solution!)

here's a version that sorts: http://jsbin.com/mabadi/2/edit?html,output

the main issue is that if you change the contents of an array, polymer has no idea that you've done that (it can't see inside of an array). So you need to create a copy of your data, sort it, and set that as the new value

it may be helpful to have a separate variable for firebaseItems and sortedItems. That way you don't end up accidentally writing back to firebase

also make sure you give iron-list a height

height: 100% or something like that. And make sure its container has a height as well

http://jsbin.com/vizexodoyi/1/edit?html,output
<html>

<head>
  <title>My Element</title>
  <script data-require="polymer@*" data-semver="1.0.0" src="http://www.polymer-project.org/1.0/samples/components/webcomponentsjs/webcomponents-lite.js"></script>
  <script data-require="polymer@*" data-semver="1.0.0" src="http://www.polymer-project.org/1.0/samples/components/polymer/polymer.html"></script>
  <base href="http://element-party.xyz/" />
  <link rel="import" href="all-elements.html" />
</head>

<body>
  <dom-module id="my-element">
    <template>
    <style>
      h3 {
        margin-bottom: 0px;
      }
      iron-list {
        padding-bottom: 16px;
        height: 100%;
      }
      .item {
        @apply(--layout-horizontal);
        margin: 16px 16px 0 16px;
        padding: 20px;
        border-radius: 8px;
        background-color: white;
        border: 1px solid #ddd;
       }
    </style>
    <firebase-collection location="https://dinosaur-facts.firebaseio.com/dinosaurs" data="{{items}}">
    </firebase-collection>
    <h3>Controls</h3>
    <paper-dropdown-menu label="Sort by">
      <paper-menu class="dropdown-content" selected="{{sortVal}}" attr-for-selected="data-sortby">
        <paper-item data-sortby="order">Order</paper-item>
        <paper-item data-sortby="height">Height</paper-item>
      </paper-menu>
    </paper-dropdown-menu>
    <br>
    <paper-toggle-button checked="{{reverse}}">Reverse</paper-toggle-button>
    <br /><br />
    <br><h3>Monitor Control Values</h3>
    <div>Sort by: [[sortVal]]</div>
    <div>Reverse: [[reverse]]</div>
    <br><h3>Iron-List Output</h3>
    <iron-list id="list" items="[[items]]" as="item">
      <template>
        <div class="item">
          Name: [[item.__firebaseKey__]]<br />
          Order: [[item.order]]<br />
          Height: [[item.height]]
        </div>
      </template>
    </iron-list>
    </template>
    <script>
    (function() {
      Polymer({
      is: "my-element",
      properties: {
        items: {
          type: Array,
        },
        sortVal: {
          type: String,
          value: 'order'
        },
        sortOrder: {
          type: Number,
          value: -1, // High to low
          computed: '_computeSortOrder(reverse)'
        }
      },
      observers: [
        'sortChanged(sortVal, sortOrder)'
      ],
      _computeSortOrder: function(bool) {
        return bool ? 1 : -1;
      },
      sortChanged(val, ord) {
        if (! this.items || this.items.length == 0) {
          return;
        }
        var temp = Array.prototype.slice.call(this.items);
        temp.sort(this._computeSort(val, ord));
        this.items = temp;
        //console.log('ord: ' + ord);
        //console.log('val: ' + val);
        //console.log('items: ' + this.items);
      },
      _computeSort: function(val, ord) {
        return function(a, b) {
          if (a[val] === b[val]) {
            return 0;
          }
          return (ord * (a[val] > b[val] ? 1 : -1));
        };
      }
     });
    })();
    </script>
  </dom-module>
  <my-element></my-element>
</body>

</html>

Optimizing Performance

For those interested in Array.prototype.slice.call(this.items), you might want to study this SO question and the performance comparison of different solutions here.

One apparent performance improvement might be:

// Reference for below: https://stackoverflow.com/questions/3199588/fastest-way-to-convert-javascript-nodelist-to-array
//var temp = Array.prototype.slice.call(this.items);
var temp = [];
var i = this.items.length;
while(i--) {
  //temp.unshift(this.items[i]);
  temp[i] = this.items[i]; 
}
查看更多
登录 后发表回答