Build A Simple MVC CRUD Web App in JavaScript (ES6) Part 1
Tags: javascript, programming, tutorial
Published: 2022-02-07 14:26:11

---
canonical_url: https://softauthor.com/javascript-es6-mvc-crud-app/
---

In this ES6 MVC JavaScript tutorial, you’re going to learn how to build a simple CRUD app using ES6 Class.

###STEP 01: Setting Up The Project

Here is the Address Book Project setup. It has just three simple files: HTML, CSS and JS.

- Create a folder structure.

```JavaScript
| AddressBook (folder) 
| -- index.html 
| -- app.js 
| -- style.css
```

- Link style.css and app.js file to the index.html file.

```HTML
<!DOCTYPE html>
<html>

<head>
    <title>Address Book - How to write testable javascript code</title>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>

<body>
    <script type="text/javascript" src="app.js"></script>
</body>

</html>
```

- Create contact list module inside index.html where all of the contacts would be.

```HTML
<body>
    <h2>Address Book</h2>

    <!-- contact list module -->
    <section>
        <ul id="contact-list">loading...</ul>
    </section>
</body>
```

###Create An MVC Structure

**Model**

Add some Model Data which is the M part of MVC inside app.js . This could be a separate class fetching data from API via AJAX call. For simplicity sake, I have made a simple array of objects called **contactsData**.

```JavaScript
// ============== Model ========================= 
const contactsData = [{
    'fname': 'Anbu',
    'lname': 'Arasan',
    'phone': '190-309-6101',
    'email': 'anbu.arasan@email.com'
}, {
    'fname': 'Arivu',
    'lname': 'Mugilan',
    'phone': '490-701-7102',
    'email': 'arivu.mugilan@email.com'
}, {
    'fname': 'Bob',
    'lname': 'Johnson',
    'phone': '574-909-3948',
    'email': 'bob.johnson@email.com'
}, {
    'fname': 'Raja',
    'lname': 'Tamil',
    'phone': '090-909-0101',
    'email': 'raja.tamil@email.com'
}, {
    'fname': 'Sundar',
    'lname': 'Kannan',
    'phone': '090-909-0101',
    'email': 'sundar.kannan@email.com'
}]
```

**View**

- Create a class name **AddressBookView** inside app.js which is the V (view) part of MVC. Add an **init()** method in it.

```JavaScript
// ============== View ========================= 
class AddressBookView {
    init() {
        console.log("render HTML here");
    }
}
```

- Define **addressBookView** object by instantiating **AddressBookView** class.

```JavaScript
const addressBookView = new AddressBookView();
```

**Controller**

- Declare a **class** called **AddressBookCtrl** which would be the **controller**.

The rule of thumb is, the **Model** and **View** can never talk to each other directly and the **controller** is the only one that should communicate to both of them.

```JavaScript
//================ Controller ================== 
class AddressBookCtrl {
    constructor(addressBookView) {
        this.addressBookView = addressBookView;
    }
    init() {
        this.addressBookView.init();
    }
}
```

- Make an **addressBookCtrl** object by instantiating **AddressBookCtrl** class and pass the addressBookView object as an argument to it like so. (Dependency Injection)

```JavaScript
const addressBookApp = new AddressBookCtrl(addressBookView);
```

- Start the application by invoking **init()** method of addressBookApp.

```JavaScript
addressBookApp.init();
```

When **addressBookApp.init()** method is invoked, addressBookView**.init()** method is to be fired and we will see the output message **render HTML here** on the browser console.

At this stage, you have successfully hooked the Controller and View. 🙂

###Get All Contacts To The View

- Get all the model data by declaring getContacts() method inside AddressBookCtrl class.

```JavaScript
getContacts() {
    return contactsData;
}
```

- Declare renderContactListModule().

