TVBZ
TVBZ

Reputation: 594

jQuery/JS: delete object from array and DOM

I am learning JS and jQuery. As an exercise, I am trying to create a basic contact list. I need to be able to add and delete contacts from the list.

But I am having some bugs in my result. I can't find the cause of it. If anyone can advise? I would be most gratefull.

BUG 1: When running the snippet below, you will see some sample contacts generated in the list. You can delete a contact just fine. You can also add a contact. But after adding a contact, the delete buttons stop working.

First I thought the issue was probably a missing character in my .html() method on the contacts objects. But I see no errors there. Anyone?

BUG 2: Inside the $renderContacts function. You can see the const html. This should replace the let html and for loop below. And it works for long list of contacts. But the first contact is rendered as [object Object]. I don't see the cause. Might these 2 bugs be related?

Please advise. Many thanks! :)

$(document).ready(function() {

  // Array to store all contacts
  let contactsArr = [];

  // counter to create incrementing ID
  let contactID = 0;

  // Constructor for contact objects
  function Contact(firstName, lastName, email, phone, address) {
    this._id = contactID += 1;
    this.firstName = firstName;
    this.lastName = lastName;
    this.email = email;
    this.phone = phone;
    this.address = address;
    contactsArr.push(this);
  };

  // Some getters, setters and a methods for contact objects
  Contact.prototype = {
    constructor: Contact,
    set(id) {
      console.log(`ID is generated on input and may not be changed`)
    },
    get id() {
      return this._id;
    },
    set firstName(firstName) {
      this._firstName = firstName;
    },
    get firstName() {
      return this._firstName;
    },
    set lastName(lastName) {
      this._lastName = lastName;
    },
    get lastName() {
      return this._lastName;
    },
    set email(emailaddress) {
      this._email = emailaddress;
    },
    get email() {
      return this._email;
    },
    set phone(phone) {
      this._phone = phone;
    },
    get phone() {
      return this._phone
    },
    set address(address) {
      this._address = address;
    },
    get address() {
      return this._address
    },
    toHTML() {
      const renderCell = (content, cssClass = "") => `<div class="table-cell ${cssClass}">${content}</div>`;
      const deleteContact = `<span title="Delete contact" class="delete-contact far fa-trash-alt fa-sm"></span>`;
      const rowActions = renderCell(deleteContact, "text-right contact-actions");
      return '<div class="table-row">' +
        renderCell(this.id, "contact-id text-right") +
        renderCell(this.firstName, "first-name") +
        renderCell(this.lastName, "last-name") +
        renderCell(this.email, "email") +
        renderCell(this.phone, "phone") +
        renderCell(this.address, "address") +
        rowActions + '</div>';
    },
  };


  // SAMPLE CONTACTS
  new Contact("John", "Cubico", "[email protected]", "111-555-6666", "Belgium");
  new Contact("Lisa", "The Sailor", "[email protected]", "111-666-7777", "Spain");
  new Contact("Christophe", "From next door", "[email protected]", "111-777-8888", "Germany");
  new Contact("Aïsha", "From elsewere", "[email protected]", "111-888-9999", "Brussels, Holland");

  // Render Samples
  function $renderContacts(arr = contactsArr) {
    //const html = arr.reduce((all, one) => all += one.toHTML()); // ==>> 1st not rendering
    let html = ``;
    for (let i = 0; i < arr.length; i++) {
      html += arr[i].toHTML();
    };
    $("#contacts-list").append(html);
  };
  $renderContacts();



  // BUTTONS & ACTIONS

  // Add contact
  $("#add-contact").on("click", () => {
    const $firstName = $("#first-name").val();
    const $lastName = $("#last-name").val();
    const $email = $("#email").val();
    const $phone = $("#phone").val();
    const $address = $("#address").val();
    const contact = new Contact($firstName, $lastName, $email, $phone, $address); // create contact
    $("#contacts-list").append(contactsArr[contactsArr.length - 1].toHTML()); // add contact to DOM
  });

  // Delete contact
  $(".delete-contact").on("click", (event) => {
    const arr = contactsArr.slice();
    const $id = Number($(event.currentTarget).closest(".table-row").find(".contact-id").text());
    const i = arr.findIndex(contact => contact.id == $id);
    contactsArr = arr.slice(0, i).concat(arr.slice(i + 1)); // delete from array of contacts
    $(event.currentTarget).closest(".table-row").remove(); // delete only this contact from DOM
  });


});
html {}

