From 7aa9c51104469734878f446981641b59dd8b30d8 Mon Sep 17 00:00:00 2001 From: Denis Monnerat Date: Fri, 13 Feb 2026 11:16:49 +0100 Subject: [PATCH] tp4 --- R4.01_R4.A.10/td_tp/tp4/README.md | 47 ++++++++++ R4.01_R4.A.10/td_tp/tp4/aide.md | 35 ++++++++ R4.01_R4.A.10/td_tp/tp4/src/ex1/css/style.css | 86 +++++++++++++++++++ R4.01_R4.A.10/td_tp/tp4/src/ex1/img/puff.svg | 13 +++ R4.01_R4.A.10/td_tp/tp4/src/ex1/index.html | 28 ++++++ R4.01_R4.A.10/td_tp/tp4/src/ex1/module/app.js | 7 ++ .../td_tp/tp4/src/ex1/module/controller.js | 26 ++++++ .../td_tp/tp4/src/ex1/module/helpers.js | 12 +++ .../td_tp/tp4/src/ex1/module/model.js | 9 ++ .../td_tp/tp4/src/ex1/module/view.js | 44 ++++++++++ 10 files changed, 307 insertions(+) create mode 100644 R4.01_R4.A.10/td_tp/tp4/README.md create mode 100644 R4.01_R4.A.10/td_tp/tp4/aide.md create mode 100644 R4.01_R4.A.10/td_tp/tp4/src/ex1/css/style.css create mode 100644 R4.01_R4.A.10/td_tp/tp4/src/ex1/img/puff.svg create mode 100644 R4.01_R4.A.10/td_tp/tp4/src/ex1/index.html create mode 100644 R4.01_R4.A.10/td_tp/tp4/src/ex1/module/app.js create mode 100644 R4.01_R4.A.10/td_tp/tp4/src/ex1/module/controller.js create mode 100644 R4.01_R4.A.10/td_tp/tp4/src/ex1/module/helpers.js create mode 100644 R4.01_R4.A.10/td_tp/tp4/src/ex1/module/model.js create mode 100644 R4.01_R4.A.10/td_tp/tp4/src/ex1/module/view.js diff --git a/R4.01_R4.A.10/td_tp/tp4/README.md b/R4.01_R4.A.10/td_tp/tp4/README.md new file mode 100644 index 0000000..f28ac65 --- /dev/null +++ b/R4.01_R4.A.10/td_tp/tp4/README.md @@ -0,0 +1,47 @@ +# TP javascript : Ajax, promesses. + +Vous disposez tous, à la racine de votre compte, d'un répertoire `public_html` qui est servi (http/https) par le serveur dwarves. +Pour accéder à vos pages, utilisez l'url : + +> http(s)://dwarves.[arda|iut-fbleau.fr]/~login + +Pour tester les différentes api http, vous pouvez utilisez la commande `curl`. + + +#### Ex1 +Le but est de faire une recherche de film en utilisant l'api [OMDb](https://www.omdbapi.com/) (Open Movie Database). + +![movie](img/search.png?style=centerme) + +Son utilisation nécessite une clé. Vous pouvez utiliser la mienne `2fcb2848`. Si +la limite de requêtes est atteinte, générez votre propre clé. + + +Complétez le [code](./src/ex1). +- J'ai volontairement utilisé des modules. Il vous faut donc tester l'application avec +http. + +- Écrire la fonction du modèle + ```js + getMovies(search) + ``` + Cette fonction renvoie une promesse qui permet de faire une requête ajax vers l'api OMDb. Pour ajax, + utilisez d'abord l'interface `fetch`, puis l'objet `XMLHttpRequest`. + +- Ecrire la fonction du contrôleur + ```js + async search(searchMovie) + ``` + qui permet de faire une recherche. Cette fonction utilise évidemment la fonction précédente `getMovies`. + +- Ecrire la fonction de la vue + ```js + renderList(movies) + ``` + quo construit la liste des films affichés. La structure attendue est + ```html +
  • + The Godfather1972 +
  • + ``` + diff --git a/R4.01_R4.A.10/td_tp/tp4/aide.md b/R4.01_R4.A.10/td_tp/tp4/aide.md new file mode 100644 index 0000000..80bb5dc --- /dev/null +++ b/R4.01_R4.A.10/td_tp/tp4/aide.md @@ -0,0 +1,35 @@ +#### XMLHttpRequest +```js +let xhr = new XMLHttpRequest() +xhr.open("GET","mon/url") + +xhr.onload = ... +xhr.onerror = ... + +xhr.send() +``` +#### Promise +```js + +let promise = new Promise ((resolve,reject) => { + setTimeout(() => { + resolve("OK") + },1000) + }) +promise.then(data => ....) +``` + +#### async/await + +```js + +function getUser(email){ + return new Promise(...) +} + +async function updateUser(){ + + let user = await getUser(...) + console.log(user) +} +``` diff --git a/R4.01_R4.A.10/td_tp/tp4/src/ex1/css/style.css b/R4.01_R4.A.10/td_tp/tp4/src/ex1/css/style.css new file mode 100644 index 0000000..995cde0 --- /dev/null +++ b/R4.01_R4.A.10/td_tp/tp4/src/ex1/css/style.css @@ -0,0 +1,86 @@ +main { + position: absolute; + top: 2rem; + left: 50%; + transform: translate(-50%, 0); +} +.loader { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + + margin: 6rem 0 0; +} +.error { + color: #FFFAAA; + margin: 1rem 0; +} +label { + align-items: center; + display: flex; + flex-direction: column; + font-size: 1.6rem; +} +label span { + text-shadow: 1px 1px 2px rgba(0,0,0,0.8); +} +input { + margin: 1rem 0 0; + font-size: 1.6rem; + font-weight: 300; + padding: 0.8rem 1rem; + color: white; + border: 1px solid rgba(255, 255, 255, 0.05); + background: rgba(255, 255, 255, 0.05); + transition: all 0.3s; + box-shadow: 1px 1px 2px rgba(0,0,0, 0.3); + -moz-appearance:none; + -webkit-appearance:none; + outline: none; +} +input:focus { + border: 1px solid transparent; + background: rgba(255, 255, 255, 0.08); +} +ul { + padding: 0; + margin: 1rem 0 2rem; +} +ul li { + padding: 0.6rem 1rem; + margin: 1px 0; + line-height: 1.4rem; + display: flex; + justify-content: space-between; + align-items: center; + align-content: space-between; + text-shadow: 1px 1px 2px rgba(0,0,0,0.8); + background: rgba(255, 255, 255, 0.08); + box-shadow: 0 0 2px rgba(0,0,0, 0.3); + box-sizing: border-box; +} +ul li:hover, +ul li:active, +ul li:focus { + background: rgba(255, 255, 255, 0.1); +} +ul li a { + margin: 0 0.6rem 0 0; + text-decoration: none; + color: white; +} +ul li span { + opacity: 0.5; +} + +body { + margin: 0; + font-family: 'Helvetica Neue', Helvetica, Arial; + font-weight: 300; + background-size: cover; + background-attachment: fixed; + background-image: -webkit-radial-gradient(ellipse farthest-corner at top, #661141, #000000); + background-image: radial-gradient(ellipse farthest-corner at top, #661141, #000000); + color: white; +} diff --git a/R4.01_R4.A.10/td_tp/tp4/src/ex1/img/puff.svg b/R4.01_R4.A.10/td_tp/tp4/src/ex1/img/puff.svg new file mode 100644 index 0000000..d598440 --- /dev/null +++ b/R4.01_R4.A.10/td_tp/tp4/src/ex1/img/puff.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/R4.01_R4.A.10/td_tp/tp4/src/ex1/index.html b/R4.01_R4.A.10/td_tp/tp4/src/ex1/index.html new file mode 100644 index 0000000..ce5e1aa --- /dev/null +++ b/R4.01_R4.A.10/td_tp/tp4/src/ex1/index.html @@ -0,0 +1,28 @@ + + + + + + + + + + + +
    + + +
    + +
    + +

    +
    +
    +
    + + + diff --git a/R4.01_R4.A.10/td_tp/tp4/src/ex1/module/app.js b/R4.01_R4.A.10/td_tp/tp4/src/ex1/module/app.js new file mode 100644 index 0000000..e57610b --- /dev/null +++ b/R4.01_R4.A.10/td_tp/tp4/src/ex1/module/app.js @@ -0,0 +1,7 @@ + +import model from '../module/model.js' +import Controller from '../module/controller.js' +import View from '../module/view.js' + +const view = new View() +const app = new Controller(view,model) diff --git a/R4.01_R4.A.10/td_tp/tp4/src/ex1/module/controller.js b/R4.01_R4.A.10/td_tp/tp4/src/ex1/module/controller.js new file mode 100644 index 0000000..31a82f5 --- /dev/null +++ b/R4.01_R4.A.10/td_tp/tp4/src/ex1/module/controller.js @@ -0,0 +1,26 @@ +class Controller { + constructor(view,model){ + this.view = view + this.model = model + + this.loading = false + this.lastSearch = null + this.error = null + this.results = [] + + this.view.setLoading(false) + this.view.bindSearch(this.search.bind(this)) + } + reset() { + this.loading = false + this.error = null + this.results = [] + } + + async search(searchMovie) { + // TODO + } +} + +export default Controller + diff --git a/R4.01_R4.A.10/td_tp/tp4/src/ex1/module/helpers.js b/R4.01_R4.A.10/td_tp/tp4/src/ex1/module/helpers.js new file mode 100644 index 0000000..ddedc6c --- /dev/null +++ b/R4.01_R4.A.10/td_tp/tp4/src/ex1/module/helpers.js @@ -0,0 +1,12 @@ + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. + function debounce(fn, wait) { + let timeout + + return (...args) => { + clearTimeout(timeout) + timeout = setTimeout(() => fn(...args), wait) + } + } +export default debounce diff --git a/R4.01_R4.A.10/td_tp/tp4/src/ex1/module/model.js b/R4.01_R4.A.10/td_tp/tp4/src/ex1/module/model.js new file mode 100644 index 0000000..bce1c69 --- /dev/null +++ b/R4.01_R4.A.10/td_tp/tp4/src/ex1/module/model.js @@ -0,0 +1,9 @@ +let apiKey = '2fcb2848' + +let model = { + getMovies(search){ + // TODO + } +} + +export default model diff --git a/R4.01_R4.A.10/td_tp/tp4/src/ex1/module/view.js b/R4.01_R4.A.10/td_tp/tp4/src/ex1/module/view.js new file mode 100644 index 0000000..b339bbb --- /dev/null +++ b/R4.01_R4.A.10/td_tp/tp4/src/ex1/module/view.js @@ -0,0 +1,44 @@ +import debounce from "./helpers.js" + +class View { + constructor(){ + this.listMovies = document.querySelector("#list-movies") + this.inputSearch = document.querySelector("input") + this.loader = document.querySelector(".loader") + this.message = document.querySelector("p.error") + } + + #getInput(){ + return this.inputSearch.value + } + + setLoading(loading){ + if (loading) + this.loader.style.display = "block" + else + this.loader.style.display = "none" + } + + renderMessage(error){ + this.message.style.display = "block" + this.message.textContent = error + } + + renderList(movies){ + let ul = document.createElement("ul") + + movies.forEach((movie)=>{ + // TODO + }) + + this.listMovies.replaceChildren(ul) + } + + bindSearch(handler){ + this.inputSearch.addEventListener("input",debounce((e)=>{ + handler(this.#getInput()) + },500)) + } +} + +export default View