```JavaScript
renderContactListModule() {
    //get all contacts and assign to contacts 
    const contacts = addressBookApp.getContacts();

    // cache #contact-list DOM 
    const $contactListUI = document.getElementById('contact-list');

    // clear HTML from the DOM 
    $contactListUI.innerHTML = '';

    for (let i = 0, len = contacts.length; i < len; i++) {
        let $li = document.createElement('li');
        $li.setAttribute('class', 'contact-list-item');
        $li.setAttribute('data-index', i);
        $li.innerHTML = `${contacts[i]['fname']},${contacts[i]['lname']}`;
        $contactListUI.append($li);
    }
}
```

Inside **renderContactListModule()** method, get the model data by invoking getContacts() method on addressBookApp.

Then, get a DOM reference to the contact list element and store it in $contactListUI.

After that, clear HTML from the $contactListUI if there were any previously.

Loop through the model data and create li element inside it. and set two attributes to it, which are class and data-index.

The value of data-index attribute holds an incremental integer on every iteration.

Finally, set the actual first name and last name data to the list item using HTML property and append li to ul.

- Invoke renderContactListModule().

Call renderContactListModule() inside **addressBookView.init()** method.

```JavaScript
init() {
    this.renderContactListModule();
}
```

At this stage, you will be able to see all the contacts on the browser.

###Get Selected Contact

Add the contact details module HTML code in **index.html** file.

```HTML
<!-- contact item details module   -->
<section>
    <div id="contact-item-details"> loading... </div>
</section>
```

- Go back to **app.js** and add an Event listener to **li** element inside **renderContactListModule()** method before appending **li** to **ul**.

```JavaScript
$li.addEventListener("click", this.renderContactDetailsModule);
```

- Define **renderContactDetailsModule()** callback function inside **AddressBookView** class.

```JavaScript
renderContactDetailsModule(e) {
    let selectedIndex = null;
    if (typeof e === 'object') {
        e.stopPropagation();
        selectedIndex = this.getAttribute('data-index')
    } else {
        selectedIndex = e;
    }

}
```

To make this call back function more accessible, define selectedIndex and set its initial value to null.

Check to see how the callback is called, either click event or just invoke it from somewhere using typeof. If it’s an object then it’s being called by a click event.

In that case, get the value of the data-index attribute from the clicked li and assign it to selectedIndex.

If e parameter is an object, you will need to use e.stopPropagation() in order to avoid event bubbling.

>*stopPropagation() will prevent parent click event when child clicks event trigger. Later, we will be adding a couple of child elements like edit and delete buttons inside li. At that time, we do not want to parent click event trigger (li) when we click the child click event (edit button).*

- Add the getContact() method to our AddressBookCtrl class.

```JavaScript
getContact(index) {
    return contactsData[index];
}
```

This function simply takes an index value and get returns the object from the contactsData, based on the index value.

- Get selected item using getContact() inside renderContactDetailsModule().

```JavaScript
const selectedItem = addressBookApp.getContact(selectedIndex);
```

- Obtain the DOM reference for details view and set the selected items data to it inside renderContactDetailsModule() as well.

```JavaScript
const $ContactItemUI = document.getElementById('contact-item-details');
```

At this stage, the renderContactDetailsModule() function should look like this:

```JavaScript
renderContactDetailsModule(e) {
    let selectedIndex = null;
    if (typeof e === 'object') {
        e.stopPropagation();
        selectedIndex = this.getAttribute('data-index')
    } else {
        selectedIndex = e;
    }

    const selectedItem = addressBookApp.getContact(selectedIndex);
    const $ContactItemUI = document.getElementById('contact-item-details');
    $ContactItemUI.innerHTML = `${selectedItem['fname']} <br> ${selectedItem['lname']} <br> ${selectedItem['phone']} <br> ${selectedItem['email']}`;

}
```

- Add the CSS rule for the details element inside style.css.

```JavaScript
/* Contact Item Details Module */
#contact-item-details {
    float: left;
    width: 200px;
    background: #333;
    overflow: auto;
    color: white;
    padding: 10px;
    margin-left: 1px;
}
```

- Highlight selected item by declaring hightlightCurrentListItem() inside AddressBookView class.

