#Session 13
Final session
Promises
##Type Ahead
See completed sample. Note city AND state name matching.
###fetch()
fetch() - No call back function but returns a promise. We DO NOT use a callback function:
fetch(endpoint, function(data){
console.log(data)
})Instead:
const cities = [];
const promise = fetch(endpoint)
console.log(promise)
Doesn't return the data but a promise.
const cities = [];
fetch(endpoint).then(blob => console.log(blob))The browser doesn't know what kind of data blob is. (Image, audio, etc.). Note the prototypes - json
const cities = [];
fetch(endpoint)
.then(blob => blob.json())
// returns another promise
.then(data => console.log(data))Returns the complete array.
We could try:
const cities = [];
fetch(endpoint)
.then(blob => blob.json())
// returns another promise
.then(data => cities = data)But this wants to overwrite a constant variable.
Use .push and we end up with an array inside an array:
const cities = [];
fetch(endpoint)
.then(blob => blob.json())
.then(data => cities.push(data))We want everything to have its own item in the array.
In the console:
> cities.push(1,2,3,4)
Let's use a spread operator.
###Spread Operator
Takes every element from an array (or any iterable) and applies it to a containing array.
See: micro/spread-operators.js
const cities = [];
fetch(endpoint)
.then(blob => blob.json())
.then(data => cities.push(...data))###Filtering the array
We need to create a function that will filter the array to a subset.
See: micro/arrays-filter.js
function findMatches(wordToMatch, cities){
return cities.filter(place => {
// does the city or state match wordToMatch?
})
}The match() method searches a string for a match against a regular expression, and returns the matches, as an Array object.
e.g. we cannot do this:
function findMatches(wordToMatch, cities){
return cities.filter(place => {
return place.city.match(/wordToMatch/i)
});
}Putting a variable into a regex:
function findMatches(wordToMatch, cities){
return cities.filter(place => {
const regex = new RegExp(wordToMatch, 'gi'); //global / entire string, case insensitive
return place.city.match(regex) || place.state.match(regex);
});
}> findMatches('new', cities)
###Displaying the matches
Create our function, variables for dom elements and bind the change event to the search input:
function displayMatches(){
console.log(this.value);
}
const searchInput = document.querySelector('.search');
const suggestions = document.querySelector('.suggestions');
searchInput.addEventListener('change', displayMatches);Only displays when clicked outside, add:
searchInput.addEventListener('keyup', displayMatches);Call findMatches and pass our information:
function displayMatches(){
const matchArray = findMatches(this.value, cities);
console.log(matchArray);
}####Use mapping
Takes in an array, does something with it, then returns a new array with (unlike filter()) the same length.
See micro/arrays-filter.js
Create an html snippet from the matches:
function displayMatches(){
const matchArray = findMatches(this.value, cities);
const html = matchArray.map(place => {
return `
<li>
<span class="name">${place.city}, ${place.state}</span>
<span class="population">${place.population)</span>
</li>
`;
})
}Add it to the dom:
function displayMatches(){
const matchArray = findMatches(this.value, cities);
const html = matchArray.map(place => {
return `
<li>
<span class="name">${place.city}, ${place.state}</span>
<span class="population">${place.population}</span>
</li>
`;
});
suggestions.innerHTML = html;
}Join the array elements together:
function displayMatches(){
const matchArray = findMatches(this.value, cities);
const html = matchArray.map(place => {
return `
<li>
<span class="name">${place.city}, ${place.state}</span>
<span class="population">${place.population}</span>
</li>
`;
}).join('');
suggestions.innerHTML = html;
}##Formatting
The highlighting - use replace() string method. (Docs)[http://www.w3schools.com/jsref/jsref_replace.asp]
function displayMatches(){
const matchArray = findMatches(this.value, cities);
const html = matchArray.map(place => {
//below
const regex = new RegExp(this.value, 'gi');
const cityName = place.city.replace(regex, `<span class="hl">${this.value}</span>`);
return `
<li>
<span class="name">${cityName}, ${place.state}</span>
<span class="population">${place.population}</span>
</li>
`;
}).join('');
suggestions.innerHTML = html;
}Do the same for state:
function displayMatches(){
const matchArray = findMatches(this.value, cities);
const html = matchArray.map(place => {
//below
const regex = new RegExp(this.value, 'gi');
const stateName = place.state.replace(regex, `<span class="hl">${this.value}</span>`);
const cityName = place.city.replace(regex, `<span class="hl">${this.value}</span>`);
return `
<li>
<span class="name">${cityName}, ${stateName}</span>
<span class="population">${place.population}</span>
</li>
`;
}).join('');
suggestions.innerHTML = html;
}Add a comma to the population (stack overflow):
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}<span class="population">${numberWithCommas(place.population)}</span>