Săptămâna trecută am decis să iau o nouă provocare. Am numit-o: The # 100Days100Proiecte Provocare.

Scopul provocării este de a crea un proiect în fiecare zi. Gândiți-vă la acesta ca la următorul pas pentru provocarea # 100DaysOfCode.

Un proiect poate fi:

  • o aplicatie
  • o componentă
  • un site web
  • un joc
  • o biblioteca
    și așa mai departe…

Limbajul de programare folosit nu este, de asemenea, important, dar trebuie să finalizez proiectul până la ora 23:59 (ora mea), altfel mă „pedepsesc” oferind 5 USD pentru 5 persoane (25 USD în total) – primele 5 persoane care subliniază pe Twitter că am ratat termenul limită. ?

Dacă doriți să vă înscrieți, puteți citi mai multe despre această provocare și despre celelalte variante pe care le are Aici.

Notă: nu trebuie să oferiți 5 dolari dacă eșuați, stabiliți doar o altă „pedeapsă” pentru dvs. De asemenea, există și alte variante cu mai puține zile (7Zile7Proiecte și 30Zile30Proiecte) dacă nu aveți chef să faceți provocarea 100Days.


Pentru primul proiect din # 100Days100Proiecte M-am gândit să lucrez cu un API public pentru a obține niște date care ar fi afișate într-o pagină web – un lucru obișnuit de făcut cu un API.

Pentru asta am ales să folosesc TheMealDBAPI-ul public pentru a obține câteva mese aleatorii apăsând un buton. Ceva simplu! ?

Consultați versiunea live a ceea ce vom construi în acest articol mai departe CodePen:

Ca întotdeauna să începem de la început:

Codul HTML

<div class="container">
	<div class="row text-center">
		<h3>
			Feeling hungry?
		</h3>
		<h5>Get a random meal by clicking below</h5>
		<button class="button-primary" id="get_meal">Get Meal ?</button>
	</div>
	<div id="meal" class="row meal"></div>
</div>

Avem un mic text, dar cele mai importante două părți sunt:

  • #get_meal buton și
  • #meal div

Vom folosi button pentru a face o cerere către API. Aceasta va trimite înapoi câteva date pe care le vom introduce în #meal div care acționează ca un container – în acest caz.

De obicei, după HTML, mă duc direct în CSS. Dar nu avem încă întregul markup, deoarece va fi populat în JavaScript secțiunea, deci asta vom face în continuare.

JavaScript

După cum sa menționat mai sus, avem nevoie de button și acel container div:

const get_meal_btn = document.getElementById('get_meal');
const meal_container = document.getElementById('meal');

Apoi, înainte de a ne scufunda mai mult în cod, să vedem ce va returna API-ul. Pentru aceasta, vă rugăm să deschideți următoarea adresă URL: https://www.themealdb.com/api/json/v1/1/random.php.

După cum puteți vedea din URL, primim un Aleatoriu masă din acest API (reîmprospătați pentru a vedea aleatoriu). Când facem un OBȚINE cerere către acel punct final (cum ar fi accesarea acestuia din browser), trimite înapoi un răspuns JSON, pe care îl putem analiza pentru a prelua datele dorite.

Datele arată cam așa:

{
	meals: [
		{
			idMeal: '52873',
			strMeal: 'Beef Dumpling Stew',
			strDrinkAlternate: null,
			strCategory: 'Beef',
			strArea: 'British',
			strInstructions: 'Long description',
			strMealThumb:
				'https://www.themealdb.com/images/media/meals/uyqrrv1511553350.jpg',
			strTags: 'Stew,Baking',
			strYoutube: 'https://www.youtube.com/watch?v=6NgheY-r5t0',
			strIngredient1: 'Olive Oil',
			strIngredient2: 'Butter',
			strIngredient3: 'Beef',
			strIngredient4: 'Plain Flour',
			strIngredient5: 'Garlic',
			strIngredient6: 'Onions',
			strIngredient7: 'Celery',
			strIngredient8: 'Carrots',
			strIngredient9: 'Leek',
			strIngredient10: 'Swede',
			strIngredient11: 'Red Wine',
			strIngredient12: 'Beef Stock',
			strIngredient13: 'Bay Leaf',
			strIngredient14: 'Thyme',
			strIngredient15: 'Parsley',
			strIngredient16: 'Plain Flour',
			strIngredient17: 'Baking Powder',
			strIngredient18: 'Suet',
			strIngredient19: 'Water',
			strIngredient20: '',
			strMeasure1: '2 tbs',
			strMeasure2: '25g',
			strMeasure3: '750g',
			strMeasure4: '2 tblsp ',
			strMeasure5: '2 cloves minced',
			strMeasure6: '175g',
			strMeasure7: '150g',
			strMeasure8: '150g',
			strMeasure9: '2 chopped',
			strMeasure10: '200g',
			strMeasure11: '150ml',
			strMeasure12: '500g',
			strMeasure13: '2',
			strMeasure14: '3 tbs',
			strMeasure15: '3 tblsp chopped',
			strMeasure16: '125g',
			strMeasure17: '1 tsp ',
			strMeasure18: '60g',
			strMeasure19: 'Splash',
			strMeasure20: '',
			strSource:
				'https://www.bbc.co.uk/food/recipes/beefstewwithdumpling_87333',
			dateModified: null
		}
	];
}

