Lesson 2
Fetching ToDo Details with Service Methods
Retrieving Data from the Service

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.

What This Lesson Covers

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.

Example: Retrieving a Single ToDo Item by ID

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.

Enhancing the Todo Service

To implement this feature, we'll extend the TodoService by adding a method to retrieve a specific item by its id.

TypeScript
1import { 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.

Updating the Todo Controller

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.

TypeScript
1import { 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.

Updating the View with a Find Form

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, XML
1<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.
Creating the Details View

Lastly, we’ll create a simple view that will render the details of the selected ToDo item.

HTML, XML
1<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.

Why This Matters

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!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.