```JavaScript
hightlightCurrentListItem(selectedIndex) {
    const $ContactListItems = document.getElementsByClassName('contact-list-item');
    for (let i = 0, len = $ContactListItems.length; i < len; i++) {
        $ContactListItems[i].classList.remove('active');
    }
    $ContactListItems[selectedIndex].classList.add("active")
}
```

Invoke it inside renderContactDetailsModule(e) function.

That should do it!

###Add New Contact

- Create <section> element with an id=”add-contact-module“ inside index.html file. This element is going to have ALL the HTML code that belongs to Add Contact Module.

```HTML
<section id="add-contact-module">
</section>
```

Then, add two elements inside it. The first one is an add button and the second one is an add a contact form.

```HTML
<section id="add-contact-module">
    <button id="open-add-contact-form-btn">+</button>
    <form>
        <h2>Add Contact</h2>
        first name:<br>
        <input type='text' data-key='fname' class='add-contact-input'><br>
        last name:<br>
        <input type='text' data-key='lname' class='add-contact-input'><br>
        phone:<br>
        <input type='text' data-key='phone' class='add-contact-input'><br>
        email:<br>
        <input type='text' data-key='email' class='add-contact-input'><br>
        <button type='button' id="add-contact-btn">add</button>
    </form>

</section>
```

- Add the CSS code inside style.css which will open up the add contact form when mousing over the add contact button.

```JavaScript
#add-contact-module {
    display: inline-block;
    margin-bottom: 1px;
    margin-left: 8px;
}

#add-contact-module #open-add-contact-form-btn {
    background: #54bb7d;
    font-size: 1.5em;
    color: white;
    padding-bottom: 5px;
}

#add-contact-module form {
    position: absolute;
    padding: 10px;
    width: 150px;
    background-color: #e1e1e1;
    border: 1px solid #999;
    display: none;
}

#add-contact-module form input {
    width: 97%;
    margin: 2px 0;
}

#add-contact-module form button {
    background: #54bb7d;
    font-size: 1em;
    padding: 0px 10px;
    color: white;
    margin-top: 10px;
}

#add-contact-module:hover form {
    display: block;
}
```

- AddContact() method will take the new contact object from the View and push it to the contactsData model array.

```JavaScript
// ============== Controller (API) =========================

class AddressBookCtrl {

    constructor(addressBookView) {
        this.addressBookView = addressBookView;
    }

    init() {
        this.addressBookView.init();
    }

    getContacts() {
        return contactsData;
    }

    getContact(index) {
        return contactsData[index];
    }

    addContact(contact) {
        contactsData.push(contact);
    }

}
```

- Declare addContactModule() inside AddressBookView class

```JavaScript
addContactModule() {
   const $addContact = document.getElementById('add-contact-btn');
   $addContact.addEventListener("click", this.addContactBtnClicked.bind(this));
}
```

Inside it, get a DOM reference to add the contact button and attach a click event to it with a callback function.

- Create addContactBtnClicked() function.

```JavaScript
addContactBtnClicked() {

    // get the add contact form inputs 
    const $addContactInputs = document.getElementsByClassName('add-contact-input');

    // this object will hold the new contact information
    let newContact = {};

    // loop through View to get the data for the model 
    for (let i = 0, len = $addContactInputs.length; i < len; i++) {

        let key = $addContactInputs[i].getAttribute('data-key');
        let value = $addContactInputs[i].value;
        newContact[key] = value;
    }

    // passing new object to the addContact method 
    addressBookApp.addContact(newContact);

    // render the contact list with the new data set
    this.renderContactListModule();

}
```

Inside it, get an array of input elements and loop through them. Create an object by setting the key from the attribute data-key of the input element and the value from the value of the input element on each iteration.

Then, invoke addContact() by passing the object as an argument which will add it to the contactsData model array.

Then, invoke renderContactListModule() method to re-render the View after the new data has been added.

- Finally, call addContactModule() inside init() method on the AddressBookView class.

```JavaScript
init() {
  this.renderContactListModule();
  this.renderContactDetailsModule(0);
  this.addContactModule();
}
```

At this stage, you should have an add contact functionality working.