cs4140 Notes: 07 More NextJS and Rails
·2 mins
Continuing from Last Time
Now let’s do the list page.
// app/jokes/page.js
import Link from 'next/link';
import { list_jokes } from '@/lib/jokes';
export default async function Jokes() {
let jokes = await list_jokes();
let joke_rows = jokes.map((joke) => (
<tr key={joke.id}>
<td>{joke.content}</td>
</tr>
));
return (
<main className="text-center min-h-screen">
<h1 className="text-2xl py-8">List Jokes</h1>
<p><Link className="text-sky-700 underline" href="/joke/new">New Joke</Link></p>
<table className="table table-striped">
<tbody>
{joke_rows}
</tbody>
</table>
</main>
);
}
We can’t see anything yet, so let’s do the new joke page.
// app/joke/new/page.js
export default async function NewJoke() {
return (
<main className="text-center min-h-screen">
<h1 className="text-2xl py-8">New Joke</h1>
<form action="/joke" method="post">
<label htmlFor="content" className="my-2">
Content <br/>
<textarea rows="4" cols="40" name="content"></textarea>
</label>
<div className="my-2">
<button className="bg-blue-600 text-white py-2 px-4 rounded"
type="Submit">
Save
</button>
</div>
</form>
</main>
);
}
Now we can post a form to “/joke”, so let’s write the handler for that.
// app/joke/route.js
import { NextResponse } from 'next/server';
import { create_joke } from '@/lib/jokes';
export async function POST(request) {
let data = await request.formData();
let joke = Object.assign({}, {content: data.get('content')});
console.log("create_joke", joke);
let joke1 = await create_joke(joke);
console.log("created", joke1);
let resp = new Response("redirect", {
status: 303,
headers: {
"Location": "/jokes",
}
});
return resp;
}
Let’s show one joke:
// app/joke/[id]/page.js
import Link from 'next/link';
import { get_joke } from '@/lib/jokes';
export default async function ShowJoke({params}) {
let {id} = params;
let joke = await get_joke(parseInt(id));
return (
<main className="text-center min-h-screen">
<h1 className="text-2xl py-8">Joke #{id}</h1>
<p>{joke.content}</p>
<p><Link className="text-sky-700 underline" href="/jokes">Back</Link></p>
</main>
);
}
And delete a joke
In jokes/page.js:
// We can't do client-side stuff in default components, so...
import DeleteJoke from '@/components/delete_joke.js';
// And, in the table row
<td>{joke.content}</td>
<td>
<DeleteJoke joke_id={joke.id} />
</td>
And the new client-side component:
// components/delete_joke.js
'use client';
export default function DeleteJoke({joke_id}) {
function click_delete(ev) {
ev.preventDefault();
if (confirm("Really delete?")) {
send_delete_joke(joke_id).then(() => {
console.log("Deleted joke #" + joke_id);
});
}
}
return (
<button onClick={click_delete}
className="bg-red-600 text-white py-2 px-4 rounded">
Delete
</button>
);
}
async function send_delete_joke(id) {
let resp = await fetch('/joke', {
method: 'DELETE',
mode: 'same-origin',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({id}),
});
let _body = await resp.json();
window.location.reload();
}
And on the server:
// in app/joke/route.js
import { create_joke, delete_joke } from '@/lib/jokes';
// add a delete handler
export async function DELETE(request) {
let {id} = await request.json();
await delete_joke(id);
return NextResponse.json({deleted: id});
}