W04 Learning Activity: Assigning Categories to a Project
Overview
This activity will walk you through the process of assigning categories to a project. You will create a page that allows users to select categories for a project using checkboxes. Then, you will update the many-to-many table in the database to reflect the changes.
Activity Instructions
Complete the following steps to create the page to assign categories to a project.
Create the model functions
For this new functionality, you can re-use your existing functions to get all categories and to get categories assigned to a specific project.
You will create two new model functions for this functionality. First, you will create a function assignCategoryToProject that assigns a category to a project in the many-to-many relationship table.
Then, you will create a new model function updateCategoryAssignments that updates the categories assigned to a project. This function will receive two parameters: projectId and categoryIds (an array of desired category IDs for the project). The function will first remove any previous assignments, then for each categoryId in the new list, it will call the assignCategoryToProject function.
- Open the
src/models/categories.jsmodel file. - Create a function
assignCategoryToProjectthat takes two parameters:projectIdandcategoryId.- This function should execute a SQL query to insert a new record into the many-to-many relationship table that links projects and categories.
- Create a function
updateCategoryAssignmentsthat takes two parameters:projectIdandcategoryIds(an array of category IDs that should be assigned to the project). - This function should first execute a SQL query to delete all existing category assignments for the specified project from the many-to-many relationship table.
- Then, for each
categoryIdin thecategoryIdsarray, call theassignCategoryToProjectfunction to create the new assignments. - Add your
updateCategoryAssignmentsfunction to the list of exports at the bottom of the file. (You do not need to add theassignCategoryToProjectfunction to the exports because it is not used outside the model file.)
Sample Code (click to expand)
const assignCategoryToProject = async(categoryId, projectId) => {
const query = `
INSERT INTO project_category (category_id, project_id)
VALUES ($1, $2);
`;
await db.query(query, [categoryId, projectId]);
}
const updateCategoryAssignments = async(projectId, categoryIds) => {
// First, remove existing category assignments for the project
const deleteQuery = `
DELETE FROM project_category
WHERE project_id = $1;
`;
await db.query(deleteQuery, [projectId]);
// Next, add the new category assignments
for (const categoryId of categoryIds) {
await assignCategoryToProject(categoryId, projectId);
}
}
Create the controller functions
Create controller functions to handle displaying the assign categories form and processing the form submission.
The request URL to display the form (GET) and process the form (POST) will be /assign-categories/:projectId where the projectId is a placeholder for the actual project ID.
- Open the
src/controllers/categories.jscontroller file. - Create a new function
showAssignCategoriesForm.- Get the projectId from the request parameters.
- This function should retrieve the project details using the existing
getProjectDetailsmodel function. (You will likely need to add this to your list of imports from the project model file). - It should also retrieve all categories using the existing
getAllCategoriesmodel function. - Additionally, it should retrieve the categories currently assigned to the project using the existing
getCategoriesByServiceProjectIdmodel function. (You will likely need to add this to your list of imports from the categories model file.) - It should set the title variable to be, "Assign Categories to Project"
- Finally, it should render a view
assign-categories(to be created in the next step) and pass the project details, all categories, and the assigned categories to the view.
- Create a new function
processAssignCategoriesForm.- Get the projectId from the request parameters.
- Get the selected category IDs from the request body. (Assume the form field name is
categoriesand it will be an array of selected category IDs.) - This function should call the
updateCategoryAssignmentsmodel function you created in the previous step, passing in the projectId and the array of selected category IDs. (You will likely need to add this to your list of imports from the categories model file.) - Set a success flash message.
- Redirect the user back to the project details page
/project/{projectId}.
- Add these two controller functions to your list of exports in the controller file.
Sample Code (click to expand)
const showAssignCategoriesForm = async (req, res) => {
const projectId = req.params.projectId;
const projectDetails = await getProjectDetails(projectId);
const categories = await getAllCategories();
const assignedCategories = await getCategoriesByServiceProjectId(projectId);
const title = 'Assign Categories to Project';
res.render('assign-categories', { title, projectId, projectDetails, categories, assignedCategories });
};
const processAssignCategoriesForm = async (req, res) => {
const projectId = req.params.projectId;
const selectedCategoryIds = req.body.categoryIds || [];
// Ensure selectedCategoryIds is an array
const categoryIdsArray = Array.isArray(selectedCategoryIds) ? selectedCategoryIds : [selectedCategoryIds];
await updateCategoryAssignments(projectId, categoryIdsArray);
req.flash('success', 'Categories updated successfully.');
res.redirect(`/project/${projectId}`);
};
Add routes
Add routes to your application for displaying the assign categories form and processing the form submission.
- Open your main routes file
src/controllers/routes.js. - Import the controller functions
showAssignCategoriesFormandprocessAssignCategoriesFormfrom the categories controller file. - Add a GET route for
/project/:projectId/assign-categoriesthat calls theshowAssignCategoriesFormcontroller function. - Add a POST route for
/project/:projectId/assign-categoriesthat calls theprocessAssignCategoriesFormcontroller function.
Sample Code (click to expand)
// Routes to handle the assign categories to project form
router.get('/assign-categories/:projectId', showAssignCategoriesForm);
router.post('/assign-categories/:projectId', processAssignCategoriesForm);
Create the view
Create a view template for the assign categories form. This form should display all categories with checkboxes. The checkboxes for the categories currently assigned to the project should be checked when the user visits the form.
- Create a new EJS file
src/views/assign-categories.ejs. - Create a form that submits to the POST route
/assign-categories/:projectId. - Display the project title at the top of the form.
- Loop through all categories and create a checkbox for each category.
- The checkbox should be checked if the category is in the list of assigned categories for the project.
- The checkbox name should be
categoryIdsso that it submits an array of selected category IDs.
- Add a submit button to the form.
- Add a link on the project page to navigate to the assign categories form. For example:
<a href="/assign-categories/<%= project.project_id %>">Edit Categories</a>
Sample Code (click to expand)
<%- include('partials/header') %>
<main>
<h1><%= projectDetails.title %></h1>
<h2>Assign Categories</h2>
<form action="/assign-categories/<%= projectId %>" method="POST">
<div class="form-group">
<% categories.forEach(category => { %>
<div>
<input
type="checkbox"
id="category-<%= category.category_id %>"
name="categoryIds"
value="<%= category.category_id %>"
<%= assignedCategories.some(ac => ac.category_id === category.category_id) ? 'checked' : '' %>
/>
<label for="category-<%= category.category_id %>"><%= category.name %></label>
</div>
<% }); %>
</div>
<button type="submit">Update Categories</button>
</main>
<%- include('partials/footer') %>
Test and deploy
Test the new functionality thoroughly to ensure that categories can be assigned and unassigned correctly. Once you are satisfied with the functionality, deploy your changes to your hosting platform.
Next Step
You have now completed all the learning activities for this week. Return to Canvas to submit a quiz.
Other Links:
- Return to: Week Overview | Course Home