Practic primim înapoi o serie de meals, dar cu un singur articol – cel generat aleatoriu. Și acest articol conține toate datele pe care vrem să le prezentăm în mica noastră aplicație. Lucruri ca:

  • numele mesei (sub strMeal)
  • alimentatie (sub strCategory)
  • imaginea mesei (sub strMealThumb)
  • un videoclip YouTube cu rețeta (sub strYoutube)
  • ingredientele și măsurile (sub strIngredientsX și strMeasureX – X reprezintă al n-lea ingredient și este măsura). Acest lucru este puțin ciudat, așa cum m-aș aștepta să aibă aici o matrice cu aceste informații, dar aleg să o adauge ca elemente de recuzită. Bine …? Important de reținut este că există maximum 20 de ingrediente / măsuri, deși nu sunt completate toate – unele dintre ele ar putea fi goale, așa că trebuie să ne dăm seama de asta.

Acum că avem butonul, vom adăuga un ascultător de evenimente pentru click eveniment. În interior vom face o solicitare către API:

get_meal_btn.addEventListener('click', () => {
	fetch('https://www.themealdb.com/api/json/v1/1/random.php')
		.then(res => res.json())
		.then(res => {
			createMeal(res.meals[0]);
		})
		.catch(e => {
			console.warn(e);
		});
});

Folosim aduc API pentru a face cererea. Trebuie doar să trecem în adresa URL a API-ului pe care dorim să-l facem OBȚINE cerem și vom primi înapoi o promisiune.

Odată ce acest lucru este rezolvat, avem un răspuns (res). Acest res nu este încă în starea în care dorim să fie, așa că vom apela la .json() metoda pe ea. Apoi, în sfârșit, avem frumosul obiect. Ura! ?

După cum sa menționat mai sus, API returnează fișierul meals matrice, dar numai cu un element în el. Deci, vom trece acel element (la index 0) în a noastră createMeal funcție, pe care o vom defini în continuare.

Voi lipi întregul bloc de cod de mai jos și vom intra în detaliu după aceea, așa că țineți o secundă. ?

const createMeal = meal => {
	const ingredients = [];

	// Get all ingredients from the object. Up to 20
	for (let i = 1; i <= 20; i++) {
		if (meal[`strIngredient${i}`]) {
			ingredients.push(
				`${meal[`strIngredient${i}`]} - ${meal[`strMeasure${i}`]}`
			);
		} else {
			// Stop if there are no more ingredients
			break;
		}
	}

	const newInnerHTML = `
		<div class="row">
			<div class="columns five">
				<img src="${meal.strMealThumb}" alt="Meal Image">
				${
					meal.strCategory
						? `<p><strong>Category:</strong> ${meal.strCategory}</p>`
						: ''
				}
				${meal.strArea ? `<p><strong>Area:</strong> ${meal.strArea}</p>` : ''}
				${
					meal.strTags
						? `<p><strong>Tags:</strong> ${meal.strTags
								.split(',')
								.join(', ')}</p>`
						: ''
				}
				<h5>Ingredients:</h5>
				<ul>
					${ingredients.map(ingredient => `<li>${ingredient}</li>`).join('')}
				</ul>
			</div>
			<div class="columns seven">
				<h4>${meal.strMeal}</h4>
				<p>${meal.strInstructions}</p>
			</div>
		</div>
		${
			meal.strYoutube
				? `
		<div class="row">
			<h5>Video Recipe</h5>
			<div class="videoWrapper">
				<iframe width="420" height="315"
				src="https://www.youtube.com/embed/${meal.strYoutube.slice(-11)}">
				</iframe>
			</div>
		</div>`
				: ''
		}
	`;

	meal_container.innerHTML = newInnerHTML;
};

