How to access stylesheet when I render as EJS in a dynamic route based on URL parameters

814 Views Asked by At

I recently started learning Express.js and mongoose by creating a simple To-Do List, and now I'm stuck with a basic thing.

When I tried to render a EJS file in a dynamic route based on URL parameters, I was unable to apply the stylesheet.css which is stored in the public folder for the EJS, and the following error message showed up in the browser console. The error message:

Refused to apply style from 'http://localhost:3000/mylist/css/styles.css' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled.

And of course, the URL request was http://localhost:3000/mylist/css/styles.css.

On the other hand, the same EJS file rendered in the home route was able to access the stylesheet.css. And the request URL was http://localhost:3000/css/styles.css.

So I guess whatever I try to render, the request URL has to be http://localhost:3000/css/styles.css in my file structure. However, I don't know how to get rid of mylist from http://localhost:3000/mylist/css/styles.css, when I render as EJS in a dynamic route based on URL parameters.

What's wrong with my code? How do you solve it?

File structure:

enter image description here

app.js

const express = require("express");
const mongoose = require("mongoose");
const app = express();

app.set('view engine', 'ejs');
app.use(express.static("/public"));

mongoose.connect("mongodb://localhost:27017/todolistDB", {useNewUrlParser: true, useUnifiedTopology: true});

const itemsSchema = mongoose.Schema({ name: String });
const customListsSchema = mongoose.Schema({ name: String, items: [itemsSchema] });

const Item = mongoose.model("Item", itemsSchema);
const CustomList = mongoose.model("CustomList", customListsSchema);

app.get("/", (req, res) =>  {
  Item.find((err, foundItems) => {
    if (!err) {
      res.render("list", { listTitle: "Today", newListItems: foundItems });
    }
  });
});

app.get("/mylist/:customListName", (req, res) => {
  const customListName = req.params.customListName;
  CustomList.findOne({ name: customListName }, (err, foundCustomList) => {
    if (!err) {
      if (foundCustomList) {
        res.render("list", {
          listTitle: foundCustomList.name,
          newListItems: foundCustomList.items
        });
      } else {
        const customList = new CustomList({ name: customListNamem });
        customList.save((err) => {
          if (!err) {
            res.redirect("/mylist/" + customListName);
          }
        });
      }
    }
  });
});

list.ejs:

<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
  <meta charset="utf-8">
  <title>To-Do List</title>
  <link rel="stylesheet" href="css/styles.css">
</head>
<body>
  <div class="box" id="heading">
    <h1> <%= listTitle %> </h1>
  </div>
  <div class="box">

    <form action="/delete" method="post">
      <% newListItems.forEach((newListItem) => { %>
        <div class="item">
          <input type="checkbox" name="checkbox" value="<%= newListItem._id %>" onchange="this.form.submit()">
          <p><%= newListItem.name %></p>
        </div>
      <% }); %>
    </form>

    <form class="item" action="/" method="post">
      <input type="text" name="newItem" placeholder="New Item" autocomplete="off">
      <button type="submit" name="listName" value="<%= listTitle %>">+</button>
    </form>

  </div>
</body>
</html>

I also tried and replaced app.use(express.static("/public")); with app.use(express.static(__dirname + "/public"));, but it didn't work either.

2

There are 2 best solutions below

0
On

I figured out how to fix the bug by myself.

It was caused by the fact that the path to the stylesheet in the EJS was a relative path. It seems that it has to be an absolute path.

So I simply added a forward slash / and turned <link rel="stylesheet" href="css/styles.css"> to <link rel="stylesheet" href="/css/styles.css"> in the list.ejs.

Now it works with app.use(express.static(__dirname + "/public")); in app.js, however it doesn't still work with app.use(express.static("/public"));.

What's the difference between them?

0
On

I couple __dirname with path.join() to create an absolute path to the directory, that will work on the machine it is being run on.

What you're doing with: app.use(express.static(path.join(__dirname, 'public'))); setting the static folder to /Absolute/Path/To/Repo/public/ where the __dirname is the "/Absolute/Path/To/Repo/" part.

So run on my computer it would return: /home/proto/UWebDev/Playground/myapp/public and on your it would be whatever the path to that particular public directory would be.

Conversely, in your initial code you simply used:

app.use(express.static("/public") ; which if console logged anywhere will return /public. Which is an absolute path that I'm assuming isn't in you repo since,I haven't heard of anyone making a repo out of there machine's root directory.

For what it's worth, what you've done in app.js made it so anything on the front-end that begins with / is actually referencing /ABS/PATH/TO/REPO/public/; in this particular scenario, I believe simply replacing your original "/public" with "./public" would have worked as well (in addition adding the / to the path to the css as you did.

Sidenote: path.join(__dirname,'public) === path.join(__dirname,'/public') for what it's worth.

I'm fairly new to developing, so if anyone sees something I made a mistake on or oversimplified, please point it out, but hopefully this helps someone who get to this page looking for answers.