This content originally appeared on flaviocopes.com and was authored by flaviocopes.com
This post is part of a new series where we build a clone of Airbnb with Next.js. See the first post here.
In this lesson I want to add the ability to add a new house.
In the previous lesson I added a link to the /host/new
URL, when we don’t have houses yet.
I am going to also add the link to the components/Header.js
file, to make it easy to find, even though in a real app you’d move it somewhere less prominent, I think, as it’s not a functionality that’s used many times:
components/Header.js
<li>
<Link href='/host/new'>
<a>Add House</a>
</Link>
</li>
Now create a new file pages/host/new.js
In this file we’re going to create a form to add a new house.
We start, by adding one house field, title
, and we we submit the form to the server, on the host/new
server-side route:
import { useState } from 'react'
import Head from 'next/head'
import Layout from '../../components/Layout'
const NewHouse = () => {
const [title, setTitle] = useState('')
return (
<Layout
content={
<div>
<Head>
<title>Add a new house</title>
</Head>
<form
onSubmit={async (event) => {
event.preventDefault()
try {
const response = await axios.post('/api/host/new', {
house: {
title,
},
})
if (response.data.status === 'error') {
alert(response.data.message)
return
}
console.log(response)
goto('/host')
} catch (error) {
alert(error.response.data.message)
return
}
}}
>
<input
id='title'
type='text'
placeholder='House title'
onChange={(event) => setTitle(event.target.value)}
/>
<button>Add house</button>
</form>
<style jsx>{``}</style>
</div>
}
/>
)
}
export default NewHouse
We use hooks to store the state of each item in our form.
Now create a new endpoint POST /api/host/new
in the server.js
file, that uses the Sequelize model to add the house to the database:
server.post('/api/host/new', async (req, res) => {
const houseData = req.body.house
if (!req.session.passport) {
res.writeHead(403, {
'Content-Type': 'application/json',
})
res.end(
JSON.stringify({
status: 'error',
message: 'Unauthorized',
})
)
return
}
const userEmail = req.session.passport.user
User.findOne({ where: { email: userEmail } }).then((user) => {
houseData.host = user.id
House.create(houseData).then(() => {
res.writeHead(200, {
'Content-Type': 'application/json',
})
res.end(JSON.stringify({ status: 'success', message: 'ok' }))
})
})
})
I basically check if the user is logged in, and then add its id
to the house data, before storing it.
If you try it now, in the terminal you’ll get a long list of errors from Sequelize:
Unhandled rejection SequelizeValidationError: notNull Violation: house.picture cannot be null,
notNull Violation: house.type cannot be null,
notNull Violation: house.town cannot be null,
notNull Violation: house.price cannot be null,
notNull Violation: house.superhost cannot be null,
notNull Violation: house.guests cannot be null,
notNull Violation: house.bedrooms cannot be null,
notNull Violation: house.beds cannot be null,
notNull Violation: house.baths cannot be null,
notNull Violation: house.wifi cannot be null,
notNull Violation: house.kitchen cannot be null,
notNull Violation: house.heating cannot be null,
notNull Violation: house.freeParking cannot be null,
notNull Violation: house.entirePlace cannot be null
//...
because we set those fields to be NOT NULL in the database.
Now that we have a basic form set up, let’s add all the fields we need to avoid this error!
import { useState } from 'react'
import Head from 'next/head'
import axios from 'axios'
import Router from 'next/router'
import Layout from '../../components/Layout'
const NewHouse = () => {
const [title, setTitle] = useState('')
const [town, setTown] = useState('')
const [price, setPrice] = useState(0)
const [picture, setPicture] = useState('')
const [description, setDescription] = useState('')
const [guests, setGuests] = useState(0)
const [bedrooms, setBedrooms] = useState(0)
const [beds, setBeds] = useState(0)
const [baths, setBaths] = useState(0)
const [wifi, setWifi] = useState(false)
const [kitchen, setKitchen] = useState(false)
const [heating, setHeating] = useState(false)
const [freeParking, setFreeParking] = useState(false)
const [entirePlace, setEntirePlace] = useState(false)
const [type, setType] = useState('Entire house')
const houseTypes = ['Entire house', 'Room']
return (
<Layout
content={
<div>
<Head>
<title>Add a new house</title>
</Head>
<form
onSubmit={async (event) => {
event.preventDefault()
try {
const response = await axios.post('/api/host/new', {
house: {
title,
town,
price,
picture,
description,
guests,
bedrooms,
beds,
baths,
wifi,
kitchen,
heating,
freeParking,
entirePlace,
type,
},
})
if (response.data.status === 'error') {
alert(response.data.message)
return
}
Router.push('/host')
} catch (error) {
alert(error.response.data.message)
return
}
}}
>
<p>
<label>House title</label>
<input
required
onChange={(event) => setTitle(event.target.value)}
type='text'
placeholder='House title'
/>
</p>
<p>
<label>Town</label>
<input
required
onChange={(event) => setTown(event.target.value)}
type='text'
placeholder='Town'
/>
</p>
<p>
<label>Price per night</label>
<input
required
onChange={(event) => setPrice(event.target.value)}
type='number'
placeholder='Price per night'
value={price}
/>
</p>
<p>
<label>House picture URL</label>
<input
required
onChange={(event) => setPicture(event.target.value)}
type='text'
placeholder='House picture URL'
/>
</p>
<p>
<label>House description</label>
<textarea
required
onChange={(event) => setDescription(event.target.value)}
></textarea>
</p>
<div>
<div>
<p>
<label>Number of guests</label>
<input
required
onChange={(event) => setGuests(event.target.value)}
type='number'
placeholder='Number of guests'
value={guests}
/>
</p>
<p>
<label>Number of bedrooms</label>
<input
required
onChange={(event) => setBedrooms(event.target.value)}
type='number'
placeholder='Number of bedrooms'
value={bedrooms}
/>
</p>
<p>
<label>Number of beds</label>
<input
required
onChange={(event) => setBeds(event.target.value)}
type='number'
placeholder='Number of beds'
value={beds}
/>
</p>
<p>
<label>Number of baths</label>
<input
required
onChange={(event) => setBaths(event.target.value)}
type='number'
placeholder='Number of baths'
value={baths}
/>
</p>
</div>
<div>
<p>
<label>Does it have Wifi?</label>
<select
onChange={(event) => setWifi(event.target.value)}
value={wifi}
>
<option value='true'>Yes</option>
<option value='false'>No</option>
</select>
</p>
<p>
<label>Does it have a kitchen?</label>
<select
onChange={(event) => setKitchen(event.target.value)}
value={kitchen}
>
<option value='true'>Yes</option>
<option value='false'>No</option>
</select>
</p>
<p>
<label>Does it have heating?</label>
<select
onChange={(event) => setHeating(event.target.value)}
value={heating}
>
<option value='true'>Yes</option>
<option value='false'>No</option>
</select>
</p>
<p>
<label>Does it have free parking?</label>
<select
onChange={(event) => setFreeParking(event.target.value)}
value={freeParking}
>
<option value='true'>Yes</option>
<option value='false'>No</option>
</select>
</p>
<p>
<label>Is it the entire place?</label>
<select
onChange={(event) => setEntirePlace(event.target.value)}
value={entirePlace}
>
<option value='true'>Yes</option>
<option value='false'>No</option>
</select>
</p>
<p>
<label>Type of house</label>
<select
onChange={(event) => setType(event.target.value)}
value={type}
>
{houseTypes.map((item, key) => (
<option value={item} key={key}>
{item}
</option>
))}
</select>
</p>
</div>
</div>
<button>Add house</button>
</form>
<style jsx>{`
input[type='number'],
select,
textarea {
display: block;
padding: 20px;
font-size: 20px !important;
width: 100%;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
margin-bottom: 10px;
}
`}</style>
</div>
}
/>
)
}
export default NewHouse
I imported the Next.js Router:
import Router from 'next/router'
so that after the form is successfully submitted, we can call Router.push('/host')
to redirect the user to the list of houses.
I used the houseTypes
array in one select to show you how to dynamically add data to a form.
The house picture is a path, either an absolute URL or relative to the base path.
I also set all the fields as required
, frontend-side, so we get automatic validation by the browser.
Great! The form should show up nicely now.
This content originally appeared on flaviocopes.com and was authored by flaviocopes.com

flaviocopes.com | Sciencx (2022-01-06T05:00:00+00:00) Airbnb clone, adding a new house. Retrieved from https://www.scien.cx/2022/01/06/airbnb-clone-adding-a-new-house/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.