Adding recipes

Let's make a new route under pages/Recipes/new.vue; this will then generate a route to localhost:3000/recipes/new. Our implementation will be simple; for example, having recipe steps as string may not be the best idea for production, but it allows us to achieve our goal(s) in development.

We can then add the appropriate data object and validation(s) with Vuelidate:

import { required, minLength } from 'vuelidate/lib/validators'

export default {
data () {
return {
title: '',
image: '',
steps: '',
categoryId: 1
}
},
validations: {
title: {
required,
minLength: minLength(4)
},
image: {
required
},
steps: {
required,
minLength: minLength(30)
}
},
}

Next up, we can add the appropriate template, which includes everything from validation messages, to contextual classes, and enabling/disabling the submit button if the form is valid/invalid:

<template>
<form @submit.prevent="submitRecipe">
<div class="field">
<label class="label">Recipe Title</label>
<input class="input" :class="{ 'is-danger': $v.title.$error}" v-
model.trim="title" @input="$v.title.$touch()" type="text">
<p class="help is-danger" v-if="!$v.title.required &&
$v.title.$dirty">Title is required</p>
<p class="help is-danger" v-if="!$v.title.minLength &&
$v.title.$dirty">Title must be at least 4 characters.</p>
</div>

<div class="field">
<label class="label">Recipe Image URL</label>
<input class="input" :class="{ 'is-danger': $v.image.$error}" v-
model.trim="image" @input="$v.image.$touch()" type="text">
<p class="help is-danger" v-if="!$v.image.required &&
$v.image.$dirty">Image URL is required</p>
</div>

<div class="field">
<label class="label">Steps</label>
<textarea class="textarea" rows="5" :class="{ 'is-danger':
$v.steps.$error}" v-model="steps" @input="$v.steps.$touch()"
type="text">
</textarea>
<p class="help is-danger" v-if="!$v.steps.required &&
$v.steps.$dirty">Recipe steps are required.</p>
<p class="help is-danger" v-if="!$v.steps.minLength &&
$v.steps.$dirty">Steps must be at least 30 characters.</p>
</div>

<div class="field">
<label class="label">Category</label>
<div class="control">
<div class="select">
<select v-model="categoryId" @input="$v.categoryId.$touch()">
<option value="1">Dessert</option>
<option value="2">Healthy Eating</option>
</select>
</div>
</div>
</div>

<button :disabled="$v.$invalid" class="button is-
primary">Add</button>
</form>
</template>

To submit the recipe, we'll need to make a POST request to our API:

import axios from 'axios'

export default {
// Omitted
methods: {
submitRecipe () {
const recipe = { title: this.title, image: this.image, steps:
this.steps, categoryId: Number(this.categoryId) }
axios.post('http://localhost:3001/recipes', recipe)
}
},
}

Instead of navigating to the http://localhost:3000/recipes/new URL manually, let's add an item to our navigation bar:

<template>
<nav class="navbar is-primary" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<nuxt-link class="navbar-item" to="/">Hearty Home Cooking</nuxt-
link>
</div>
<div class="navbar-end">
<nuxt-link class="navbar-item" to="/recipes/new">+ Add New
Recipe</nuxt-
link>
</div>
</nav>
</template>

Here's what our page now looks like:

Although we haven't used the recipe steps in our application, I've included it in this example as a feature you may want to include yourself.