Search code examples
vue.jsvuejs3lodash

Vue3 sortedArray not updating on sorting correctly


I have this sortedArray where I am using v-for to display all service items. Each item has a stationName, startDate, and endDate. I am using lodash to sortBy a specific key inside the array.

Can't see what I am doing wrong. Can someone help? I tried multiple things but can't make it work. I want to be able to sort by end date, start date, and station name.

<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';
import { orderBy } from 'lodash';

// Types
import type { Stationwork } from 'types/store/stationworks';

// Store
import { useStationStore } from 'store/stations';
import { useStationworksStore } from 'store/stationworks';

// Components
import BaseIcon from 'components/atoms/BaseIcon.vue';
import MultiSelect from 'components/molecules/MultiSelect.vue';
import StationWorksListItem from 'components/molecules/StationWorksListItem.vue';

// Composables
import { useComponent } from 'composable/component';

const stationStore = useStationStore();
const stationworksStore = useStationworksStore();

const module = 'v-station-works-list';

const selectedStations = ref<any>([]);
const selectedFields = ref<string[]>(['filterCurrent']);

const filterFieldOptions = ref([
    { id: 1, text: 'Filter zichtbaarheid in infobox', value: 'filterVisible' },
    { id: 2, text: 'Filter actuele werkzaamheden', value: 'filterCurrent' }
]);

const { childClass } = useComponent(module);

const handleSelectUpdate = (selected: string) => {
    selectedStations.value = selected;
};

const currentStationsOptions = computed(() => stationworksStore.currentStationsOptions);

const mapStation = (station: Stationwork) => ({
    uuid: station.uuid,
    stationCode: station.stationCode,
    stationName: stationStore.getStationName(station.stationCode),
    workSubject: station.workSubject,
    startDate: station.startDate,
    endDate: station.endDate,
    workContent: station.workContent,
    showWork: station.showWork
});

const currentStationWorks = computed(() => {
    const filterCurrent = selectedFields.value.includes('filterCurrent');
    const filterVisible = selectedFields.value.includes('filterVisible');

    const works = stationworksStore.works
        .filter((station: Stationwork) => {
            const isSelectedStation = selectedStations.value.includes(
                stationStore.getStationName(station.stationCode)
            );

            if (!isSelectedStation) {
                return false;
            }

            if (filterCurrent) {
                const startDate = station.startDate ? Date.parse(station.startDate) : false;
                const endDate = station.endDate ? Date.parse(station.endDate) : false;

                if (!startDate) {
                    return false;
                }

                const isBeforeToday = startDate <= Date.now();
                let isInFuture = false;

                if (endDate) {
                    isInFuture = endDate >= Date.now();
                }

                const isInCurrentRange =
                    startDate && endDate ? isBeforeToday && isInFuture : isBeforeToday;

                if (!isInCurrentRange) {
                    return false;
                }
            }

            if (filterVisible && !station.showWork) {
                return false;
            }

            return true;
        })
        .map(mapStation);

    return works;
});

const moduleClasses = computed(() => ({
    [module]: true
}));

enum SortCriteria {
    STATION = 'stationName',
    START_DATE = 'startDate',
    END_DATE = 'endDate'
}

const sortOrder = ref<'asc' | 'desc'>('asc');

const sortCriteria = ref<SortCriteria | string>('');

const sortedArray = computed(() => {
    return orderBy(currentStationWorks.value, [sortCriteria.value], [sortOrder.value]);
});

const sortBy = (criteria: SortCriteria) => {
    sortOrder.value = sortOrder.value === 'asc' ? 'desc' : 'asc';

    const sortedWorks = orderBy(currentStationWorks.value, [criteria], [sortOrder.value]);

    console.log(sortedWorks);

    return sortedWorks;
};

onMounted(async () => {
    selectedStations.value = stationworksStore.currentStationsOptions;

    if (!stationStore.stations.length) {
        await stationStore.fetchAll();

        selectedStations.value = stationworksStore.currentStationsOptions;
    }
});
</script>

<template>
    <div :class="moduleClasses">
        <div :class="childClass('container')">
            <div :class="childClass('content')">
                <h1 :class="childClass('title')">Stationswerkzaamheden</h1>
                <p :class="childClass('text')">
                    Mogelijkheid filteren op station, selecteer station(s)
                </p>
            </div>

            <div :class="childClass('holder')">
                <div :class="childClass('header')">
                    <MultiSelect
                        class="w-full max-w-2xl"
                        v-if="currentStationsOptions.length"
                        all_option_text="Alle stations"
                        num_text=" stations geselecteerd"
                        all_text="Alle stations geselecteerd"
                        placeholder="Selecteer station(s)"
                        :initial_value="currentStationsOptions"
                        :choices="currentStationsOptions"
                        :update_while_opened="true"
                        :options_show_full="1"
                        @select-update="handleSelectUpdate"
                    />

                    <BaseIcon
                        v-else
                        icon="loader"
                        class="animate-spin text-lg text-ns-light-blue"
                    />

                    <div class="ml-auto flex flex-col items-center gap-x-6 gap-y-2 lg:flex-row">
                        <div
                            class="flex items-center"
                            v-for="field in filterFieldOptions"
                            :key="field.id"
                        >
                            <input
                                :id="field.value"
                                type="checkbox"
                                class="h-5 w-5 shrink-0 border-ns-light-gray bg-gray-100 accent-ns-light-blue focus:ring-ns-blue"
                                :name="field.text"
                                v-model="selectedFields"
                                :value="field.value"
                            />
                            <label :for="field.value" class="ms-2 cursor-pointer text-sm">{{
                                field.text
                            }}</label>
                        </div>
                    </div>
                </div>

                <div :class="childClass('station-header')">
                    <span class="col-span-2" @click="sortBy(SortCriteria.STATION)">Station</span>
                    <span class="col-span-3 lg:col-span-4">Omschrijving</span>
                    <span class="col-span-2 lg:col-span-1" @click="sortBy(SortCriteria.START_DATE)"
                        >Startdatum</span
                    >
                    <span class="col-span-2 lg:col-span-1" @click="sortBy(SortCriteria.END_DATE)"
                        >Einddatum</span
                    >
                    <span class="col-span-3 lg:col-span-4">Uitgebreide omschrijving</span>
                </div>

                <div v-if="currentStationsOptions.length" :class="childClass('overview')">
                    <StationWorksListItem
                        v-for="station in sortedArray"
                        :key="station.uuid"
                        :station="station"
                    />
                </div>
            </div>
        </div>
    </div>
</template>

Solution

  • In your template, you're using sortedArray which always sorts by [SortCriteria.STATION]. Your sortBy function is indeed called when clicking what I assume is your column header, but its result is never used (except in your console.log), and so, is lost.
    Make a state for the sort criteria itself and use it in your computed value 'sortedArray'.