Source code breakdown

First to get started I'll create a index.html and main.js file. The HTML file is the main entry point for the application which will load the JavaScript from the main js file.

JQuery, Bootstrap and Chance.js

First I'll be using Bootstrap styling for the application referenced from their CDN. This way all my elements are styled automatically using standard guidelines.

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

For JQuery I can use the script referencing the Google AJAX API like so:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

Also, I load in the Bootstrap script before my closing HTML tag:

<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>

To generate unique identifiers like issue tags on GitHub, I'll be using an external library known as ChanceJS by including the following script tag:

<script src="http://chancejs.com/chance.min.js"></script>

Deploying a live server

For development I'm using live-server which can be installed using npm. It's essentially a small server that I can use to deploy my application and reload whenever I make changes. Once it is installed, I can start the server and run my application by typing live-server in command prompt/shell.

$ npm install -g live-server

You can find the documentation about live-server below. It's good to hacking together a website for development but definitely not for hosting production websites. Moreover, you can support HTTPS if you want using a server key and certificate to encrypt traffic for testing.

index.html: Implementing the application UI

At the moment my UI is pretty barebones and has nothing in it, so the app is empty. First, let's look at the head of my html file.

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">

So these three meta tags sit at the top of my head container.

Charset

To display the HTML page properly, the web browser needs to know what character set it should use. This tag specifies to use utf-8 which contains all the characters and symbols you typically see all over the internet.

HTTP Equiv

The HTTP Equiv meta tag provides a HTTP header served for edge.

Viewport

The viewport defines the visible area for the user, which is determined by what screen they're using. The dimensions of the viewport therefore changes depending on the device; ranging from large viewports on computers to smaller ones on mobile screens. Therefore, to adapt the content sizing for a responsive website - defining the scale of the content depending on the viewport size helps us deliver a better experience across different devices.

Here the meta tag allows us to take control of the viewport and give instructions to the browser on how to control the page dimensions and sizing of elements displayed.

IDs

<!-- Unique IDs for input allows JS to reference elements in the DOM -->
<div class="jumbotron">
    <h3>Add new issue:</h3>
    <form id="issueInput">
        <div class="form-group">
            <label for="issueDescription">Description</label>
            <input type="text" class="form-control" id="issueDescription" placeholder="Describe the issue...">
        </div>
        
        <div class="form-group">
            <label for="issueDescription">Priority</label>
            <select class="form-control" id="issuePriority">
                <option value="Low">Low</option>
                <option value="Med">Med</option>
                <option value="High">High</option>
            </select>
        </div>
        
        <div class="form-group">
            <label for="issueDescription">Assigned to</label>
            <input type="text" class="form-control" id="issueAssignment" placeholder="Enter username of who is responsible...">
        </div>
        
        <div class="form-group">
            <label for="issueDescription">Labels</label>
            <input type="text" class="form-control" id="issueLabel" placeholder="Label this issue for indexing">
        </div>
        
        <button type="submit" class="btn btn-primary">Add</button>
    </form>
</div>
<div class="row">
    <div class="col-lg-12">
        <div id="issuesList">
            <!-- Add issues here -->
        </div>
    </div>
</div>

Here I've created a form layout to let users enter new issues into the application. Each input method has a unique ID which you can see that allows me to directly reference the element in JavaScript later. Then I can retrieve the issue data from local storage, generate the HTML and then push that to desired position on the DOM easily.

In this case, my issues will exist inside of the issuesList as shown - so JS will take the information the user inputs into the above fields, and locate the list to display my issues. In this way, the form is dynamic and can grow/shrink in size depending on what the user does with the application.

main.js

Fetching issue data from Local Storage

The text data that the user enters should be stored in the Browser's Local Storage. So the event handler fetchIssues() will be handling retrieving that data from local storage. As you can see above, that is executed with the body tag in our HTML.

function fetchIssues() {
    var issues = JSON.parse(localStorage.getItem('issues')); // retrieve issue data from LS
    var issuesList = document.getElementById('issuesList'); // get reference to HTML list
    
    issuesList.innerHTML = ''; // create HTML syntax for element
    
    // generate HTML and fill the list with retrieved issues
    for (var i=0; i < issues.length; i++) {
        var issue = issues[i]; // retrieve issue instance
        
        // segment attributes of issue
        var id = issue.id;
        var desc = issue.description;
        var priority = issue.priority;
        var assignment = issue.assignment;
        var status = issue.status;
        
        // append issue statement to HTML
        issuesList.innerHTML += '<div class="well">' +
                                '<h6>Issue ID: ' + id + '</h6' +
                                '<p><span class="label label-info">' + status + '</span></p>' +
            
                                '<h3>Description' + desc + '</h3>' +
                                '<p><span class="glyphicon glyphicon-time"></span>' + priority + ' ' +
                                '<span class="glyphicon glyphicon-user"></span>' + assignment + '</p>' +    
                                '<a href="#" class="btn btn-warning" onclick="setStatusClosed(\'' + id + '\')">Close</a> ' +
                                '<a href="#" class="btn btn-danger" onclick="deleteIssue(\'' + id + '\')">Delete</a> ' +
                                '</div>';
    }
}

