The Lunch Week Details Component
The Lunch Week Details Component
The next big feature we need to add is a Lunch Week Details adminstration component. This component allows admin users to view and edit the daily lunch details for a given week.
We already added a placeholder component called LunchMenuAdminDetails
that we render when a user clicks on a Lunch Week row from the Lunch Week list table. We'll be working with that component now.
Here's what the final product will look like:

Initializing the Component#
To get going, we can start with a loading flag and the onMount
lifecycle function. We'll fetch the lunchWeek
object for the given lunchWeekId
specified in the route and set it to a state variable called lunchWeek
.
import { onMount } from 'svelte'
import axios from 'axios'
import Icon from 'svelte-awesome'
import { refresh } from 'svelte-awesome/icons'
export let currentRoute
let routeLunchWeekId = currentRoute.namedParams.lunchWeekId
let lunchWeek = {} // the lunchWeek state variable
let loading = true
onMount(async () => {
try {
const response = await axios.get(`${process.env.API_ROOT}/api/lunch-week/${routeLunchWeekId}`)
lunchWeek = response.data // set the state
loading = false
} catch (e) {
console.error(e)
}
})
Then add the loading refresh
icon logic to the markup using an {#if}
block. For early testing purposes, we can stringify lunchWeek
and show it in the DOM. This will give us a way to watch the state while we are testing:
<div>
<nav class="breadcrumb" aria-label="breadcrumbs">
<ul>
<li>
<a href="/">Home</a>
</li>
<li>
<a href="/admin/manage-menus">Lunch Menu Administration</a>
</li>
<li class="is-active">
<a href="/#">{$user.schoolName}</a>
</li>
</ul>
</nav>
{#if loading}
<div class="section">
<Icon spin data="{refresh}" scale="3" />
</div>
{:else}
<section>{JSON.stringify(lunchWeek)}</section>
{/if}
</div>
If everything works, you should see something like this:

Nesting the Lunch Days List#
Currently our GET /lunch-week
endpoint only returns the main lunchWeek
object. Since our component needs to work with both the lunchWeek
and the child lunchDay
list, we can update the backend endpoint to nest the lunchDay
list in the response payload.
To get a lunch day list for a given week, we can create a getLunchDayList
helper function:
const getLunchDayList = (lunchWeekId) => {
return knex.select().from('lunch_day').where('lunch_week_id', lunchWeekId)
}
Change the backend GET /lunch-week
endpoint in lunch-week.js
to the following, using the new helper function to nest the lunch day list:
router.get('/:lunchWeekId', async function (req, res) {
try {
const id = parseInt(req.params.lunchWeekId)
const lunchWeek = await getLunchWeekById(id)
if (lunchWeek) {
let lunchDays = await getLunchDayList(id) // fetch the lunch days list
lunchWeek.lunchDays = lunchDays // set lunchDays as a property on the lunchWeek object
res.send(lunchWeek)
} else {
const message = `Lunch Week Id ${req.params.lunchWeekId} not found`
res.status(404).send({
message: message,
})
}
} catch (e) {
const message = `Error getting Lunch Week Id ${req.params.lunchWeekId}`
res.status(500).send({
message: message,
error: e.toString(),
})
}
})
Now if we test the front end again, we should see a lunchDays
list in the JSON response:
{
"lunchWeekId": 2,
"weekOf": "2020-10-12",
"isPublished": true,
"lunchDays": [] // HERE!
}
Seeding the Lunch Days List#
In this section, we are going to do some date parsing, formatting and manipulation. Install the package date-fns
in the frontend project. This makes working with dates easier.
$ npm install date-fns
In the earlier screenshot of the week-details page, we saw five text areas - one for each school day of the given week. We will manage the creation and binding of these text areas by seeding and then iterating the lunchDays
list.
We need to handle two different scenarios:
A
lunchDay
entity does not exist for the given day that we want to seedA
lunchDay
entity already exists for the day we want to seed
Here's a seedLunchDay
function that populates a list of five objects on the lunchDayList
property, where they don't already exist for a given day:
import { add, parseISO, format } from 'date-fns'
const lunchWeek = {
lunchDays: []
}
const seedLunchDays = () => {
// first, we need to turn the ISO formatted lunchWeek.weekOf
// date into a date object
// the parseISO function comes from date-fns
const weekOfDate = parseISO(lunchWeek.weekOf)
for (let i = 0; i < 5; i++) {
// then we loop 5 times, starting with i = 0
// and adding 1 day each time, e.g., monday, then tuesday, etc
const calculatedDay = add(weekOfDate, { days: i })
const formattedDay = format(calculatedDay, 'yyyy-MM-dd')
// if our lunchWeek.lunchDays list already has
// that day from the server, continue to the next iteration
if (lunchWeek.lunchDays.some(x => x.day === formattedDay)) {
continue
}
// otherwise, create a new child lunch day object
// and splice it into the list at the correct position
const lunchDay = {
day: formattedDay,
lunchWeekId: lunchWeek.lunchWeekId,
menuDetails: null,
}
lunchWeek.lunchDays.splice(i, 0, lunchDay)
}
}
This page is a preview of Fullstack Svelte