Skip to content
Permalink
Browse files
Merge Conflict
  • Loading branch information
aa7401 committed Nov 28, 2019
2 parents 5c8d482 + 023d6e4 commit 2b870277a87b6fde19e5661471f58741c2d6cbaf
Show file tree
Hide file tree
Showing 7 changed files with 252 additions and 3 deletions.
@@ -8,7 +8,7 @@
"jest": true
},
"parserOptions": {
"ecmaVersion": 8
"ecmaVersion": 2018
},
"rules": {
"arrow-body-style": 2,
@@ -30,7 +30,7 @@
"max-lines-per-function": ["error", 25],
"max-nested-callbacks": ["error", 4],
"max-params": ["error", 5],
"max-statements": ["error", 10],
"max-statements": ["error", 15],
"no-cond-assign": 2,
"no-dupe-args": "error",
"no-dupe-keys": "error",
@@ -118,4 +118,24 @@ Continuous integration is the process of regularly merging code changes into the

Committing and pushing to trigger the workflow.

When running CI you need to run headless.
## 3 Continuous Delivery

Once you have mastered the deployment process and configuring your CI pipeline you will want to implement a full CD workflow. This will require you to automate the deployment to one (or more) cloud servers. To achieve this you will need to add additional stages to your CI build to:

1. Deploy to a test server.
2. Run a suite of acceptance tests.
3. Deploy to the live server.

Whilst you will need to spend time configuring the build and ensuring that the correct stages of the pipeline run on the correct branches you will find the following snippet helpful:

```yaml
deploy-staging-server:
stage: staging-server
script:
- apt-get update -qy
- apt-get install -y ruby ruby-dev rubygems-integration
- gem install dpl
- dpl --provider=heroku --app=<YOUR APP NAME> --api-key=<YOUR KEY HERE>
only:
- master
```
@@ -59,12 +59,37 @@ e.target.parentElement.firstChild.innerHTML

## 2 Form Validation

Now you have mastered DOM manipulation its time to move onto one of the most common uses of client-side JavaScript, form validation. The base code can be found in the `exercises/05_client/contacts/` directory. In the previous example we opened the `index.html` file using the `files://` protocol by dragging the html file into the chrome browser. Since we will be implementing some features that don't support this we need to access the page over `http://`. One way would be to create a koa script that serves static content however a quick and simple solution is to use the [`serve`](https://www.npmjs.com/package/serve) package. This needs to be installed and run from the `contacts/` directory.

```javascript
$ npm install serve
$ ./node_modules/.bin/serve
┌──────────────────────────────────────────────────┐
│ │
│ Serving!
│ │
- Local: http://localhost:5000 │
- On Your Network: http://10.17.114.20:5000 │
│ │
│ Copied local address to clipboard!
│ │
└──────────────────────────────────────────────────┘
```

Now you can paste the url into the chrome browser to access the page over http! Since the code is loaded by the browser you don't need to restart this server after making changes to the code. Remember to stop the server using `ctrl+c` when you have stopped working on the site.

Handling form validation

Revealing sections of the form

Preventing form submission

### 2.1 Data Persistence

If you close your browser down and re-open it you will see that the contacts you added have been persisted! The data is stored within the browser using a document database techology called [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Basic_Concepts_Behind_IndexedDB).

Viewing the data.

## 3 Making API Calls

In this section you will learn how to retrieve data from an API, specifically we will be covering:
@@ -10,3 +10,6 @@ node index.js&
node_modules/.bin/jest --runInBand --detectOpenHandles acceptance\ tests/*
read -p "Press enter to continue"
kill %1

# if you are running on unix and the node process is not being destroyed by the kill %1 command above then you can try:
#pkill node
@@ -0,0 +1,42 @@

<!doctype html>

<html lang="en">
<head>
<meta charset="utf-8">

<title>Contacts</title>
<meta name="description" content="Contacts">
<meta name="author" content="Mark J Tyers">
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
</head>
<body>
<main>
<header>
<h1>Contacts</h1>
</header>
<nav>
<h2>My Contacts</h2>
<ol>
<li>Mr Ted</li>
<li>Sir Moo</li>
</ol>
</nav>
<article>
<form>
<input type="hidden" name="key" />
<p><label for="name">Name</label>
<input name="name" type="text" autofocus /></p>
<p><label for="email">Email</label>
<input name="email" type="email" /></p>
<p>
<button id="submit" type="submit">Add Record</button>
<button id="delete" type="submit">Delete Record</button>
</p>
</form>
<table></table>
</article>
</main>
</body>
</html>
@@ -0,0 +1,101 @@

'use strict'

const minNameLength = 3
const minEmailLength = 10

document.addEventListener('DOMContentLoaded', async() => getAll())

function selectContact(e) {
// We access the id attribute of the link we clicked
const key = e.target.getAttribute('id')
// And use this to retrieve the relevant record
const data = JSON.parse(localStorage.getItem(key))
// Now we insert the data into the form
document.querySelector('input[name="name"]').value = data.name
document.querySelector('input[name="email"]').value = data.email
document.querySelector('input[name="key"]').value = key
// now we change the text on the button and rewire the event handler
document.querySelector('button#submit').innerHTML = 'Update Record'
document.querySelector('button#submit').onclick = updateContact
// finally we reveal the delete button
document.querySelector('button#delete').classList.remove('hidden')
return false
}

function updateContact() {
// we grab all the data from the form's input boxes into an object
const data = Array.from(document.querySelectorAll('input')).reduce( (acc, val) => {
if(val.name !== 'key') acc[val.name] = val.value
return acc
}, {})
// next we get the record key stored in the hidden field
const key = document.querySelector('input[name="key"]').value
// and finally store the updated data using the same key
localStorage.setItem(key, JSON.stringify(data))
// lastly we reload the contact list
getAll()
return false
}

function deleteContact() {
// we get the record key stored in the hidden field
const key = document.querySelector('input[name="key"]').value
// and delete that object from localstorage
localStorage.removeItem(key)
// lastly we reload the contacts list
getAll()
return false
}

function clearForm() {
// we delete the data from all the input boxes
document.querySelector('input[name="name"]').value = ''
document.querySelector('input[name="email"]').value = ''
// return the cursor focus to the name field
document.querySelector('input[name="name"]').focus()
// change the button text
document.querySelector('button').innerHTML = 'Add Record'
// wire the button to the add function
document.querySelector('button').onclick = addContact
document.querySelector('button#delete').onclick = deleteContact
// hide the delete button
document.querySelector('button#delete').classList.add('hidden')
}

function addContact() {
if (document.querySelector('input[name="name"]').value.length < minNameLength) return false
if (document.querySelector('input[name="email"]').value.length < minEmailLength) return false
const data = Array.from(document.querySelectorAll('input')).reduce( (acc, val) => {
if(val.name !== 'key') acc[val.name] = val.value
return acc
}, {})
const key = getHash()
localStorage.setItem(key, JSON.stringify(data))
getAll()
return false
}

function getAll() {
const items = {...localStorage} // spread operator to return all keys
const list = document.querySelector('ol')
list.innerHTML = ''
for(const key in items) {
const listItem = document.createElement('li')
const link = document.createElement('a')
link.onclick = selectContact
link.setAttribute('href', '#')
link.setAttribute('id', key)
link.innerHTML = JSON.parse(items[key]).name
listItem.appendChild(link)
list.appendChild(listItem)
}
clearForm()
}

function getHash() {
const hashLen = 36
const keyLen = 8
const offset = 2
return Math.random().toString(hashLen).substr(offset, keyLen)
}
@@ -0,0 +1,58 @@

body {
font-family: verdana;
}

table {
margin: 20px;
border-spacing: 0;
}

td {
border: 1px solid grey;
margin: 0;
padding: 5px;
}

header {
height: 120px;
background-color: #999;
}

h1, h2 {
padding: 20px;
}

h2 {
font-size: 1.2em
}

/* button#delete {
visibility: hidden;
} */

.hidden {
visibility: hidden;
}

form {
background-color: #666;
padding: 20px;
padding-left: 220px;
}

main {
width: 800px;
background-color: yellow;
margin: auto;
}

nav {
width: 200px;
background-color: grey;
float: left;
}

article {
background-color: #ccc;
}

0 comments on commit 2b87027

Please sign in to comment.