arraysfilterpolymerpolymer-2.xdom-repeat

How to Filter Restructured Data in Polymer (dom-repeat)


I am trying to filter an array that is being reindexed on-the-fly.

I would like to have a single input field that matches strings on multiple properties.

<paper-input value="{{val}}" placeholder="Filter Cards"></paper-input>

<template is="dom-repeat" items="[[restructure(data)]]" initial-count="2" filter="{{filter(val, data)}}">
  <card-items data="[[item]]" items="[[item.items]]" links="false"></card-items>
</template>

...

This function restructures the data to be formatted for a card layout.

returnInvoices(data) {
  let newData = [];
  for (let i = 0; i < data.length; i++) {
    let noOrder = data[i].noOrder;
    if (!newData[noOrder]) {
      newData[noOrder] = {
        idMaster: data[i].idMaster,
        itemId: data[i].itemId,
        description: data[i].description,
        noOrder: noOrder,
        items: []
      };
    }
    newData[noOrder].items.push('{' +
      '"idMaster":"' + data[i].idMaster + '",' +
      '"itemId":"' + data[i].itemId + '"}');
  }
  return newData.filter(val => val).sort((a, b) => {return b.noInvoice - a.noInvoice}) // reindex array
}

I would like this function to return objects in the array whom have properties that match a string.

filter(val, data) {
  if (!val) return null;
    else {
      val = val.toLowerCase();
      // not sure what to do here
      // would like to filter on all object properties (from data)
    return data[0];
  }
}

...

Example

if(val == 1) return data[0] & data[1];

if(val == 'Item') return data[0] & data[2];

For data array

let data = [
  {"itemId": "1", "description": "Nice Item", "noOrder": 123},
  {"itemId": "2", "description": "Good Product", "noOrder": 123},
  {"itemId": "3", "description": "Fine Item", "noOrder": 444}
}

...

How can I filter strings on all 3 properties?

How can I use the filter as an intermediate function to the restructuring?


Solution

  • The documentation for dom-repeat's filter property includes following statements:

    The function should match the sort function passed to Array.filter. Using a filter function has no effect on the underlying items array.

    And the Array.filter is documented as

    Function is a predicate, to test each element of the array. Return true to keep the element, false otherwise.

    So from your filter func just return true if any of the properties matches input and false otherwise, something like

    filter(item) {
      let val = this.val;
      // if the filter is empty show everything
      if (!val) return true;
      // otherwise see is there a match
      val = val.toLowerCase();
      return // for the "description" use "contains" logic
             (item.description.toLowerCase().includes(val)) ||
             // for the "noOrder" use "starting" logic
             (item.noOrder.toString().indexOf(val) == 0)
           // add matching conditions here ...
    }
    

    Now to trigger the filtering you must observe the properties which trigger filtering, ie your html would be like

    <paper-input value="{{val}}" placeholder="Filter Cards"></paper-input>
    
    <template is="dom-repeat" filter="[[filter]]" observe="val" items="[[restructure(data)]]" initial-count="2">
      <card-items data="[[item]]" items="[[item.items]]" links="false"></card-items>
    </template>
    

    BTW why do you push items into newData as strings? Why not as objects, ie

    newData[noOrder].items.push({
      idMaster: data[i].idMaster,
      itemId: data[i].itemId
    });
    

    and I think you can lose the newData.filter(val => val) step...