.active {}

.inactive {
  color: #b8b8b8;
}

.table td,
.table th {
  padding: 0.5rem;
}

.text-large {
  font-size: 1.5rem;
}

.relative {
  position: relative;
}


/*** Search ***/

#search-input {
  font-size: 2rem;
  font-weight: 300;
}

span#search-btn {
  position: absolute;
  right: 0;
  top: 0;
  width: 50px;
  height: 47px;
  padding: 10px;
  box-sizing: border-box;
  font-size: 1.6rem;
  line-height: 34px;
}


/*** Contacts table ***/

.table-header {
  padding: 15px 0 5px;
}

.table-row {
  display: grid;
  grid-template-columns: 30px repeat(5, 1fr) 100px;
  grid-column-gap: 20px;
  margin: 3px 0;
  padding: 2px 0;
  background: #f0f0f0;
}

.table-cell {
  padding: 3px;
}

.editable-cell {
  background: rgba(255, 255, 255, 0.6);
}

.contact-actions span {
  line-height: 18px;
  padding: 3px;
  display: inline-block;
  width: 26px;
  text-align: center;
}


/*** Form ***/

#form-new-contact {
  align-items: end;
  margin: 0 -15px;
  padding: 15px;
}

#add-contact {
  width: 100%;
}
<html>

<head>
  <title>jQuery contacts app</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
  <link rel="stylesheet" type="text/css" href="styles.css">
</head>

<body>
  <header>
    <div class="container text-center mt-4 mb-4">
      <h1>jQuery contacts app</h1>
    </div>
  </header>
  <main>
    <div class="container">
      <form id="form-new-contact" action="" method="POST" class="table-row mt-4 mb-4">
        <div class="text-center"><span class="fas fa-user-plus fa-sm mb-2"></span></div>
        <div class="">
          <label for="first-name">First name:</label>
          <input name="first-name" id="first-name" class="form-control form-control-sm" type="text" value="Voornaam" placeholder="John" required>
        </div>
        <div class="">
          <label for="last-name">Last name:</label>
          <input name="last-name" id="last-name" class="form-control form-control-sm" type="text" value="Achternaam" placeholder="Doe" required>
        </div>
        <div class="">
          <label for="email">Email:</label>
          <input name="email" id="email" class="form-control form-control-sm" type="email" value="E-mailadres" placeholder="[email protected]" required>
        </div>
        <div class="">
          <label for="phone">Phone:</label>
          <input name="phone" id="phone" class="form-control form-control-sm" type="tel" value="Telefoon/GSM" placeholder="555-666-8989" required>
        </div>
        <div class="">
          <label for="address">Address:</label>
          <input name="address" id="address" class="form-control form-control-sm" type="text" value="Adres" placeholder="Somewhere" required>
        </div>
        <div class="">
          <button id="add-contact" type="button" class="btn btn-sm btn-success"><i class="fas fa-plus-circle mr-2"></i>Add</button>
        </div>
      </form>
      <div class="table">
        <div class="table-row table-header">
          <div id="id-header" class="text-right">
            <h3 class="h6">ID</h3>
          </div>
          <div id="first-name-header" class="">
            <h3 class="h6">Firstname</h3>
          </div>
          <div id="last-name-header" class="">
            <h3 class="h6">Lastname</h3>
          </div>
          <div id="email-header" class="">
            <h3 class="h6">Email</h3>
          </div>
          <div id="phone-header" class="">
            <h3 class="h6">Phone</h3>
          </div>
          <div id="address-header" class="">
            <h3 class="h6">Address</h3>
          </div>
          <div class="text-right">
            <h3 class="h6">Actions</h3>
          </div>
        </div>
        <div id="contacts-list" class="">

        </div>

      </div>
    </div>
  </main>


  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <script type="text/javascript" src="script.js"></script>
</body>

</html>

Upvotes: 0

Views: 169

Answers (1)

Lab Lab
Lab Lab

Reputation: 801

BUG1:

You can't fire on click function on dynamically generated DOM element like that. Add event listener to your element (using pure JS):

elem.addEventListener("click", func, false);

Or change syntax of your click function to this (using jQuery):

$(document).on("click",'.delete-contact', (event) => {
 // your code here
});

BUG2:

I don't know why this function acts like that, but you always have to pass default string parameter. Look to the next lines I made:

let html = arr.reduce((all, one) => all += one.toHTML(), '');

Working fiddle

Upvotes: 4

Related Questions