Terakhir diperbaharui: Jul 1, 2021
Membuat ulang Aplikasi DinoTes #2
Setelah berhasil membuat fungsi untuk menambah note baru, pada bagian ini kita akan melanjutkan menambahkan beberapa fungsi lain yaitu:
- Menampilkan semua note pada halaman utama
- Mengedit note
6. Note List Page
Step by Step
Buat sebuah file baru bernama NotesList.js di dalam folder components.
Component NotesList ini akan menjadi component yang berfungsi untuk menampilkan semua note yang sudah ditambahkan.
Tambahkan code berikut ini:
components/NotesList
1import React, { useEffect, useState } from "react";2import { API } from "@aws-amplify/api";34import { listNotes } from "../graphql/queries";56const NotesList = () => {7 const [notes, setNotes] = useState("");89 useEffect(() => {10 async function getNotes() {11 const { data } = await API.graphql({12 authMode: "API_KEY",13 query: listNotes,14 });15 setNotes(data.listNotes.items);16 }1718 getNotes();19 }, []);2021 return (22 <div className="flex flex-col items-center justify-center m-4">23 <div className="grid grid-cols-1 md:grid-cols-3 gap-4 my-8 overflow-y-auto">24 {notes &&25 notes.map((note) => {26 return (27 <div className="text-left p-4 border rounded-md" key={note.id}>28 <h4 className="text-lg font-semibold text-purple-900">29 {note.title}30 </h4>31 <p>{note.content.slice(0, 101)}</p>32 </div>33 );34 })}35 </div>36 </div>37 );38};3940export default NotesList;
Import NotesList ke file index.js
pages/index.js
1import Layout from "../components/Layout";2import NotesList from "../components/NotesList";34const Home = () => (5 <div className="text-center">6 <Layout>7 <NotesList />8 </Layout>9 </div>10);1112export default Home;
Sekarang jika kita akses halaman utama (localhost:3000) kita akan dapatkan data note yang sudah kita submit sebelumnya.
7. Edit Page
Agar note bisa diedit maka kita perlu lakukan langkah-langkah berikut.
Step by Step
Buat sebuah file baru bernama EditNoteForm.js di dalam folder components.
Dan tambahkan code berikut ini:
1import React, { useState } from "react";2import { API } from "@aws-amplify/api";3import { useRouter } from "next/router";4import { deleteNote, updateNote } from "../graphql/mutations";5import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";6import { faSave, faTrashAlt } from "@fortawesome/free-solid-svg-icons";7import { useFormik } from "formik";89import Message from "./Message";1011const InfoWrapper = (props) => {12 const { status } = props;1314 if (status !== null) {15 if (status === false) {16 return <Message type="error" text="Title cannot be empty" />;17 }18 return <Message type="success" text="Data successfully saved" />;19 }20 return <></>;21};2223const EditNoteForm = (props) => {24 const [isSuccess, setIsSuccess] = useState(null);25 const router = useRouter();2627 const { note } = props;2829 const formik = useFormik({30 initialValues: {31 title: note.title,32 content: note.content,33 },34 onSubmit: async (values) => {35 await API.graphql({36 authMode: "API_KEY",37 query: updateNote,38 variables: {39 input: { id: note.id, title: values.title, content: values.content },40 },41 });4243 setIsSuccess(true);44 },45 });4647 const handleDelete = async () => {48 await API.graphql({49 authMode: "API_KEY",50 query: deleteNote,51 variables: {52 input: { id: note.id },53 },54 });5556 router.push("/");57 };5859 return (60 <>61 <InfoWrapper status={isSuccess} />62 <form className="flex flex-col w-4/5" onSubmit={formik.handleSubmit}>63 <div className="flex flex-col">64 <input65 className="my-8 p-2 text-xl font-bold w-full focus:outline-none focus:ring focus:border-blue-300"66 type="text"67 name="title"68 placeholder="Title"69 onChange={formik.handleChange}70 value={formik.values.title}71 ></input>72 <textarea73 className="resize-y mb-8 p-2 focus:outline-none focus:ring focus:border-blue-300"74 name="content"75 rows="12"76 placeholder="Your content goes here.."77 onChange={formik.handleChange}78 value={formik.values.content}79 ></textarea>80 </div>81 <div className="flex justify-end">82 <button83 className="bg-purple-700 text-white text-base m-2 p-3 border rounded-md"84 type="submit"85 >86 <FontAwesomeIcon icon={faSave} /> Save87 </button>88 <button89 className="bg-red-500 text-white text-base m-2 p-3 border rounded-md"90 onClick={handleDelete}91 >92 <FontAwesomeIcon icon={faTrashAlt} /> Delete93 </button>94 </div>95 </form>96 </>97 );98};99100export default EditNoteForm;
Cara user mengedit notes tidak berubah, jika user ingin mengedit sebuah note maka user harus klik note tersebut di halaman utama.
Selanjutnya user akan diarahkan ke halaman edit, dimana aplikasi akan melakukan render component EditNoteForm dengan data sesuai id dari note yang bisa didapatkan dari alamat url.
Konsep yang digunakan untuk membuat fungsi edit ini adalah Dynamic Routes.
Pada Next.js, menerapkan konsep ini bisa dibilang sangat mudah. Penjelasan lengkap bisa dilihat kembali pada pembahasan Next.js.
Dynamic Routes
Buat sebuah folder dengan nama edit di dalam folder pages, kemudian buat sebuah file dengan nama [id].js.
Tambahkan code berikut ini di dalam file [id].js
1import React, { useState, useEffect } from "react";2import { useRouter } from "next/router";3import Link from "next/link";4import { API } from "@aws-amplify/api";5import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";6import { faArrowLeft } from "@fortawesome/free-solid-svg-icons";7import { getNote } from "../../graphql/queries";89import Layout from "../../components/Layout";10import EditNoteForm from "../../components/EditNoteForm";1112const EditPage = () => {13 const [note, setNote] = useState(null);14 const router = useRouter();1516 const { id } = router.query;1718 useEffect(() => {19 async function getSingleNote() {20 const { data } = await API.graphql({21 authMode: "API_KEY",22 query: getNote,23 variables: { id: id },24 });2526 setNote(data.getNote);27 }2829 getSingleNote();30 }, []);3132 if (!note) return null;3334 return (35 <Layout>36 <div className="flex flex-col items-center justify-center m-4">37 <div className="flex w-full">38 <div className="text-lg font-semibold text-blue-500">39 <FontAwesomeIcon icon={faArrowLeft} /> {" "}40 <Link href="/">Back</Link>41 </div>42 </div>43 <EditNoteForm note={note} />44 </div>45 </Layout>46 );47};4849export default EditPage;
Yang perlu diperhatikan disini adalah kita mendapatkan id dari url menggunakan useRouter:
1...2import { useRouter } from "next/router";3...4 const { id } = router.query;5...
Hal ini berbeda dengan langkah membuat Dynamic Routes menggunakan getStaticPaths() dan getStaticProps(), ini dilakukan karena AWS Amplify (bagian deployment) belum sepenuhnya support semua teknik prerendering Next.js.
Dan langkah-langkah diatas bisa saja berubah dan akan diperbaharui setelah AWS Amplify support semua teknik prerendering termasuk teknik membuat dynamic routes menggunakan getStaticPaths() dan getStaticProps().
Langkah selanjutnya adalah mengubah title pada NotesList menjadi link yang mengarah ke halaman untuk mengedit.
components/NotesList.js
1import React, { useEffect, useState } from "react";2import { API } from "@aws-amplify/api";3import Link from "next/link";45import { listNotes } from "../graphql/queries";67const NotesList = () => {8 ...9 return (10 <div className="flex flex-col items-center justify-center m-4">11 <div className="grid grid-cols-1 md:grid-cols-3 gap-4 my-8 overflow-y-auto">12 {notes &&13 notes.map((note) => {14 return (15 <div className="text-left p-4 border rounded-md" key={note.id}>16 <h4 className="text-lg font-semibold text-purple-900">17 <Link href={`/edit/${note.id}`}>{note.title}</Link>18 </h4>19 <p>{note.content.slice(0, 101)}</p>20 </div>21 );22 })}23 </div>24 </div>25 );26};2728export default NotesList;
Hasil Akhir:
8. Navigasi antar Halaman
Setelah 3 halaman utama (Home, Add, Edit) dari aplikasi DinoTes sudah dibuat, yang perlu dilakukan selanjutnya adalah melengkapi navigasi antar halaman.
Antara halaman depan (Homepage) dengan halaman edit sudah tersambung melalui link pada title, sedangkan Homepage dan halaman Add belum tersambung. Untuk itu kita akan tambahkan sebuah button pada Homepage sebagai penghubung.
Update file Header.js menjadi seperti ini:
components/Header.js
1import React from "react";2import Link from "next/link";3import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";4import { faFile } from "@fortawesome/free-solid-svg-icons";56const Header = () => {7 return (8 <>9 <div className="flex justify-between items-center border-b-2 border-gray-100 py-6 md:justify-start md:space-x-3">10 <img11 className="h-14 w-auto sm:h-16"12 src="/header-logo.png"13 alt="logo"14 />15 <div className="md:flex items-center justify-end md:flex-1 lg:w-0">16 <Link href="/add">17 <button className="bg-purple-700 text-white text-base m-2 p-3 border rounded-md">18 <FontAwesomeIcon icon={faFile} />19 New Note20 </button>21 </Link>22 </div>23 </div>24 </>25 );26};2728export default Header;