For the event handler, we first want to do the following:

  • Retrieve the data from Local Storage

  • Get reference to the issue list div in HTML

  • Generate the HTML for each issue

  • Push the HTML for the issues to the DOM

The first line of the event handler does this for us; localStorage.getItem('issues') and parses the string it gets into a JSON object. This allows us to thereafter split the attributes of the object into variables which we can use to create the HTML for the list below.

The reference to the list of issues in the HTML is retrieved in the second line; document.getElementById('issuesList') where I use the id associated earlier with the empty div as seen above. The HTML content inside that div is accessed using innerHTML which basically allows me to set the content to the converted data (or in this case append).

Here the process is basically done inside of a loop that goes over all the properties of the issue and adds the HTML output iteratively. After returning, this should reflect changes in the DOM where it can now load the page (remember we called this event handler at the <body> tag!)

Saving issue data to Local Storage

After the user interacts with the application by typing in their issue and hitting submit, I want the data to stay in Local Storage such that I can retrieve it after (using fetchIssues). In order to do so, I need to attach an event listener to the submit button that runs a subroutine when the button is clicked. This can be achieved by the following JavaScript:

document.getElementById('issueInput').addEventListener('submit', saveIssue);

What this is doing is:

  • Looking for the element in the DOM with the id #issueInput

  • Adds an event listener to the element

    • Arguments taking a string

    • and the name of the event listener (in this case the save handler function)

In other words, this is taking the addEventListener function attaches the submit event to the event handler saveIssue().

Let's look at the saveIssue function:

function saveIssue(e) {
    var issueID = chance.guid();
    var issueDesc = document.getElementById('issueDescription').value;
    var issuePriority = document.getElementById('issuePriority').value;
    var issueAssignment = document.getElementById('issueAssignment').value;
    var issueStatus = 'Open';
    
    var issue = {
        id: issueID,
        description: issueDesc,
        priority: issuePriority,
        assignment: issueAssignment,
        status: issueStatus
    }
    
    if (localStorage.getItem('issues') === null) {
        var issues = [];
        issues.push(issue);
    } else {
        var issues = JSON.parse(localStorage.getItem('issues'));
        issues.push(issue);
    }
    
    localStorage.setItem('issues', JSON.stringify('issues'));
    document.getElementById('issueInput').reset();
    
    fetchIssues();
    
    e.preventDefault();
}

The first thing I do is take the information that the user has entered into the HTML and store those values into variables. As for the issue ID, as previously mentioned, I'm using the Chance.js library to generate a unique id. This then forms a JSON object which I can push and save to local storage.

After this new issue is pushed (using setItem, we then want to clear the form which we can do so by using the reset() method on the form (issueInput). Immediately after, we then want to update the application to show the new state of the issue list by calling fetchIssues() to generate a new list with the newly added issue. Finally, we also use preventDefault() to ensure that the default event for 'Submit' is happening when the user clicks the button.

Setting closed status for issues

Earlier in the fetch Issues, I had a button that would set an issue to 'Closed', which should remove that issue from the list. In order to support that functionality, I'll again need to write a function in JavaScript to do the legwork behind that feature.

Since I already attached an onclick event to the button in the HTML to trigger a function (recall the code for the element was as shown below).

<a href="#" class="btn btn-warning" onclick="setStatusClosed(\''+id+'\')">Close</a>

So what I need now to is write that setStatusClosed function in JavaScript, which looks like this:

function setStatusClosed(id) {
    var issues = JSON.parse(localStorage.getItem('issues'));

    for (var i = 0; i < issues.length; i++) {
        if (issues[i].id == id) {
            issues[i].status = "Closed";
        }
    }

    localStorage.setItem('issues', JSON.stringify(issues));

    fetchIssues();
}

Looking at this function, first I'm taking in a parameter id as an argument so that I can directly identify which issue that I'm after. This will allow me to select the issue by simply going over the list and matching the ID which was generated using Chance.js mentioned prior.

After finding the specfic issue, I simply set the state of that issue to 'Closed' and update the list in local storage. Following that, I once again call fetchIssues event handler to regenerate that list of issues in HTML.

Deleting an issue

Next to the close issue option, I also added a button to delete issues. Clicking on this button should remove that issue from the list and also update those changes in local storage so the change is reflected across. Again, just like for closing the issue I had a button which has an onclick event to trigger the deleteIssue event handler. The code for this can be seen below:

function deleteIssue(id) {
    var issues = JSON.parse(localStorage.getItem('issues'));

    for (var i = 0; i < issues.length; i++) {
        if (issues[i].id == id) {
            issues.splice(i, 1);
        }
    }

    localStorage.setItem('issues', JSON.stringify(issues));

    fetchIssues();
}

The implementation for this is almost identical to setting an issue status to close, except that the issue instead of having its attribute status to closed, it is spliced out of the list (sliced, spliced, removed, etc). After which I again set this change in local storage and update the list by fetching the new state and pushing it to the DOM.

Last updated