@oliverturner
Director of Education @ConstructorLabs
1. Communication
2. Focus
3. Ability
import Model from "./model.mjs";
import View from "./view.mjs";
class App {
constructor(options) {
this.model = new Model(options.model);
this.view = new View(
Object.assign({}, options.view, {
handlers: { onSearch: this.onSearch.bind(this) }
})
);
this.initialise();
}
async initialise() {
const { ok, records, err } = await this.model.loadData();
ok ? this.view.display(records) : this.view.displayError(err);
}
onSearch(event) {
event.preventDefault();
const term = event.target.searchterm.value;
const results = this.model.filter(term);
this.view.display(results);
}
}
export default App;
class Model {
...
parseHTML(html) {
const div = document.createElement("div");
div.innerHTML = html.trim();
return div;
}
/**
* Grab the HTML of the target document via AJAX
* Return a queryable DOM node from the returned string
*/
fetchData() {
const { url, parser } = this.options;
return fetch(`https://cors-anywhere.herokuapp.com/${url}`, {
headers: { "X-Requested-With": "Panache" }
})
.then(res => (res.ok ? res.text() : Promise.reject(res.err)))
.then(str => parser(this.parseHTML(str)))
.catch(err => err);
}
filter(term) {
term = term.toLowerCase();
return this.state.records.filter(record =>
record.label.toLowerCase().includes(term)
);
}
}
export default Model;
// type Record = {href: string, label: string}
export default {
url: "whatsonnetflix.com/netflix-hacks/the-netflix-id-bible-every-category-on-netflix/",
parser: function(el) {
const nodeList = el.querySelectorAll(".articleBody p");
// Extract genre objects into an array, then use a dict to dedupe by id
// => {[id: string]: Record}
const records = Array.from(nodeList)
.filter(node => '/^\d{1,6} = [\w &;]+/'.test(node.textContent))
.reduce((acc, node) => {
const [id, label] = node.textContent.split(" = ");
const href = `http://www.netflix.com/browse/genre/${id}`;
return Object.assign({}, acc, { [id]: { href, label } });
}, {});
// Return an array of records alphabetically sorted by label
// => Record[]
return Object.values(records).sort((a, b) => b.label < a.label);
}
};
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Unfathomable</title>
<link rel="stylesheet" href="./styles.css">
</head>
<body>
<div class="app is-loading" id="app">
<form action="{URL}" class="search">
<div class="search__content">
<label for="searchterm" class="search__label">Search:</label>
<input id="searchterm" name="searchterm" class="search__term" autofocus="">
</div>
</form>
<div class="content"></div>
</div>
<script type="module">
import App from "./modules/index.js"
import model from "./modules/parsers/netflix.js"
const app = new App({ model, view: { root: document.querySelector("#app") } })
</script>
<script src="./no-module.js" defer nomodule></script>
</body>
</html>
Conclusion