Welcome back to our journey in building an MVC-based ToDo application with NestJS! In the previous lesson, you learned how to build a basic MVC application with a hardcoded list of ToDo items.
Now, it's time to expand on that foundation by adding functionality to retrieve specific ToDo items based on their IDs. This will allow users to view the details of a specific ToDo item, enhancing the app's usability.
In this lesson, you'll learn:
- Extending the Service: How to add a method to your
TodoService
for retrieving a specific ToDo item by its ID. - Updating the Controller: How to modify your
TodoController
to handle requests for retrieving and displaying a specific ToDo item. - Dynamic Routing: How to handle dynamic routes to access detailed views of ToDo items.
This lesson will help you take a step toward building dynamic applications that provide users with specific, detailed information.
Let’s start by retrieving a ToDo item using its ID. This is a common feature in many applications that display detailed information about a single record.
To implement this feature, we'll extend the TodoService
by adding a method to retrieve a specific item by its id
.
TypeScript1import { Injectable } from '@nestjs/common'; 2import { Todo } from './todo.model'; 3 4@Injectable() 5export class TodoService { 6 private todos: Todo[] = [ 7 new Todo(1, 'Learn NestJS', '2023-09-01', 'Explore the basics of NestJS'), 8 new Todo(2, 'Develop MVC App', '2023-10-01', 'Build an application using the MVC pattern'), 9 new Todo(3, 'Study Node.js', '2023-11-01', 'Deep dive into Node.js fundamentals'), 10 new Todo(4, 'Review TypeScript', '2023-12-01', 'Brush up on TypeScript essentials') 11 ]; 12 13 findAll(): Todo[] { 14 return this.todos; 15 } 16 17 findOne(id: number): Todo | undefined { 18 return this.todos.find(todo => todo.id === id); 19 } 20}
Here, the findOne
method takes an id
as a parameter and searches through the todos
array to find the matching Todo
object. If a match is found, it returns the Todo
item; otherwise, it returns undefined
. This method is a typical pattern for fetching individual records, such as viewing user profiles or specific tasks.
By keeping the data handling logic within the service, the controller can focus on handling requests and passing the necessary data to the view.
Now that we have a method to retrieve a single item, let's modify the TodoController
to handle the request for fetching an individual ToDo item.
TypeScript1import { Controller, Get, Param, Render } from '@nestjs/common'; 2import { TodoService } from './todo.service'; 3 4@Controller('todos') 5export class TodoController { 6 constructor(private readonly todoService: TodoService) {} 7 8 @Get() 9 @Render('index') 10 findAll() { 11 const todos = this.todoService.findAll(); 12 return { title: 'ToDo List', todos }; 13 } 14 15 @Get(':id') 16 @Render('details') 17 findOne(@Param('id') id: string) { 18 const todo = this.todoService.findOne(Number(id)); 19 return { title: 'Todo Details', todo }; 20 } 21}
In the controller, we introduce a new route for fetching a specific ToDo item by its id
. The @Param
decorator is used to extract the id
from the URL, and the controller calls the findOne
method from the service to retrieve the corresponding ToDo item.
It's important to note the order of methods in the controller: routes with static paths (like /count
) should come before dynamic routes (like /:id
). This ensures that routes are matched correctly and the controller doesn't mistakenly treat "count" as an id
parameter. This practice helps avoid unintended routing issues as your application grows.
We will now add a form to the index.hbs
view to allow users to find a specific ToDo item by its ID. We'll use JavaScript to handle the form submission and redirect the user to the details page.
HTML, XML1<h1>{{title}}</h1> 2<ul> 3 {{#each todos}} 4 <li>{{this.title}} - ID: {{this.id}}</li> 5 {{/each}} 6</ul> 7 8<div class="search-container"> 9 <h2>Find ToDo by ID</h2> 10 <form id="find-todo-form"> 11 <input type="number" id="todo-id" placeholder="Enter ToDo ID" required /> 12 <button type="submit">Find</button> 13 </form> 14</div> 15 16<script> 17 document.getElementById('find-todo-form').addEventListener('submit', function(event) { 18 event.preventDefault(); 19 const todoId = document.getElementById('todo-id').value; 20 if (todoId) { 21 window.location.href = `/todos/${todoId}`; 22 } 23 }); 24</script>
This Handlebars template includes:
- A list of ToDo items displaying only their titles and IDs.
- A form allowing the user to enter a ToDo ID.
- A JavaScript snippet that intercepts the form submission, retrieves the entered ID, and redirects the user to the appropriate URL (
/todos/:id
) to fetch the details of the selected ToDo item. Don't worry if the JavaScript part seems unfamiliar; it's simply used to dynamically construct the/todos/id
route in the HTML based on the user's input.
Lastly, we’ll create a simple view that will render the details of the selected ToDo item.
HTML, XML1<h1>{{title}}</h1> 2<p>Title: {{todo.title}}</p> 3<p>Description: {{todo.description}}</p> 4<p>Due Date: {{todo.dueDate}}</p>
This template dynamically renders the title
, description
, and dueDate
of the ToDo item that the controller passes. The todo
object is populated in the controller and sent to the view for rendering.
This template provides the user with a clear and detailed view of the specific ToDo item they selected.
Retrieving specific items by their properties, such as id
, is a crucial feature in most applications. Here's why it matters:
- Detail Display: Users need to view detailed information about specific items, which is a fundamental feature in applications like task managers, e-commerce stores, and content management systems.
- Dynamic Routing: Handling dynamic routes allows you to build flexible and interactive applications that respond to user input.
- Scalability: As your application grows, the ability to efficiently retrieve and display specific records becomes essential for both performance and maintainability.
By mastering this lesson, you’re laying the groundwork for more advanced features, such as editing or deleting individual items, which will be covered in upcoming lessons.
Ready to practice? Head over to the practice section to see this in action and implement similar functionality yourself!