vue.jscomputed-propertiesvue-material

sorting a filtered table as computed property


I have a md-table in which I can filter the rows by the name of course, the filter works fine but as soon as I click on on the column name, the rows are not sorted

you should be able to sort them like in this sandbox https://codesandbox.io/s/n7l7o742qm?module=App.vue

but if you try my code https://codesandbox.io/s/vue-material-search-and-empty-state-forked-6bb48?file=/App.vue

the sort functionality won't work

HTML

<md-table
  v-model="filteredRepetitions"
  md-sort="corso"
  md-sort-order="asc"
  md-card
  md-fixed-header
>
  <md-table-toolbar>
    <h1 class="md-title">With auto select and alternate headers</h1>
    <md-field md-clearable class="md-toolbar-section-end">
      <md-input placeholder="Search by course..." v-model="search"/>
    </md-field>
  </md-table-toolbar>


  <md-table-row
    class="md-primary"
    slot="md-table-row"
    slot-scope="{ item }"
  >
    <md-table-cell md-label="Corso" md-sort-by="corso">{{ item.corso }}</md-table-cell>
    <md-table-cell md-label="Docente" md-sort-by="docente">{{ item.docente }}</md-table-cell>
    <md-table-cell md-label="Data" md-sort-by="timeSlot.data">{{ item.timeSlot.data }}</md-table-cell>
    <md-table-cell
      md-label="Fascia oraria"
      md-sort-by="timeSlot.fasciaOraria"
    >{{ item.timeSlot.fasciaOraria }}</md-table-cell>
    <md-table-cell md-label="Stato" md-sort-by="stato">{{ item.stato }}</md-table-cell>
  </md-table-row>
</md-table>

JS

const toLower = text => text.toString().toLowerCase();
export default {
    data() {
        return {
            search: '',
            repetitions2: [
                {
                    corso: 'Programmazione 3',
                    docente: 'Liliana Aridissono',
                    timeSlot: {
                        fasciaOraria: '15:00-16:00',
                        data: '2020-09-17'
                    },
                    stato: 'attiva'
                },
                {
                    corso: 'Python',
                    docente: 'Marco Amedura',
                    timeSlot: {
                        fasciaOraria: '17:00-18:00',
                        data: '2020-03-17'
                    },
                    stato: 'noattiva',
                },
                {
                    corso: 'Matematica Discreta',
                    docente: 'Vincenzo Bellomo',
                    timeSlot: {
                        fasciaOraria: '16:00-17:00',
                        data: '2020-02-17'
                    },
                    stato: 'noattiva',
                },
                {
                    corso: 'Algoritmi',
                    docente: 'Patti Viviana',
                    timeSlot: {
                        fasciaOraria: '16:00-17:00',
                        data: '2020-10-17'
                    },
                    stato: 'attiva',
                },
                {
                    corso: 'Analisi',
                    docente: 'Pinco Pallo',
                    timeSlot: {
                        fasciaOraria: '18:00-19:00',
                        data: '2020-10-26'
                    },
                    stato: 'attiva',
                }
            ]
        }
    },
    methods: {
    },
    computed: {
        filteredRepetitions: {
            get: function () {
                return this.repetitions2.filter(item => toLower(item.corso).includes(toLower(this.search)))
            },
            set: function (array) {
               // what should i say?
            }
        }
    },
}

BONUS QUESTION: is it possible not to filter only for a particular attribute (for instance now I can filter only by course name)


Solution

  • After playing around with <md-table> a bit more, it turns out you don't actually need computed for sortColumn and sortOrder. You do need to use .sync modifier, though, as outlined in the docs.

    And you have to provide an empty setter (md-table will try to assign to the computed when it's changed). However, you don't need to assign anything, as sortColumn and sortOrder change accordingly and your getter reacts to those changes.

    I added a watch which resets sortOrder every time the sortColumn is changed - other than that, it's pretty clean.

    working demo.

    Relevant code:

    import { sortBy } from 'lodash-es';
    
    { 
      data: () => ({
        searchTerm: "",
        searchColumn: "corso",
        sortColumn: "corso",
        sortOrder: "asc",
        items: [...]
      }),
      computed: {
        filteredItems() {
          return this.items.filter(
            item => this.searchColumn
              .split(".")
              .reduce((obj, prop) => obj[prop], item)
              .toLowerCase()
              .indexOf(this.searchTerm.toLowerCase()) > -1
          );
        },
        orderedItems: {
          get: function() {
            return sortBy(this.filteredItems, this.sortColumn);
          },
          set: () => {}
        }
      },
      watch: {
        sortColumn() {
          this.$nextTick(() => (this.sortOrder = "desc"));
        }
      },
    }