# 6.- Editando, Eliminando nuestro article
Para eso ya tenemos nuestra api Route::get('/articles', [ArticleController::class, 'index'])
y la utilizamos en el frontend para traer todos los articles
- En este caso solo los usuarios logeados pueden realizar un petición GET y para eso desde el front tenemos que enviar su token del usuario para capturar el user
import { useAuth } from '@/hooks/auth'
const [articles, setArticles] = useState()
console.log('articles', articles) // miramos los datos
// Para eso necesitamos el token que almacenamos en las cookies
const { getCookie } = useAuth()
if (typeof window !== 'undefined') {
var token = getCookie('token')
}
// Creamos función
async function getFilms() {
const response = await axios.get(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/articles`,
{
headers: {
// Enviando la cookie de usuario
"Authorization": `Bearer ${token}`,
},
})
const data = response.data
// Almacenamos los datos
setArticles(response.data.articles.data)
}
- Ya tenemos los datos en
setArticles
. - Recordar que aparte de los datos también tenemos la paginación.
// Mostrando los datos
<div>
{articles !== undefined ?
(<div>
{articles.map(article => (
<div key={article.id}>
<div>{article.title}</div>
</div>
))}
</div>) :
(<p> cargando datos</p>)}
</div>
# Editar un article y también funciona como detalle (Frontend)
*Para mostrar el detalle de un articulo tenemos que crear una carpeta nueva con el nombre 'article' y dentro crear un archivo '[id].js' en este caso editaremos un article y al mismo tiempo mostraremos solo los datos del article que queremos actualizar
En el archivo principal donde mostramos todos los articles debemos de agregar un Botón para que nos envíe a una nueva ruta donde solo mostraremos los datos de ese artículo seleccionado
Para eso importaremos useRouter
import { useRouter } from 'next/router'
const router = useRouter()
<div key={article?.id}>
<div>{article?.title}</div>
<button
onClick={e =>
router.push(
'/articles/[id]',
`/articles/${article?.id}`,
)}> Edit
</button>
</div>
- En el otro archivo creado ahora colocamos los siguientes datos
[id].js
Los datos ya traídos se encuentran en las props const ArticleDetail = ({ articles })
Tenemos una función donde traemos los datos de un solo article en getServerSideProps
y almacenamos en un const [article, setArticle] = useState(articles.article)
import { useRouter } from 'next/router'
import { useEffect, useState, useSyncExternalStore } from 'react'
import axios from 'axios'
import { useAuth } from '@/hooks/auth'
const ArticleDetail = ({ articles }) => {
const [article, setArticle] = useState(articles.article)
console.log(article)
return (
<div>
hello
</div>
)
}
export const getServerSideProps = async context => {
const { data: articles } = await axios.get(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/article/` + context.query.id,
)
return {
props: {
articles,
},
}
}
export default ArticleDetail
- Para editar un artículo ahora lo realizamos sin utilizar librerías.
En esta oportunidad Next ya nos creó componentes reutilizables como Input y Label
import Label from '@/components/Label'
import Input from '@/components/Input'
<Label htmlFor="title">title</Label>
<Input
id="title"
type="text"
placeholder="title"
//value={updateTitle}
//onChange={event => setUpdateTitle(event.target.value)}
/>
- Ahora creamos todos los parámetros ya sea input y const que necesitamos para todos los datos de un article
const [id, setId] = useState(articles.article.id)
const [title, setTitle] = useState(articles.article.title)
const [slug, setSlug] = useState(articles.article.slug)
const [body, setBody] = useState(articles.article.body)
const [image, setImage] = useState(articles.article.image)
- Ahora en los Input colocamos el value
value={updateTitle}
<Label htmlFor="title">title</Label>
<Input
id="title"
type="text"
placeholder="title"
value={title}
onChange={event => setTitle(event.target.value)}
/>
- Así se tendría que tener todos los inputs
<form>
<Label htmlFor="title">title</Label>
<Input
id="title"
type="text"
placeholder="title"
value={title}
onChange={event => setTitle(event.target.value)}
/>
<Label htmlFor="slug">slug</Label>
<Input
id="slug"
type="text"
placeholder="slug"
value={slug}
onChange={event => setSlug(event.target.value)}
/>
<Label htmlFor="body">body</Label>
<Input
id="body"
type="text"
placeholder="body"
value={body}
onChange={event => setBody(event.target.value)}
/>
<Label htmlFor="image">image</Label>
<Input
id="image"
type="file"
value={image}
onChange={event => setImage(event.target.value)}
/>
<img src={image}/>
<button >Update</button>
</form>
- Para la parte de la imagen tenemos que crear una función nuevamente para poder actualizar y mostrar en pantalla si ingresamos una imagen nueva al input
file
.
Así queda nuestro input para la imagen
<Label htmlFor="image">image</Label>
<Input
id="image"
type="file"
//Creamos una función nueva ya dentro de esta función guardaremos la imagen
onChange={ event => submitImage(event)}
/>
<img id="output"
src={image}
/>
Ya capturamos la imagen ahora tenemos que enviar los datos nuevos que queremos enviar para actualizar el dato
- Para enviar los datos utilizaremos
FormData()
así que al botón le agregamos la opción onClick
function submitImage(event) {
const files = event.target.files
const file = files[0]
getBase64(file)
//show image
var reader = new FileReader();
reader.onload = function () {
var output = document.getElementById('output');
output.src = reader.result;
};
reader.readAsDataURL(event.target.files[0]);
}
const onLoad = fileString => {
setImage(fileString)
}
const getBase64 = file => {
let reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => {
onLoad(reader.result)
}
}
Ya capturamos la imagen, ahora tenemos que enviar los datos nuevos que queremos enviar para actualizar el dato
- Para enviar los datos utilizaremos
FormData()
así que al botón le agregamos la opción onClick
<button onClick={updateArticle}>Update</button>
const updateFilm = async e => {
// Colocamos los datos a enviar
}
En nuestra función agregamos lo siguiente
//submitDataUpdate article
const updateArticle = async e => {
e.preventDefault()
let formData = new FormData()
formData.append('title', title)
formData.append('slug', slug)
formData.append('body', body)
formData.append('image', image)
await axios
.post(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/article/${id}/edit`,
formData,
)
.then(function (response) {
console.log(response, 'llega')
// router.push("/")
})
.catch(function (error) {
console.log(error)
})
}
# Editar un article (Backend)
En nuestro backend tenemos que crear un endpoint para actualizar nuestro article
Vamos a ArticleController.php
y buscamos la función public function update(){}
public function update(Request $request, $id)
{
$article = Article::find($id);
$article->title = $request->title;
$article->body = $request->body;
$article->slug = $request->slug;
$article->image = $request->image;
$article->save();
return response()->json(['message' => 'Article update succesfully']);
}
Ahora vamos al archivo api.php
y creamos la ruta
Route::post('/article/{id}/edit', [ArticleController::class, 'update']);
Y listo ya podemos actualizar nuestro article, recordar que faltan las validaciones como: Que el usuario con el mismo Id pueda actualizar y otras validaciones más para que sea más seguro.
# Eliminando un article (Frontend)
- Ahora vamos a eliminar un article
//Enviamos el id del article
const handleDelete = async (id) =>{
await axios
.post(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/article/${id}/delete`,
)
.then(function (response) {
console.log(response, 'llega')
})
.catch(function (error) {
console.log(error)
})
}
<button
//Enviamos el Id del article
onClick={() => handleDelete(article?.id)}
className="btn btn-danger">
Delete
</button>
# Eliminando un article (Backend)
- Ahora vamos a la parte del backend a crear nuestro endpoint para poder eliminar
Vamos al archivo api.php
y agregamos la nueva ruta de nuestro endpoint.
Route::post('/article/{id}/delete', [ArticleController::class, 'destroy']);
Ahora vamos al archivo ArticleController.php
y buscamos la función destroy
public function destroy($id)
{
$article = Article::find($id);
$article->delete();
return response()->json(['message' => 'Article delete succesfully']);
}
Listo así de fácil es eliminar un article
# Actualizando los datos de la lista sin refrescar página
Ahora actualizaremos los datos sin recargar la página y vaciamos los input, acordarse que estamos utilizando una librería externa para poder crear un article así que tendremos que realizar algunas configuraciones diferentes para que funcione correctamente.
Para reset los input tenemos que utilizar una función de la librería react-hooks-form llamara reset
y colocarla acá
const { handleSubmit, control, formState: { errors }, reset } = useForm(
{
defaultValues: {
title: '',
slug: '',
body: '',
image: '',
}
}
);
Además coloquemos los datos restantes que faltan como body y image para que funcione correctamente
- Al momento de crear un nuevo article en la función
onSubmit
tenemos que agregar unas líneas para actualizar la lista de articles sin refrezcar la página
async function onSubmit(data) {
data.image = imageselect
var { title, slug, body, image } = data
await axios.post(`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/article`, data,
{headers: { "Authorization": `Bearer ${token}` }})
.then(function (response) {
//Actualizando articles pero esto va con un nuevo useEffects
setArticles()
//Ocultando la imagen después de crear un article
var output = document.getElementById('output');
output.src = '';
//Limpiando valor del input imageselect para dejar vacía
setImage('')
//Limpiando los datos restantes
reset();
})
.catch(function (error) {
console.log(error)
})
};
Ya habiendo limpiado los inputs ahora nos toca actualizar la lista de articles
- Se agregó esta parte porque al momento de crear un article en la parte
const [articles, setArticles] = useState()
donde almacenamos la lista. Por consola podemos darnos cuenta de que nos llega undefined así que añadiendo un nuevo useEffect hacemos que la lista se actualice nuevamente.
useEffect(() => {
if(articles === undefined){
getArticles()
}}, [articles])
# Eliminar article sin refrescar página
- En este caso como arriba creamos un useEffect indicando que actualicé la lista si es que en algún momento le llegan undefined ya sea al crear o eliminar lo que realizara es refrezcar cada vez que se realice un evento de crear o eliminar
const handleDelete = async (id) =>{
await axios
.post(`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/article/${id}/delete`,)
.then(function (response) {
//Actualizando lista
setArticles()
})
.catch(function (error) {
console.log(error)
})
}