In the last chapter, we saw how to create a new task and save it to the
database. Let's add a new feature to display the newly created task.
Features
These are the requirements of this feature:
-
A show button should be present for each task. User should be redirected to
task details page on clicking the show button.
-
Task details page should only contain the task title for now. Below is a
picture showing how UI would look like once implemented.
Do not get overwhelmed by the edit and delete buttons as well as the comment
section. We will cover those features later in this book.
Technical design
To implement this feature, we need to introduce the following changes:
On the backend
-
Add a show route for tasks RESTful resources in the routes file.
-
We will add a show action in the TasksController. When the application
receives the request to show a task, that request will be processed by the
show action.
On the frontend
-
Add a GET API connector for fetching a task inside the tasks API collection.
-
Add a Show component which will receive the task slug using URL params.
Show component will call the API to fetch a task and display the task
details.
-
In the Dashboard component, add a new showTask function which will
redirect the application to a new page based on the task slug using
react-router-dom.
-
Pass down showTask function as a prop to the Row component through the
Table component.
-
Add a new Route in the App component for the task page. This route should
render the Show component.
Implementing show action in TasksController
Open /app/controllers/tasks_controller.rb and add the following lines of code:
In the show action we are trying to find and respond with a task by filtering
using the slug attribute.
Note that in the above action we have used the find_by! method which will
raise an exception if no matching record is found in the database. We do not
need to worry about handling the exception anymore as we have already handled
that inside the ApplicationController.
After the task is loaded successfully, the render_json method responds with an
:ok status and the task json.
We have chosen the render_json method over the render_notice method here
because render_notice method also sends back a success message along with a
json which gets displayed as a notification in the client-side. We do not want
to send a success message in this case.
Building Show Component
Let's first create our task's show component:
In Show.jsx, paste the following content:
useParams hook is provided by the react-router-dom package and it returns an
object of URL parameters as key/value pairs. For example, for
/tasks/:task-slug, useParams will return the following object:
Let's now create an API route to handle our show request in
app/javascript/src/apis/tasks.js.
Note: Until now we have been using highlighted lines of code to show what
needs to be added to the codebase/component.
However, from here onwards, since we will be making a lot of changes and
additions to each of the components, if we haven't highlighted any lines in code
block, then the best way to stay in sync is to copy the whole code snippet and
replace the corresponding component/file in your local project with that
content, unless specified otherwise.
App.jsx will only require updates in most chapters, rather than full
replacement.
This will help you from not missing out on any of the changes that needs to be
reflected in your local project.
Now update tasks.js with the following content:
Now we will create a showTask handler function in the Dashboard and pass it
down to the Table component. Fully replace
app/javascript/src/components/Dashboard/index.jsx with the following content:
We need to pass down showTask function as props to Row component and attach
it to an onClick event which will be fired upon clicking the show button.
To do so, update app/javascript/src/components/Tasks/Table/index.jsx with the
following lines of code:
Then, update the content of Row.jsx with following lines:
Now we have completed adding the show action handler for a task and hooked it to
the onClick event. The last step is to define a route inside of App.jsx to
render our tasks show page.
But while importing the Show component in App.jsx we will be using the same
namespace as in Create component which is components/Tasks.
Here we can add an index.js file to components/Tasks to reduce the number of
import statements in App.jsx and also to make things look cleaner.
These conventions are documented in
this chapter
in our React Best Practices Book.
Create a new file index.js in app/javascript/src/components/Tasks and add
the following lines:
Here, we have imported the Create and Show as CreateTask and ShowTask
because outside the tasks namespace or inside App.jsx names like Create
and Show can be very confusing and may also give errors as there can be
multiple Create and Show from different folders(namespaces). Thus it's better to
export them as CreateTask and ShowTask.
Now add the highlighted lines to App.jsx:
Now, let's go to the dashboard URL and click
on the Show button, we might see that an error has occurred:
We are getting an error because the Rails router can't find any route matching
the format /tasks/:slug for a GET request or more accurately since we
haven't defined show action in task resources.
Let's solve this by adding that action into /config/routes.rb.
Note: It is possible that you're able to access the show page for an individual
task even before updating the routes.rb file without encountering any routing
error.
But the issue that you might notice is that the title of the task will remain
empty as we are not able to fetch details from the backend due to absence of the
corresponding API route for returning the title details.
Adding the route in the following step will allow us to view the show page with
title of the task:
Now if we visit dashboard URL and then click
on Show button corresponding to a task, we will be able to see the details of
that task.
Using Rails console to search tasks
Let's fire up the console once again using the rails console command:
As shown above, we use the find_by method and pass it with any slug.
If an entry in the database exists with corresponding slug, then the record is
fetched otherwise exception is returned.
You can use the find_by! method which will raise an
ActiveRecord::RecordNotFound exception if no tasks with a matching slug is
found in the database.
We can also use other attributes to look for a specific task using the where
method.
Let's try with the title attribute:
Now let's commit these changes: