Now that we have everything in the right place we are all set to handle the task of building api to update a task.
We will start by adding the update
action in TasksController
.
Let's open /app/controllers/tasks_controller.rb
and change the code as shown below.
1class TasksController < ApplicationController
2 before_action :load_task, only: %i[show update]
3
4 ...
5
6 def update
7 if @task.update(task_params)
8 render status: :ok, json: { notice: 'Successfully updated task.' }
9 else
10 render status: :unprocessable_entity, json: { errors: @task.errors.full_messages }
11 end
12 end
13
14 private
15
16 ...
We have used the update
method to save the updated values.
If the update is successful, we will return status ok with a notice. And on failure, we will return status unprocessable_entity along with the error messages.
Now, update the tasks resources in config/routes.rb
file:
1resources :tasks, except: %i[new edit]
Let's now create a new component for updating task details. To do so, like before, we will abstract the API
logic and form
logic to different components.
First, let's add an API
route to edit tasks inside app/javascript/src/apis/tasks.js
To do so, add the following lines to tasks.js
1import axios from "axios";
2
3const list = () => axios.get('/tasks');
4
5const show = slug => axios.get(`/tasks/${slug}`);
6
7const create = payload => axios.post('/tasks/', payload);
8
9const update = ({ slug, payload }) => axios.put(`/tasks/${slug}`, payload);
10
11const tasksApi = {
12 list,
13 show,
14 create,
15 update,
16};
17
18export default tasksApi;
Now, let's create our react components to update task details. To do so, first run the following command:
1touch app/javascript/src/components/Tasks/EditTask.jsx
Inside EditTask.jsx
, add the following content:
1import React, { useState, useEffect } from "react";
2import { useParams } from "react-router-dom";
3
4import Container from "components/Container";
5import TaskForm from "./Form/TaskForm";
6import tasksApi from "apis/tasks";
7import PageLoader from "components/PageLoader";
8import Toastr from "components/Common/Toastr";
9
10const EditTask = ({ history }) => {
11 const [title, setTitle] = useState("");
12 const [userId, setUserId] = useState("");
13 const [loading, setLoading] = useState(false);
14 const [pageLoading, setPageLoading] = useState(true);
15 const { slug } = useParams();
16
17 const handleSubmit = async event => {
18 event.preventDefault();
19 try {
20 await tasksApi.update({ slug, payload: { task: { title, user_id: userId } } });
21 setLoading(false);
22 Toastr.success("Successfully updated task.")
23 history.push("/dashboard");
24 } catch (error) {
25 setLoading(false);
26 logger.error(error);
27 }
28 };
29
30 const fetchTaskDetails = async () => {
31 try {
32 const response = await tasksApi.show(slug);
33 setTitle(response.data.task.title);
34 setUserId(response.data.task.user_id);
35 } catch (error) {
36 logger.error(error);
37 } finally {
38 setPageLoading(false);
39 }
40 };
41
42 useEffect(() => {
43 fetchTaskDetails();
44 }, []);
45
46 if (pageLoading) {
47 return (
48 <div className="w-screen h-screen">
49 <PageLoader />
50 </div>
51 );
52 }
53
54 return (
55 <Container>
56 <TaskForm type="update" title={title} userId={userId} setTitle={setTitle} setUserId={setUserId} loading={loading} handleSubmit={handleSubmit} />
57 </Container>
58 );
59};
60
61export default EditTask;
TaskForm
is the reusable Form
UI that we had created while working on creating a task
. Here, fetchTaskDetails
function is used to pre-populate the input
field with the existing title of the task.
Now, we need to create a route inside of our App.jsx
. To do so, open App.jsx
and add the following lines:
1import React, { useEffect, useState } from "react";
2import CreateTask from "components/Tasks/CreateTask";
3import EditTask from "components/Tasks/EditTask";
4import Dashboard from "components/Dashboard";
5
6import { Route, Switch, BrowserRouter as Router } from "react-router-dom";
7const App = () => {
8 return (
9 <Router>
10 <Switch>
11 <Route exact path="/tasks/:slug/edit" component={EditTask} />
12 <Route exact path="/tasks/create" component={CreateTask} />
13 <Route exact path="/dashboard" component={Dashboard} />
14 </Switch>
15 </Router>
16 );
17};
18
19export default App;
20
Once we have added a route in react router dom to render our component, we need to use the update API in our previously created reusable Table
component.
1import React from "react";
2import PropTypes from "prop-types";
3
4const TableRow = ({ data, destroyTask, updateTask }) => {
5 return (
6 <tbody className="bg-white divide-y divide-gray-200">
7 {
8 data.map(rowData => (
9 <tr key={rowData.title}>
10 <td className="px-6 py-4 text-sm font-medium leading-5 text-gray-900 whitespace-no-wrap">
11 {rowData.title}
12 </td>
13 <td className="px-6 py-4 text-sm font-medium leading-5 text-gray-900 whitespace-no-wrap">
14 {rowData.user_id}
15 </td>
16 <td className="px-6 py-4 text-sm font-medium leading-5 text-right cursor-pointer">
17 <a className="text-indigo-600 hover:text-indigo-900" onClick={() => updateTask(rowData.id)}>Edit</a>
18 </td>
19 <td className="px-6 py-4 text-sm font-medium leading-5 text-right cursor-pointer">
20 <a className="text-red-500 hover:text-red-700">Delete</a>
21 </td>
22 </tr>
23 ))
24 }
25 </tbody>
26 );
27};
28
29TableRow.propTypes = {
30 data: PropTypes.array.isRequired,
31 destroyTask: PropTypes.func,
32 updateTask: PropTypes.func
33};
34
35export default TableRow;
Finally, add the updateTask
function to app/javascript/src/components/Dashboard/index.jsx
. To do so, add the following lines:
1import React, { useState, useEffect } from "react";
2import { isNil, isEmpty, either } from "ramda";
3
4import Container from "components/Container";
5import ListTasks from "components/Tasks/ListTasks";
6import tasksAPI from "apis/tasks";
7import PageLoader from "components/PageLoader";
8
9const Dashboard = ({ history }) => {
10
11 // --- Previous Content ---
12 const updateTask = slug => {
13 history.push(`/tasks/${slug}/edit`);
14 };
15
16 if (either(isNil, isEmpty)(tasks)) {
17 return (
18 <Container>
19 <h1 className="text-xl leading-5 text-center">You have no tasks assigned 😔</h1>
20 </Container>
21 );
22 }
23
24 return (
25 <Container>
26 <ListTasks data={tasks} destroyTask={destroyTask} updateTask={updateTask} showTask={showTask} />
27 </Container>
28 );
29};
30
31export default Dashboard;
32
Now, on the dashboard page while listing tasks, clicking on Edit
would render the EditTask
component that we just created where we will be able to edit task details.
1git add -A
2git commit -m "Added ability to update a task"