skip to Main Content

I’m trying to make this post feature where the user can enter a number of names, and each name will be printed as a list item in ascending order like this:

    1. Bruce Wayne

    2. Peter Parker

    3. Clark Kent

I’ve tried having an empty array, and then having the results of the form pushed onto the array, and have the computer print that name as a list-item, but it hasn’t worked. I just get the aforementioned problem.

Here’s my code:

JS part:

app.post("/submit", (req, res) => {
    const clientName = req.body["fName"] + req.body["mName"] + req.body["lName"];
    const nameList = [];
    const characterName = nameList.push(clientName)

    res.render("index.ejs", nameList, { 
        userName: clientName,
    });
});

EJS part:

    <% if (locals.userName) { %>

        <ul>
            <% for (let userName = 0; userName < locals.characterName; userName++) { %>

                <li>
                    <%= nameList[userName] %>
                </li>
            <% } %>
        </ul>

    <% } else { %>

        <p>No Names to Display</p>

    <% } %>

2

Answers


  1. Make that const nameList = [] a global variable. Otherwise the data of the array will not be stored and gets destroyed everytime the get method at /submit being called.
    Also it feels like you are missing the property characterName in the object you are passing in the res.render function.

    Login or Signup to reply.
  2. Alright, there is a lot going on in your question.

    First and foremost, as @David says in his comment, it’s not clear exactly what error you are encountering. Further, there are many decisions for the code logic that will not work this way. Let’s look at all the issues one after another:

    (I know it’s a lot, but if you really want to understand what is happening and why, you should read through this)

    1. Use of variables

    The main issue of this code logic is that there is actually no variable storing the names. Just because you define the nameList variable as const doesn’t mean it is a globally available variable from this point on in any scope. What you are doing right now (as @Injamul is saying in his answer), is creating the list each time the request handler is invoked. Instead, what you should do, is to define the nameList variable earlier in your code as a global variable.

    import express from 'express'
    import...
    
    const app = express();
    
    const nameList = []; // defined as global variable
    
    app.post("/submit", (req, res) => {
      ...
      nameList.push(...) // so that it can be used here
      ...
    });
    

    The characterName variable, on the other hand, is useless, as you can just say nameList.length in the template. But in the end that comes down to personal preference. But at the very least, you need to pass the variable to the render function so it can be used. Which leads us to…

    2. Rendering EJS templates

    The next big issue in your code is the (mis)use of variables in the context of rendering templates.

    You seem to (somewhat?) know that variables you want to use in the EJS template need to be passed to the render() method. However, I simply cannot understand what led you to have 3 variables and handling each one different (you did not even pass characterName)?? The only variable that is properly passed to the function is clientName. You need to pass any variable you want to use in the template in a single object, not as another parameter for the render() method or any way else (except manually adding a variable to the locals context).

    res.locals.someVariable = someValue; // manually adding to the locals context
                                         // (can be done, but rather do the standard way)
    
    // "standard" way:
    res.render("index.ejs", { 
      userName: clientName,
      nameList: nameList,
      characterName: characterName
    });
    

    This adds the variables to the locals context, which in the template then can be accessed either as just someVariable or locals.someVariable, it makes no difference (more on EJS variables and the locals context here).

    3. EJS template

    The last problem is with your EJS template itself. In the definition of the for() loop you initialize a variable with the same name as a variable you passed to the render() function: userName. That means, up to the point where the for loop is, userName was holding a string with the username, but once you (re)initialized that variable in the for loop as for(let userName = 0... its value changed to 0. So if you had passed all variables correctly to the render function (and made nameList a global variable), userName would not be the last added name, but a number from 0 to the number of names saved. You should name that variable different, generally most people define a for loop as for(let i = 0; i < someVariable; i++) (or let j, let k and so on).

    To sum everything up, making the current code you have running, it should look something like:

    const nameList = [];
    
    app.post("/submit", (req, res) => {
        const clientName = req.body["fName"] + req.body["mName"] + req.body["lName"];
        const characterCount = nameList.push(clientName);
        res.render("index.ejs", {nameList: nameList, count: characterCount});
    });
    
        <% if (nameList && nameList.length) { %>
    
            <ul>
                <% for (let i = 0; i < count; i++) { %>
    
                    <li>
                        <%= nameList[i] %>
                    </li>
                <% } %>
            </ul>
    
        <% } else { %>
    
            <p>No Names to Display</p>
    
        <% } %>
    

    But ask yourself this: Is it really necessary making a post request and have the user load the page every time a name is added to the list?

    Instead, you could manipulate the DOM by appending <li> items to the list, so something like:

    let container = document.querySelector('.container-list');
    let inputs = document.querySelector('.container-inputs').children;
    let fName = inputs[0];
    let mName = inputs[1];
    let lName = inputs[2];
    
    function addNameToList() {
      if(fName.value === "" || lName.value === "")
        return;
      
      if(container.firstElementChild.nodeName === "P") {
        let list = document.createElement("ul");
        list.classList.add(".name-list");
        container.replaceChildren(list);
      }
      
      let name = fName.value.trim() + " " + mName.value.trim() + " " + lName.value.trim();
      let item = document.createElement("li");
      item.textContent = name;
      container.firstElementChild.append(item);
    }
    
    function removeNameFromList() {
      let list = container.firstElementChild;
    
      if(list.nodeName !== "UL")
        return;
        
      list.removeChild(list.lastElementChild);
      
      if(!list.children.length) {
        let text = document.createElement("p");
        text.textContent = "No names to display";
        container.replaceChildren(text);
      }
    }
    <div class="container-list">
      <p>No names to display</p>
    </div>
    <form class="container-inputs" onsubmit="event.preventDefault();">
      <input type="text" name="fName" autocomplete="given-name" placeholder="First name" required>
      <input type="text" name="mName" autocomplete="additional-name" placeholder="Middle name">
      <input type="text" name="lName" autocomplete="family-name" placeholder="Last name" required><br>
      <button onclick="addNameToList()">Add name</button>
      <button onclick="removeNameFromList()">Remove name</button>
    </form>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search