Practic, scopul întregii funcții este de a obține răspunsul JSON, de a-l analiza și de a-l transforma într-o componentă HTML. Pentru asta trebuie să facem câteva lucruri, deoarece datele nu sunt încă formate exact așa cum vrem să fie.

În primul rând, primim toate ingrediente si al lor măsuri. După cum sa menționat mai sus, există maximum 20 de ingrediente, dar ele sunt separate în propriile lor proprietăți în obiect, cum ar fi: strIngredient1, strIngredient2, etc … (încă nu știu de ce au făcut asta, dar …?).

Deci, creăm un for bucla care merge de la 1 la 20 și verifică dacă meal are acel corespondent ingredientmeasure pereche. Dacă da, o punem în ingredients matrice. Dacă nu mai există ingrediente, oprim bucla for cu un break condiție.

Apoi, creăm newInnerHTML șir care va conține întregul marcaj HTML. În el analizăm proprietățile rămase pe care dorim să le afișăm.

Notă că unele proprietăți ar putea să nu fie disponibile. Deci pentru asta folosim operator ternar pentru a verifica dacă avem datele pentru a afișa eticheta corespunzătoare. Dacă nu îl avem, returnăm un șir gol și nimic nu va fi afișat pe pagină. category si area sunt exemple ale acestui tip de proprietăți.

Etichetele apar într-un șir împărțit cu o virgulă precum: 'tag1,tag2,tag3'. Deci trebuie split prin virgula respectivă și join înapoi cu o virgulă și un spațiu, deoarece arată mai frumos ('tag1, tag2, tag3' ❤️). Sau cel puțin pentru mine. ?

Pentru a arăta ingredients, mapăm peste matrice și creăm un <li> pentru fiecare pereche ingredient / măsură. La sfârșit, ne alăturăm matricei înapoi pentru a forma un șir. (Acesta este un lucru pe care l-ați face în ReactJS, dar fără joinparte?).

Există, de asemenea, un videoclip Youtube şir (poate) care returnează adresa URL a videoclipului. Dar, pentru ca noi să încorporăm videoclipul în pagină, trebuie să extragem numai ID-ul videoclipului. Pentru asta folosim .slice(-11) pentru a obține ultimele 11 caractere ale șirului, deoarece aici se ascunde ID-ul?

Și, în sfârșit, stabilim totul newInnerHTML a fi meal_container‘s innerHTML -> acest lucru va popula divul cu toate aceste informații!

Întregul proces se va repeta de fiecare dată când apăsăm pe Get Meal buton.

CSS

Ultima parte este să o stilizezi puțin, nu? ?

Pentru CSS Am vrut să folosesc ceva nou, așa că am încercat SkeletonCSS bibliotecă. Este util dacă aveți un proiect mic și nu doriți să vă lăsați copleșiți de toate acele clase, deoarece are doar câteva dintre ele care se ocupă de unele stiluri de bază (butonul, de exemplu) și partea receptivă.

@import url('https://fonts.googleapis.com/css?family=Muli&display=swap');

* {
	box-sizing: border-box;
}

body {
	display: flex;
	flex-direction: column;
	justify-content: center;
	align-items: center;
	padding: 30px 0;
	min-height: calc(100vh - 60px);
}

img {
	max-width: 100%;
}

p {
	margin-bottom: 5px;
}

h3 {
	margin: 0;
}

h5 {
	margin: 10px 0;
}

li {
	margin-bottom: 0;
}

.meal {
	margin: 20px 0;
}

.text-center {
	text-align: center;
}

.videoWrapper {
	position: relative;
	padding-bottom: 56.25%;
	padding-top: 25px;
	height: 0;
}

.videoWrapper iframe {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
}

Puteți vedea că CSS este destul de simplu. Singura parte care merită menționată este .videoWrapper Declarație CSS. Acest lucru asigură faptul că încorporarea YouTube este receptivă. (Am primit asta de la CSS-Tricks – multumesc baieti! ?)

Concluzie

Și voilà! Au fost efectuate! ?

Acum ar trebui să știți cum să utilizați un API public pentru a obține niște date pe care apoi le puteți insera cu ușurință pe pagină! Foarte bine! ?

Acesta este primul proiect pe care l-am făcut pentru # 100Days100Proiecte provocare. Puteți verifica ce alte proiecte am construit și care sunt regulile provocării (dacă doriți să vă înscrieți) făcând clic Aici.

Puteți citi mai multe articole despre www.florin-pop.com.

Codificare fericită! ?