DEV Community

Duy Anh Ngac
Duy Anh Ngac

Posted on • Originally published at cornerofprogress.com

Log[4] - Giary - Vuex getter with a parameter

TL;DR:

Note: a dummy state is provided for visualisation purposes

Despite a busy past week, I had very little time to work on the Giary project, yet I was still determined that at least one feature must be implemented, even if it's the simplest one.

Weekly task structure

I decided the simple feature will be retrieving and displaying weekly tasks of a current week. Like with most data, I start with defining some structure.

src/types/Task.interface.ts

export enum TaskStatus {
  InProgress = "In Progress",
  Completed = "Completed",
  Deleted = "Deleted",
  Rescheduled = "Rescheduled",
}

export default interface Task {
  id?: string;
  title: string;
  status: TaskStatus;
  dueDate: string;
  rescheduledFrom: string;
  rescheduledTo: string;
}
Enter fullscreen mode Exit fullscreen mode

Besides the basic information, I have also included fields rescheduledFrom and rescheduledFrom which will be utilized when a task is, you guessed it, rescheduled. I am also using an enum to make sure all of the tasks will have consistent status values.

Weekly Task state

Before moving on any further, it is a good idea to create some dummy data for better visualization.

src/store/index.ts

export const store = createStore<State>({
  state: {
    goalList: [
      {
        id: "fdde9397-337c-4a75-9641-968c37374a32",
        title: "Hello world",
        dueDate: "31/12/2021",
      },
    ],
    weeklyTasks: [
      {
        goalId: "fdde9397-337c-4a75-9641-968c37374a32",
        tasks: [
          {
            id: "fdde9397-667c-4a75-9641-9685jg373ff3",
            title: "In progress weekly Task 1",
            status: TaskStatus.InProgress,
            dueDate: moment().format("DD/MM/YYYY").toString(),
            rescheduledFrom: "",
            rescheduledTo: "",
          },
        ],
      },
    ],
  },
});
Enter fullscreen mode Exit fullscreen mode

I could have nested weekly tasks inside a goal object, but I don't like a deeply nested object, they make everything harder to keep track of.

Getter with a parameter

As it stands now, the component will have to retrieve a whole weeklyTask array and then retrieve it. This might cause duplication of code, but more importantly, there is no guarantee that the data will be consistent.

And that is exactly what Vuex was designed, and getters in particular. They are what is needed to compute derived state based on store state, for example filtering through a list of items and counting them.

computed: {
  doneTodosCount () {
    return this.$store.state.todos.filter(todo => todo.done).length
  }
}
Enter fullscreen mode Exit fullscreen mode

Based on, the documentation, a getter only receive state as a 1st argument and getters as a 2nd argument. Based on the requirements, I would like to filter a weeklyTask array based on the goalId. In order to do so, a Method-Style Access must be used, which means passing arguments to getters by returning a function

src/store/index.ts

export const store = createStore<State>({
  // ..state
  getters: {
    getWeeklyTasksByGoalId(state: State) {
      return (goalId: string) =>
        state.weeklyTasks.filter((week) => week.goalId == goalId)[0];
    },
  },
  // ... mutations
});
Enter fullscreen mode Exit fullscreen mode

Based on the structure, there can only be one object for one goalId so I can safely return data from the first index of a filtered array.

Using getters in components

In composition api, in order to access getters, a computed variable must be created references to retain reactivity.

src/views/WeeklyPlan.vue

<script lang="ts">
// imports

export default defineComponent({
  setup() {
    const route = useRoute();
    const store = useStore();

    const isPast = ref(false);

    const goalWeeklyTasks = computed(() => store.getters.getWeeklyTasksByGoalId(
      route.params.goalId
    ));

    const weeklyTasksByDueDate = computed(() =>
      goalWeeklyTasks.tasks.filter(
        (task: Task) =>
          task.dueDate === moment().format("DD/MM/YYYY").toString()
      )
    );

    return {
      weeklyTasksByDueDate,
      isPast,
    };
  },
});
</script>
Enter fullscreen mode Exit fullscreen mode

Goal's weekly tasks are retrieved, it can be further filtered down based on the current date with another computed property. Nothing too fancy here.

Displaying weekly tasks

Firsly, a simple update to the AppTaskListItem.vue component, where it will recieve a task object as a prop.

src/components/AppTaskListItem.vue

<template>
  <li class="p-10 border border-gray-200 rounded-md text-left">
    <div
      v-if="state === 'active' || state === 'underReview'"
      class="flex items-center justify-end mb-5"
    >
      <!-- rest of the component -->

      <p class="text-lg font-light">
        {{ task.title }}
      </p>
    </div>
  </li>
</template>

<script lang="ts">
import Task from "@/types/Task.interface";
import { defineComponent, PropType } from "vue";

export default defineComponent({
  props: {
    task: {
      type: Object as PropType<Task>,
      required: true,
    },
  },
});
</script>
Enter fullscreen mode Exit fullscreen mode

And, secondly, clean up a WeeklyPlan.vue template and pass a task data via newly added props.

src/views/WeeklyPlan.vue

<template>
  <BaseLayout>
    <!-- rest of the component -->

    <ul class="mt-5 space-y-8">
      <AppTaskListItem
        v-for="task in weeklyTasksByDueDate"
        :key="task.id"
        :task="task"
      />
    </ul>

    <!-- rest of the component -->
  </BaseLayout>
</template>
Enter fullscreen mode Exit fullscreen mode

Conclusion

There wasn't much done during the past week and I would like to change a few things here and there. However, a little bit of progress is always better than no progress. Also, a lesson learned on how to create a Vuex getter with parameters.

Like this? Buy me a coffee

Discussion (0)