Nikola Brežnjak blog - Tackling software development with a dose of humor
  • Home
  • Daily Thoughts
  • Ionic
  • Stack Overflow
  • Books
  • About me
Home
Daily Thoughts
Ionic
Stack Overflow
Books
About me
  • Home
  • Daily Thoughts
  • Ionic
  • Stack Overflow
  • Books
  • About me
Nikola Brežnjak blog - Tackling software development with a dose of humor
React

Getting started with React by building a Pokemon search application

TL;DR

In the previous tutorial I showed you how to get started with Vue.js 3 by building a Pokemon search application. I’m going to do the same here, but by using React.

In this post, you’ll learn how to use create-react-app (official React project scaffolding tool) to build a React application for searching Pokemon by using Poke API.

Introduction – The Battle is Over Indeed

A few years ago, this was a popular meme. It was funny because it was true 🙂

However, it seems that the craziness has settled down a bit and that the war is seemingly over. For now. These top four (top 5 lists are overrated 😉) frameworks established themselves:

  • Angular (93k)
  • React (218k)
  • Svelte (75k)
  • Vue.js (45k)

I added the number of Github *s (at the time of this writing), but I don’t want you to read into that too much 🤗

Analysis paralysis will, well, paralyze you!

You could argue that there are other options like Ember, Mithril, or good ‘ol jQuery even! However, this tweet says it all:

Developers are fighting over which frontend framework is better.. it’s not like users know or care. They mostly care about the User Experience. UX should be our focus on the frontend.

Personally, I stuck to the Angular bandwagon since version 1.0, but that started losing its ooomph after versions 2, 3, 4, … sorry, lost count.

In the previous post I said that Vue.js (due to its progressive and flexible nature) is a perfect fit for teams that have to rewrite old codebases one step at a time.

I also said that:

React’s JSX just seems wrong, and some people, smarter than me, swear by that being TheRightWay™, and I guess we’re not here to discuss tastes…

Well, after playing with it a bit, I can say it’s not that bad and that indeed one gets used to it.

Also, I recently checked out Svelte and kinda like it, so will be doing a post like this next; stay tuned.

General ‘framework war’ kind of questions I tend to answer in the following way:

Please just stop with the analysis paralysis already.

Do your research, pick a framework (any framework for that matter) that the community is using, use it, and see how far it gets you.

All these talks about X being slow or Y being better just make no sense until you try it yourself for your use case and preference.

Besides, nowadays speed will not be a deciding factor among the top JS frameworks.

With all this out of the way, fasten your seatbelts, take a venti (or trenta) sized cup of coffee, and let’s go do something practical! 💪

The Demo App

As said in the intro, we’ll build an application for searching (and showing) Pokemon by using Poke API.

You can fork the complete source code on Github, and you can see the app in action here.

Prerequisites

Make sure that you have the following tools installed:

  • Node.js – here’s a free but outdated step by step guide/book for both Windows and Mac. Or, really, just go to the main website and download the executable for your machine
  • Git – here’s a fun getting started tutorial in case you’re new to it

Start a new app with create-react-app

To start the project, run the following command (in your Terminal): npx create-react-app pokemon_search_react

This command, as their docs say, will install and execute create-react-app, the official React project scaffolding tool. The output of that command will be similar to the one below:

Creating a new React app in /Users/nikola/Development/Testing/pokemon_search_react.

Installing packages. This might take a couple of minutes.
Installing react, react-dom, and react-scripts with cra-template...

added 1490 packages in 2m

258 packages are looking for funding
  run `npm fund` for details

Initialized a git repository.

Installing template dependencies using npm...

added 67 packages, and changed 1 package in 8s

262 packages are looking for funding
  run `npm fund` for details
Removing template package using npm...

removed 1 package, and audited 1557 packages in 1s

262 packages are looking for funding
  run `npm fund` for details

8 vulnerabilities (2 moderate, 6 high)

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.

Created git commit.

Success! Created pokemon_search_react at /Users/nikola/Development/Testing/pokemon_search_react
Inside that directory, you can run several commands:

  npm start
    Starts the development server.

  npm run build
    Bundles the app into static files for production.

  npm test
    Starts the test runner.

  npm run eject
    Removes this tool and copies build dependencies, configuration files
    and scripts into the app directory. If you do this, you can’t go back!

We suggest that you begin by typing:

  cd pokemon_search_react
  npm start

Happy hacking!

Running our scaffolded project

Let’s run the commands (in terminal) noted at the end of the previous output:

cd pokemon_search_react
npm start

You should get this something similar to this output:

 Compiled successfully!

You can now view pokemon_search_react in the browser.

  Local:            http://localhost:3001
  On Your Network:  http://192.168.8.165:3001

Note that the development build is not optimized.
To create a production build, use npm run build.

webpack compiled successfully
One of your dependencies, babel-preset-react-app, is importing the
"@babel/plugin-proposal-private-property-in-object" package without
declaring it in its dependencies. This is currently working because
"@babel/plugin-proposal-private-property-in-object" is already in your
node_modules folder for unrelated reasons, but it may break at any time.

babel-preset-react-app is part of the create-react-app project, which
is not maintianed anymore. It is thus unlikely that this bug will
ever be fixed. Add "@babel/plugin-proposal-private-property-in-object" to
your devDependencies to work around this error. This will make this message
go away.

⚠️ If you’re bothered by the seeming error-like output, then just add the following code to your package.json file:

"devDependencies": {
  "@babel/plugin-proposal-private-property-in-object": "^7.16.7"
},

You should see the following page in your browser if you open http://localhost:3001. Please note that the port (number after localhost:) could be different. Usually it’s 3000, but I had some other stuff running at that port, so it took the next available one.

Folder structure

Now, let’s open this project in the editor of your choice (I’m using Visual Studio Code), and you should see something like this:

⚠️ This is an introduction tutorial, to get you running fast, so I won’t be going into any specific details and will be only focusing on the src folder.

Add content

OK, so let’s add something to our app.

But, where to start? 🤔

Well, one of the first things I do when I come to a project for the first time is to look at the generated output. Then I try to find the strings corresponding to that output within the source code.

So, if you search for the string Learn React, you’ll see the string is within the App.js file. This file contains the following code:

import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;

Without knowing much about React, you can see where you would change the Learn React text. So, let’s change that to Welcome to Pokemon Search. Save that, and voila!, you’ll see the change reflected immediately in your browser.

If you’re new to web dev, you may not appreciate the usefulness of the so-called hot-reload. Back in the day, we had to refresh the browser after each save. Yeah, yeah, I know: #okboomer 🫣

Add some style

I borrowed the image (hope they don’t mind) from Wikipedia and saved it in the src folder as logo.png, added a search input, and styled it a bit to match it with the Pokemon style. In case you’re wondering, I asked ChatGPT to help with coming up with the style 🙂

Here’s the final code that I came up with in App.js:

import logo from './logo.png';
import './App.css';

function App() {
  return (
    <div className="App">
      <header>
        <img alt="react logo" className="logo" src={logo} />
      </header>

      <main>
        <div className="search-container">
          <input className="search-box" type="text" placeholder="Search..." />
        </div>
      </main>
    </div>
  );
}

export default App;

And the CSS in the App.css file:

body {
  background: linear-gradient(to right, #FDDF3C, #3B4CCA);
}

header {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

.logo {
  margin: 0 2rem 0 0;
}

.search-container {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  gap: 10px;
}

.search-box {
  width: 30%;
  height: 50px;
  font-size: 1.5em;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
  margin: 40px 0;
  text-align: center;
}

The way this looks now is as follows:

If you get stuck in any of the above steps, please reach out in the comments, and I’ll be happy to help.

Pokemon API

Finally, we come to the cool part 🥶, and that is to fetch some data from the PokeAPI and show it in our app.

How do we get this API? Well, if you do a simple Google search for pokemon api and open the first link, you’ll also get to the documentation for their API.

By looking at the docs, you can find that the API endpoint that lists all the available Pokemon is this: https://pokeapi.co/api/v2/pokemon. There’s a total of 1302 records.

Now, fetching all this data every time you load your app would be a bad idea from the performance standpoint, but also from their fair use policy.

So, what we will do is open this link which will fetch all 1320 Pokemon endpoints. The output looks like this:

Now, save this output (just Command + S or Ctrl + s if you’re on Windows) to a local file that we’ll name pokemonapi.json and place it in the src/ folder.

⚠️ I just want to give proper props to PokeAPI for their awesome service 👏

For our demo purposes, this will be fine, but if you’d like to deploy this somewhere where you’d get a lot of traffic (or, if the main list of Pokemon changes), you’d have to cache the responses. We’ll tackle this in another blog post, but just something to keep in mind.

Show the data

Now we’ll want to list all the Pokemon that we have in the pokemonapi.json file. To do that, we’ll add some code at the top of the App.js file that looks like this:

import pokemonData from "./pokemonapi.json";
import React, { useState } from "react";

And then, inside the function App():

  const [pokemonList, setPokemonList] = useState(pokemonData.results);
  const [searchTerm, setSearchTerm] = useState("");
  const [selectedPokemon, setSelectedPokemon] = useState(null);

  const filteredPokemonList = pokemonList.filter((pokemon) =>
    pokemon.name.includes(searchTerm)
  );

filteredPokemonList function returns the list of pokemon that we loaded from the pokemonapi.json file and filters them by what we enter in the input box.

We’ll add this portion of JSX just below the search-container div:

<ul>
  {filteredPokemonList.map((pokemon) => (
    <li key={pokemon.id} className="pokemon-item">
      <a href="#">{pokemon.name}</a>
    </li>
  ))}
</ul>

And, we’ll add a bit of CSS:

.pokemon-item {
  float: left;
  margin: 10px;
}
.pokemon-item a {
  color: #000;
  text-decoration: none;
  font-size: 16px;
  transition: color 0.3s ease;
  text-transform: capitalize;
}

.pokemon-item a:hover {
  color: #3B4CCA;
}

ul {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 5px;
  list-style: none;
}

Doing this, you should have your app look like this:

Get additional details about each Pokemon

If you open any of the URLs that you see in the pokemonapi.json file, you will get an output like this:

To show some additional information about a certain Pokemon (once clicked on its name in the list), we’ll update the a tag inside the li:

<li key={pokemon.id} className="pokemon-item">
  <a href="#" onClick={() => showPokemon(pokemon.url)}>{pokemon.name}</a>
</li>

On the click event we attached the showPokemon function (by using onClick) and passed it the URL that we get in the main API call (loaded from the JSON file).

Now we should define this function:

const showPokemon = async (url) => {
  const response = await fetch(url);
  if (!response.ok) {
    console.error(`Error fetching Pokemon: ${response.statusText}`);
    return;
  }

  const data = await response.json();
  setSelectedPokemon(data);
}

To show the selected Pokemon, we’ll add a bit of JSX between the search-container div and the ul:

{selectedPokemon && (
  <div className="pokemon-details">
    <h2>{selectedPokemon.name}</h2>
    <img
      src={selectedPokemon.sprites.front_default}
      alt={selectedPokemon.name}
    />
    <p>Height: {selectedPokemon.height}</p>
    <p>Weight: {selectedPokemon.weight}</p>

    {selectedPokemon.stats.map((stat, index) => (
      <div key={index}>
        <p>
          {stat.stat.name}: {stat.base_stat}
        </p>
      </div>
    ))}
  </div>
)}

We’re using selectedPokemon && so that the line {{ selectedPokemon.name }} doesn’t throw an error for trying to access a property that doesn’t exist on the object selectedPokemon (which is initialized as null in the beginning: const [selectedPokemon, setSelectedPokemon] = useState(null);).

BTW, useState is a so-called React Hook that essentially enables you to update a certain variable.

Anyways, back on point; we’re using map to loop through all the elements of the stats array and output the name and base_stat properties.

To make it look nicer, we’ll use a bit of CSS:

.pokemon-details {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 30%;
  margin: 20px auto;
  padding: 20px;
  border: 1px solid #000;
  border-radius: 10px;
  color: #000;
  text-transform: capitalize;
}

.pokemon-details img {
  width: 100px;
  height: 100px;
}

Two-way data binding

Right now if you test your app and type something in the search box, it will not filter the list of Pokemon.

You may remember that in Vue.js, we achieved this by using the v-model. However, in React, there isn’t a direct equivalent to Vue’s v-model for two-way data binding. In React we can achieve a similar effect by using a combination of state and event handlers.

To do this, we’ll update the input tag to this:

<input className="search-box" type="text"
  placeholder="Search..." 
  value={searchTerm} 
  onChange={event => setSearchTerm(event.target.value)}
/>

In this code, searchTerm is a state variable that is set as the value of the input field. The onChange event handler updates searchTerm whenever the user types something into the input field. This creates a two-way data binding effect similar to Vue’s v-model.

Go try it out! The app is now fully functional 💪

All the code

For reference, for those following the tutorial to the dot, here’s the full listing of App.js file:

import logo from "./logo.png";
import "./App.css";
import pokemonData from "./pokemonapi.json";
import React, { useState } from "react";

function App() {
  const [pokemonList, setPokemonList] = useState(pokemonData.results);
  const [searchTerm, setSearchTerm] = useState("");
  const [selectedPokemon, setSelectedPokemon] = useState(null);

  const filteredPokemonList = pokemonList.filter((pokemon) =>
    pokemon.name.includes(searchTerm)
  );

  const showPokemon = async (url) => {
    const response = await fetch(url);
    if (!response.ok) {
      console.error(`Error fetching Pokemon: ${response.statusText}`);
      return;
    }

    const data = await response.json();
    setSelectedPokemon(data);
  };

  return (
    <div className="App">
      <header>
        <img alt="react logo" className="logo" src={logo} />
      </header>

      <main>
        <div className="search-container">
          <input className="search-box" type="text" placeholder="Search..." 
          value={searchTerm} 
          onChange={event => setSearchTerm(event.target.value)}
          />
        </div>

        {selectedPokemon && (
          <div className="pokemon-details">
            <h2>{selectedPokemon.name}</h2>
            <img
              src={selectedPokemon.sprites.front_default}
              alt={selectedPokemon.name}
            />
            <p>Height: {selectedPokemon.height}</p>
            <p>Weight: {selectedPokemon.weight}</p>

            {selectedPokemon.stats.map((stat, index) => (
              <div key={index}>
                <p>
                  {stat.stat.name}: {stat.base_stat}
                </p>
              </div>
            ))}
          </div>
        )}

        <ul>
          {filteredPokemonList.map((pokemon) => (
            <li key={pokemon.id} className="pokemon-item">
              <a href="#" onClick={() => showPokemon(pokemon.url)}>
                {pokemon.name}
              </a>
            </li>
          ))}
        </ul>
      </main>
    </div>
  );
}

export default App;

And here’s the full contents of the App.css file:

body {
  background: linear-gradient(to right, #FDDF3C, #3B4CCA);
}

header {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

.logo {
  margin: 0 2rem 0 0;
}

.search-container {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  gap: 10px;
}

.search-box {
  width: 30%;
  height: 50px;
  font-size: 1.5em;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
  margin: 40px 0;
  text-align: center;
}

.pokemon-item {
  float: left;
  margin: 10px;
}
.pokemon-item a {
  color: #000;
  text-decoration: none;
  font-size: 16px;
  transition: color 0.3s ease;
  text-transform: capitalize;
}

.pokemon-item a:hover {
  color: #3B4CCA;
}

ul {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 5px;
  list-style: none;
}

.pokemon-details {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 30%;
  margin: 20px auto;
  padding: 20px;
  border: 1px solid #000;
  border-radius: 10px;
  color: #000;
  text-transform: capitalize;
}

.pokemon-details img {
  width: 100px;
  height: 100px;
}

.pokemon-details p {
  margin: 5px;
}

B e a utiful

At this point, we can search for some Pokemon (my son’s favorite is, ofc, Pikachu), and if we click on it, we’ll get this:

search results - pikachu

Deployment

If you’d like to host this on your web server, then first run npm run build and you’ll get an output similar to this:

> [email protected] build
> react-scripts build

Creating an optimized production build...
Compiled with warnings.

[eslint] 
src/App.js
  Line 64:15:  The href attribute requires a valid value to be accessible. Provide a valid, navigable address as the href value. If you cannot provide a valid href, but still need the element to resemble a link, use a button and change it with appropriate styles. Learn more: https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/HEAD/docs/rules/anchor-is-valid.md  jsx-a11y/anchor-is-valid

Search for the keywords to learn more about each warning.
To ignore, add // eslint-disable-next-line to the line before.

File sizes after gzip:

  58.34 kB (-1 B)  build/static/js/main.b3bd969c.js
  1.78 kB          build/static/js/453.bd7a2879.chunk.js
  677 B            build/static/css/main.af098727.css

The project was built assuming it is hosted at /.
You can control this with the homepage field in your package.json.

The build folder is ready to be deployed.
You may serve it with a static server:

  serve -s build

Find out more about deployment here:
  https://cra.link/deployment

Now all you have to do is take the contents of the build folder (you may remember that in Vue that folder was called dist) and ‘paste’ it on your static web server.

If you don’t have a server of your own, then Vite has an extensive description for deploying your static pages to many popular services like Github Pages, Netlify, Vercel, Surge, etc.

You can deploy to Github Pages in under 2 minutes by following their documentation.

Just for brevity sake the steps are as follows:

  • create a new public Github repository and name it username.github.io, where username is your username on GitHub.
  • clone the repo with git clone https://github.com/username/username.github.io
  • inside the folder copy the contents of the build folder
  • commit and push the changes:
git add --all 
git commit -m "Initial commit"
git push -u origin main

Now your site will be visible online at https://username.github.io (again, where username is your Github username)

You can see my deployment live here.

⚠️ If you’ll have multiple folders in Github pages, then before using the npm run build command, you’ll want to set the homepage variable in the package.json file to that folder. Example: "homepage": "/pokemon-search-react".

Conclusion

In this tutorial, you learned how to get started with using React by building an application for searching Pokemon by using PokeAPI and making it publically accessible via Github Pages.

I intend to cover at least two more frameworks in the future blog posts, so stay tuned. Finally, out of curiosity, which one do you prefer so far: React or Vue.js? I’m looking to test Svelte next, but curious which one you’d like to see me cover.

Please leave any comments and feedback in the discussion section below, and thank you for reading!

JavaScript, Vue.js

Getting started with Vue.js 3 by building a Pokemon search application

TL;DR

In the previous tutorial I showed you how to get started with Vue.js 3 by building a Giphy search application. That was a long while ago 🙂, so here’s a new and updated post.

But, to not just update the post, I decided to do something different. Since my kids are into Pokemon, what better use of my coding skills than to make them an app where they can search for their favorite Pokemon and check their stats.

So, in this post, you’ll learn how to use create-vue (official Vue project scaffolding tool) to build a Vue.js 3 application for searching Pokemon by using Poke API.

Introduction – The Battle is Over Indeed

A few years ago, this was a popular meme. It was funny because it was true 🙂

However, it seems that the craziness has settled down a bit and that the war is seemingly over. For now. These top four (top 5 lists are overrated 😉) frameworks established themselves:

  • Angular (93k)
  • React (218k)
  • Svelte (75k)
  • Vue.js (206k v2, 43k v3)

I added the number of Github *s (at the time of this writing), but I don’t want you to read into that too much 🤗

Analysis paralysis will, well, paralyze you!

You could argue that there are other options like Ember, Mithril, or good ‘ol jQuery even! However, this tweet says it all:

Developers are fighting over which frontend framework is better.. it’s not like users know or care. They mostly care about the User Experience. UX should be our focus on the frontend.

Personally, I stuck to the Angular bandwagon since version 1.0, but that started losing its ooomph after versions 2, 3, 4, … sorry, lost count.

I believe that Vue.js (due to its progressive and flexible nature) is a perfect fit for teams that have to rewrite old codebases one step at a time. However, you can also use it as a full-blown framework if you wish.

Personally, React’s JSX just seems wrong, and some people, smarter than me, swear by that being TheRightWay™, and I guess we’re not here to discuss tastes…

Recently I checked out Svelte and kinda like it, so will be doing a post like this for it; stay tuned.

General ‘framework war’ kind of questions I tend to answer in the following way:

Please just stop with the analysis paralysis already.

Do your research, pick a framework (any framework for that matter) that the community is using, use it, and see how far it gets you.

All these talks about X being slow or Y being better just make no sense until you try it yourself for your use case and preference.

Besides, nowadays speed will not be a deciding factor among the top JS frameworks.

With all this out of the way, fasten your seatbelts, take a venti (or trenta) sized cup of coffee, and let’s go do something practical! 💪

The Demo App

As said in the intro, we’ll build an application for searching (and showing) Pokemon by using Poke API.

You can fork the complete source code on Github.

I’m using Vue.js 3, and I’ll be referring to it as just Vue in the rest of this post.

Prerequisites

Make sure that you have the following tools installed:

  • Node.js – here’s a free but outdated step by step guide/book for both Windows and Mac. Or, really, just go to the main website and download the executable for your machine
  • Git – here’s a fun getting started tutorial in case you’re new to it

Start a new app with create-vue

To start the project, run the following command (in your Terminal): npm create vue@latest

This command, as their docs say, will install and execute create-vue, the official Vue project scaffolding tool. You will be presented with prompts for several optional features such as TypeScript and testing support (I named the project PokemonSearch_Vue3):

✔ Project name: … PokemonSearch_Vue3
✔ Package name: … pokemonsearch-vue3
✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
✔ Add Vitest for Unit Testing? … No / Yes
✔ Add an End-to-End Testing Solution? › No
✔ Add ESLint for code quality? … No / Yes

Scaffolding project in /Users/Nikola.Breznjak/Development/Web/Vue/PokemonSearch_Vue3...

Done. Now run:

cd PokemonSearch_Vue3
npm install
npm run dev

In the previous blog post, we used Vue CLI. That has been deprecated and create-vue is now the default. The reasons that they quote are:

Vue CLI is based on webpack, while create-vue is based on Vite. Vite supports most of the configured conventions found in Vue CLI projects out of the box, and provides a significantly better development experience due to its extremely fast startup and hot-module replacement speed. Learn more about why we recommend Vite over webpack here.
Unlike Vue CLI, create-vue itself is just a scaffolding tool: it creates a pre-configured project based on the features you choose, and delegates the rest to Vite. Projects scaffolded this way can directly leverage the Vite plugin ecosystem which is Rollup-compatible.

You can learn more about ‘why’ Vite here.

Running our scaffolded project

Let’s run the commands (in terminal) noted in the previous output:

cd PokemonSearch_Vue3
npm install
npm run dev

You should get this output:

 VITE v5.0.12  ready in 175 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help

You should see the following page in your browser if you open http://localhost:5173.

Folder structure

Now, let’s open this project in the editor of your choice (I’m using Visual Studio Code + Volar extension), and you should see something like this:

This is an introduction tutorial, to get you running fast, so I won’t be going into any specific details and will be only focusing on the src folder.

Add content

OK, so let’s add something to our app.

But, where to start? 🤔

Well, one of the first things I do when I come to a project for the first time is to look at the generated output. Then I try to find the strings corresponding to that output within the source code.

So, if you search for the string You did it!, you’ll see the string is within the App.vue file. This file contains the following code:

<script setup>
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>

<template>
  <header>
    <img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />

    <div class="wrapper">
      <HelloWorld msg="You did it!" />
    </div>
  </header>

  <main>
    <TheWelcome />
  </main>
</template>

<style scoped>
header {
  line-height: 1.5;
}

.logo {
  display: block;
  margin: 0 auto 2rem;
}

@media (min-width: 1024px) {
  header {
    display: flex;
    place-items: center;
    padding-right: calc(var(--section-gap) / 2);
  }

  .logo {
    margin: 0 2rem 0 0;
  }

  header .wrapper {
    display: flex;
    place-items: flex-start;
    flex-wrap: wrap;
  }
}
</style>

Without knowing much about Vue.js, you can see where you would change the You did it! text. So, let’s change that to Welcome to Pokemon Search. Save that, and voila!, you’ll see the change reflected immediately in your browser.

Add some style

I borrowed the image (hope they don’t mind) from Wikipedia and saved it in the assets folder as logo.webp, added a search input, and styled it a bit to match it with the Pokemon style. In case you’re wondering, I asked ChatGPT to help with coming up with the style 🙂

Here’s the final code that I came up with in App.vue:

<template>
  <header>
    <img alt="Vue logo" class="logo" src="./assets/logo.webp" />
  </header>

  <main>
    <div class="search-container">
      <input class="search-box" type="text" placeholder="Search..." v-model="searchTerm">
    </div>
  </main>
</template>

<style scoped>
header {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

.logo {
  margin: 0 2rem 0 0;
}

.search-container {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  gap: 10px;
}

.search-box {
  width: 30%;
  height: 50px;
  font-size: 1.5em;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
  margin: 40px 0;
  text-align: center;
}

Also, I did a few other smaller things:

  • added background: linear-gradient(to right, #FDDF3C, #3B4CCA); in the base.css file in the body tag
  • removed the main.css file altogether
  • imported base.css in the main.js file instead of main.css
  • removed the whole components folder

The way this looks now is as follows:

If you get stuck in any of the above steps, please reach out in the comments, and I’ll be happy to help.

Pokemon API

Finally, we come to the cool part 🥶, and that is to fetch some data from the PokeAPI and show it in our app.

How do we get this API? Well, if you do a simple Google search for pokemon api and open the first link, you’ll also get to the documentation for their API.

By looking at the docs, you can find that the API endpoint that lists all the available Pokemon is this: https://pokeapi.co/api/v2/pokemon. There’s a total of 1302 records.

Now, fetching all this data every time you load your app would be a bad idea from the performance standpoint, but also from their fair use policy.

So, what we will do is open this link which will fetch all 1320 Pokemon endpoints. The output looks something like this:

Now, save this output (just Command + S or Ctrl + s if you’re on Windows) to a local file that we’ll name pokemonapi.json and place this file in the src/assets folder.

⚠️ I just want to give proper props to PokeAPI for their awesome service 👏

For our demo purposes, this will be fine, but if you’d like to deploy this somewhere where you’d get a lot of traffic (or, if the main list of Pokemon changes), you’d have to cache the responses. We’ll tackle this in another blog post, but just something to keep in mind.

Show the data

Now we’ll want to list all the Pokemon that we have in the saved pokemonapi.json file. To do that, we’ll write some Javascript code in the top of the App.vue file that looks like this:

<script>
import pokemonData from './assets/pokemonapi.json';

export default {
  data() {
    return {
      pokemonList: pokemonData.results,
      searchTerm: '',
      selectedPokemon: null
    }
  },
  computed: {
    filteredPokemonList() {
      return this.pokemonList.filter(pokemon => pokemon.name.includes(this.searchTerm));
    }
  }
}
</script>

filteredPokemonList function returns the list of pokemon that we loaded from the pokemonapi.json file and filters them by what we enter in the input box.

And we’ll add this portion of HTML just below the search box:

<ul>
  <li v-for="pokemon in filteredPokemonList" :key="pokemon.id" class="pokemon-item">
    {{ pokemon.name }}
  </li>
</ul>

And, we’ll add a bit of CSS:

.pokemon-item {
  float: left;
  margin: 10px;
}
.pokemon-item a {
  color: #000;
  text-decoration: none;
  font-size: 16px;
  transition: color 0.3s ease;
  text-transform: capitalize;
}

.pokemon-item a:hover {
  color: #3B4CCA;
}

ul {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 5px;
  list-style: none;
}

Doing this, you should have your app look something like this:

Get additional details about each Pokemon

If you open any of the URLs that you see in the pokemonapi.json file, you will get something like this:

To show some additional information about a certain Pokemon (once clicked on its name in the list), we’ll update the li item by adding the a link to it like this:

<li v-for="pokemon in filteredPokemonList" :key="pokemon.id" class="pokemon-item">
  <a href="#" @click="showPokemon(pokemon.url)">{{ pokemon.name }}</a>
  </li>

On the click event we attached the showPokemon function (by using @click) and passed it the URL that we get in the main API call (loaded from the JSON file).

Now we should define this function in the methods object like this (I’m showing the contents of the whole script tag):

import pokemonData from './assets/pokemonapi.json';

export default {
  data() {
    return {
      pokemonList: pokemonData.results,
      searchTerm: '',
      selectedPokemon: null
    }
  },
  computed: {
    filteredPokemonList() {
      return this.pokemonList.filter(pokemon => pokemon.name.includes(this.searchTerm));
    }
  },
  methods: {
    async showPokemon(url) {
      const response = await fetch(url);
      if (!response.ok) {
        console.error(`Error fetching Pokemon: ${response.statusText}`);
        return;
      }

      this.selectedPokemon = await response.json();
      console.log(this.selectedPokemon);
    }
  }
}

To show the selected Pokemon, we’ll add a bit of HTML:

<div class="pokemon-details" v-if="selectedPokemon">
  <h2>{{ selectedPokemon.name }}</h2>
  <img :src="selectedPokemon.sprites.front_default" alt="selectedPokemon.name">
  <p>Height: {{ selectedPokemon.height }}</p>
  <p>Weight: {{ selectedPokemon.weight }}</p>

  <div v-for="(stat, index) in selectedPokemon.stats" :key="index">
    <p>{{ stat.stat.name }}: {{ stat.base_stat }}</p>
  </div>
</div>

We’re using v-if="selectedPokemon" so that the line {{ selectedPokemon.name }} doesn’t throw an error for trying to access a property that doesn’t exist on the object selectedPokemon (which is initialized as null in the beginnning).

We’re using v-for to loop through all the elements of the stats array and output the name and base_stat properties.

To make it look nicer, we’ll use a bit of CSS:

.pokemon-details {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 30%;
  margin: 20px auto;
  padding: 20px;
  border: 1px solid #000;
  border-radius: 10px;
  color: #000;
  text-transform: capitalize;
}

.pokemon-details img {
  width: 100px;
  height: 100px;
}

For reference, for those following the tutorial to the dot, here’s the full listing of HelloWorld.vue file:

<script>
import pokemonData from './assets/pokemonapi.json';

export default {
  data() {
    return {
      pokemonList: pokemonData.results,
      searchTerm: '',
      selectedPokemon: null
    }
  },
  computed: {
    filteredPokemonList() {
      return this.pokemonList.filter(pokemon => pokemon.name.includes(this.searchTerm));
    }
  },
  methods: {
    async showPokemon(url) {
      const response = await fetch(url);
      if (!response.ok) {
        console.error(`Error fetching Pokemon: ${response.statusText}`);
        return;
      }

      this.selectedPokemon = await response.json();
      console.log(this.selectedPokemon);
    }
  }
}
</script>

<template>
  <header>
    <img alt="Vue logo" class="logo" src="./assets/logo.webp" />
  </header>

  <main>
    <div class="search-container">
      <input class="search-box" type="text" placeholder="Search..." v-model="searchTerm">
    </div>

    <div class="pokemon-details" v-if="selectedPokemon">
      <h2>{{ selectedPokemon.name }}</h2>
      <img :src="selectedPokemon.sprites.front_default" alt="selectedPokemon.name">
      <p>Height: {{ selectedPokemon.height }}</p>
      <p>Weight: {{ selectedPokemon.weight }}</p>

      <div v-for="(stat, index) in selectedPokemon.stats" :key="index">
        <p>{{ stat.stat.name }}: {{ stat.base_stat }}</p>
      </div>
    </div>

    <ul>
      <li v-for="pokemon in filteredPokemonList" :key="pokemon.id" class="pokemon-item">
        <a href="#" @click="showPokemon(pokemon.url)">{{ pokemon.name }}</a>
      </li>
    </ul>
  </main>
</template>

<style scoped>
header {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

.logo {
  margin: 0 2rem 0 0;
}

.search-container {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  /* Change the direction to row */
  gap: 10px;
}

.search-box {
  width: 30%;
  height: 50px;
  font-size: 1.5em;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
  margin: 40px 0;
  text-align: center;
}

.pokemon-item {
  float: left;
  margin: 10px;
}
.pokemon-item a {
  color: #000;
  text-decoration: none;
  font-size: 16px;
  transition: color 0.3s ease;
  text-transform: capitalize;
}

.pokemon-item a:hover {
  color: #3B4CCA;
}

ul {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 5px;
  list-style: none;
}

.pokemon-details {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 30%;
  margin: 20px auto;
  padding: 20px;
  border: 1px solid #000;
  border-radius: 10px;
  color: #000;
  text-transform: capitalize;
}

.pokemon-details img {
  width: 100px;
  height: 100px;
}
</style>

B e a utiful

At this point, we can search for some Pokemon (my son’s favorite is, ofc, Pikachu), and if we click on it, we’ll get something like this:

Deployment

If you’d like to host this on your web server, then first run npm run build and you’ll get an output similar to this:

Now all you have to do is take the contents of the dist folder and ‘paste’ it on your static web server.

If you don’t have a server of your own, then Vite has an extensive description for deploying your static pages (if you don’t have the ) to many popular services like Github Pages, Netlify, Vercel, Surge, etc.

You can deploy to Github Pages in under 2 minutes by following their documentation.

Just for brevity sake the steps are as follows:

  • create a new public Github repository and name it username.github.io, where username is your username on GitHub.
  • clone the repo with git clone https://github.com/username/username.github.io
  • inside the folder copy the contents of the dist folder
  • commit and push the changes:
git add --all 
git commit -m "Initial commit"
git push -u origin main

Now your site will be visible online at https://username.github.io (again, where username is your Github username)

You can check my deployment here.

Conclusion

In this tutorial, you learned how to get started with using Vue.js 3 by building an application for searching Pokemon by using PokeAPI and making it publically accessible via Github Pages.

Please leave any comments and feedback in the discussion section below, and thank you for reading!

JavaScript

This is How Your Parents Used CSS and JavaScript

TL;DR

This is the second part of the "This is How Your Parents Used to Build Websites". The first blog post is here.
Now, if you thought our HTML journey was exciting, buckle up! We’re about to dive into the world of CSS and JavaScript. These are the tools that will transform your static HTML pages into vibrant, interactive experiences.

Ready? Let’s go!

Part 1: CSS – Making Your Website Stylish

Basic Concepts of CSS

This is how a simple CSS statement would look like: p { color: blue};.

There are three basic concepts of CSS:

  • Selectors:
    • Think of them as the ‘who’ in styling.
    • For example, p { } targets all <p> elements.
  • Properties and Values:
    • The ‘what’ and ‘how’. They go withing the {} brackets.
    • Like color: blue; – this tells the paragraph text to be blue.

Incorporating CSS into HTML

There are three ways you can add CSS into HTML:

  • Inline Styles: Directly in an HTML tag, like <p style="color: blue;">.
  • Internal Styles: Inside the <style> tag in the HTML head.
  • External Stylesheets: A separate .css file, linked in the HTML head with <link rel="stylesheet" href="style.css">.

Exploring Common CSS Properties

Color and Fonts

body { color: #333; font-family: 'Arial', sans-serif; }

Font Size, Weight, and Style

p {
  font-size: 16px;
  font-weight: bold;
  font-style: italic;
}

Text Alignment and Decoration

h1 {
  text-align: center;
  text-decoration: underline;
}

Margin and Padding

.content {
  margin: 20px;
  padding: 15px;
}

Borders

.box {
  border: 2px solid #000000; /* Solid black border */
  border-radius: 10px; /* Rounded corners */
}

Styling Links

  • Hover Effect

    a:hover {
    color: #ff0000; /* Red color on hover */
    text-decoration: none;
    }
  • Link Visited State

    a:visited {
    color: #800080; /* Purple for visited links */
    }

Layout with Flexbox

  • display: flex; enables a flexible box layout.
  • There are games that explain flexbox, so if you’re curious go check them out.

Responsive Design Basics

In order to adapt the site to various screen sizes and make sure it looks good on an iPhone as well as on your laptop, we can use the so-called media queries:

@media (max-width: 600px) { body { background-color: lightblue; }}

This changes the background color on screens smaller than 600px.

Advanced Selectors

  • Pseudo-classes
    a:hover { color: red; } changes link color on hover

  • CSS Transitions
    transition: background-color 0.5s ease;

  • CSS Animations

    @keyframes example {
    from {background-color: red;}
    to {background-color: yellow;}
    }
    div {
    animation-name: example;
    animation-duration: 4s;
    }

Advanced CSS

If you’re curious enough, please explore SASS (CSS with superpowers) on your own.

Part 2: JavaScript – Bringing Interactivity to Your Website

JavaScript is the scripting language that lets you create dynamic content.

Basic Syntax and Concepts

Variables
let message = 'Hello, world!'; – with this we defined the variable called ‘message’ and saved in it the string ‘Hello, world!’.

You may come across the keyword var, but that was an old keyword that is no longer suggested to be used because of the unintended consequences.

Functions

function greet(name) {
  alert('Hello, ' + name);
}

With this we created a function called greet that takes a parameter called name and outputs Hello name via an alert (OFC, name is replaced with whatever you pass into the function as a parameter).

Events
<button onclick="greet('Alice')">Greet</button>

With onclick we define what function is going to be run when the button is clicked.

DOM Manipulation

  • Change text

    document.getElementById('myText').innerHTML = 'New Text';
  • A button that changes a theme color

    <button onclick="changeColor()">Change Theme</button>
    <script>
    function changeColor() {
    document.body.style.backgroundColor = 'lightblue';
    }
    </script>
  • Changing styles on hover using JavaScript

    document.getElementById('myElement').onmouseover = function() {
    this.style.color = 'red';
    };
  • Interactive portfolio enhancements

    document.getElementById('project1').onmouseover = function() {
    this.style.transform = 'scale(1.1)';
    };
    document.getElementById('project1').onmouseout = function() {
    this.style.transform = 'scale(1)';
    };
  • Handling a click event

    document.getElementById('myButton').addEventListener('click', function() {
    alert('Button clicked!');
    });

Advanced topics

Using fetch to load data

fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data));

I wrote a three series blog post on doing so-called AJAX calls (fetching data from some API), check them out here:

  • Making AJAX calls in pure JavaScript
  • Making AJAX calls using jQuery
  • Making AJAX calls using the Fetch API

JavaScript ES6
There are some awesome things that have been added to the ‘new’ flavor of JavaScript, called ES6. I won’t go into the details here, but use Google (or ChatGPT, or Bard, or …) and look into these topics:

  • Arrow Functions: const add = (a, b) => a + b;
  • Template Literals
    const greeting = `Hello, ${name}!`;

Conclusion

And that’s a wrap! We’ve just scratched the surface of the amazing world of CSS and JavaScript. Remember, practice is key, and the web is your canvas. Go out there and create something awesome!

In the next blog post we’ll cover frameworks for CSS and JavaScript, pick one for each and create a Pokemon search application.

JavaScript

This is How Your Parents Used to Build Websites

TL;DR

Hey future web wizards! Are you ready to embark on your journey into the vast world of web development? This post is your beginner-friendly guide to HTML. We’ll break down the essentials, infuse some humor, and provide plenty of examples for you to experiment with. By the end of this, you’ll be well on your way to crafting your first web page. Let’s dive into the world of tags and elements and make them your new best friends!

The intro you just read might sound like a blast from the past – a time when all aspiring web developers had were books and a computer (if they were lucky). Back then, the web was a simpler place, primarily text and links.

But hold on, we’re not just reminiscing about the old days. Even though web development has evolved tremendously (and yes, it’s a lot cooler now), understanding the basics of HTML is still crucial. So, even if you’re planning to leap into more advanced topics (which we’ll cover in the next blog post), this article is worth your time to grasp the fundamentals of HTML tags.

In this blog post we’ll cover HTML basics. In the next one we’ll touch on CSS and JavaScript.

Setting the Scene

Picture this: It’s your first day as an aspiring web developer, buzzing with ideas and ready to make your mark on the internet. But there’s a key skill you need to master first: HTML. Don’t fret – it’s far from daunting. HTML is the skeleton of all websites, and together, we’re going to unravel its mysteries.

Why HTML, Anyway?

HTML stands for HyperText Markup Language. It’s the skeleton of all web pages. Think of it as the foundation of a house – without it, you can’t build anything stable. HTML is where every web developer begins their journey. It’s simple, powerful, and the stepping stone to more complex web technologies.

OK, but What’s in a Markup Language?

Markup languages are all about annotating text to give it structure and meaning. HTML does this by using tags to turn plain text into a structured, visually appealing web page. It’s like taking a plain old document and adding magic to make it a web page.

⚠️ And, yes, to settle the debate: No, HTML is not programming. Sorry, not sorry.

Your First HTML Tags

Let’s start with the basics. Every HTML document has some key tags:

  • <!DOCTYPE html>: This declaration defines the document type and HTML version
  • <html>: The root element that wraps the entire content
  • <head>: This contains meta-information about the document, like its title
  • <body>: The meat of the page – this is where all your content will go

Creating Your First HTML Page

Open up your favorite text editor (Visual Studio Code, Sublime Text, Vim, … – take your pick) and let’s get coding. Copy the following code into a file and name it index.html:

<!DOCTYPE html>
<html>
<head>
    <title>My First Web Page</title>
</head>
<body>
    <h1>Hello, World!</h1>
    <p>Welcome to my first web page.</p>
</body>
</html>

Now open this index.html file in your favorite browser (Chrome, Safari, Firefox, …). Usually, just doubleclicking the file should make it automatically open in your default browser.

Voila! You’ve just created your first web page that should look like this:

Understanding the HTML Structure

HTML documents are structured as a tree of elements. Each element has an opening tag (<tag>) and a closing tag (</tag>). Some elements are self-closing, like <img /> (for inserting images) and <br /> (for inserting a new line). Think of it as a family tree, but for your web content.

Dive into Elements and Attributes

HTML elements can have attributes that provide additional information about the element. For example, <a href="http://www.nikola-breznjak.com/">My site</a> uses the href attribute to define the link’s destination.

Text Formatting and Layout

HTML offers a range of elements for formatting text:

  • <h1> to <h6>: Headings from most to least important
  • <p>: Paragraphs
  • <strong> and <em>: Bold and italic text for emphasis
  • <ul> and <ol>: Unordered (bullets) and ordered (numbers) lists
  • <li>: List items
  • <div>: A generic container for content, great for layout and styling purposes

Adding Images

To add an image, use <img src="image.jpg" alt="Description">. Change the image.jpg with an URL of an actual image. For example, edit the index.html file to look like this:

<!DOCTYPE html>
<html>
<head>
    <title>My First Web Page</title>
</head>
<body>
    <h1>Hello, World!</h1>
    <p>Welcome to my first web page.</p>
    <img src="https://i.imgur.com/AoX2x7f.png" alt="VIM for Life">
</body>
</html>

And you should get this:

Adding Links

To create a link, use the a tag. Example: <a href="http://www.google.com">Link to Google.com</a>.

Forms and User Input:

Forms are what you should use to get some input from users. Use the <form> element to create an input area. Inside, use <input>, <textarea>, <button>, and other form elements to gather user input.

For example, here’s the usual login form:

<form action="/submit-your-login-form" method="post">
    <label for="username">Username:</label>
    <input type="text" id="username" name="username" required>

    <label for="password">Password:</label>
    <input type="password" id="password" name="password" required>

    <button type="submit">Login</button>
</form>

If you add that to the index.html file, you should see this in your browser (when you refresh the page):

Embedding Media

You can embed videos and audio in HTML5 using <video> and <audio> tags. Here’s an example for video:

<video width="320" height="240" controls>
    <source src="movie.mp4" type="video/mp4">
    <source src="movie.ogg" type="video/ogg">
    Your browser does not support the video tag.
</video>

and here’s the example for audio:

<audio controls>
    <source src="audio.mp3" type="audio/mpeg">
    <source src="audio.ogg" type="audio/ogg">
    Your browser does not support the audio element.
</audio>

Of course, you would have to have the video and audio formats to try this out. You can Google, or create some of your own. Or, for the sake of quickly testing this out, here’s a link to an MP3 of a crowbar sound from a cult game Half Life.

Building a Simple Personal Portfolio Site with HTML

Let’s enhance our basic HTML skills by creating a simple personal portfolio page. This website will have four main sections: a header, an about me section, a projects section, and a contact section.

Creating the Basic Structure

Start by defining the structure of your page. Open your text editor and create a new file named portfolio.html. Here’s how you’ll structure it:

<!DOCTYPE html>
<html>
<head>
    <title>My Personal Portfolio</title>
</head>
<body>
    <header>
        <!-- Header content goes here -->
    </header>

    <section id="about">
        <!-- About me content goes here -->
    </section>

    <section id="projects">
        <!-- Projects list goes here -->
    </section>

    <section id="contact">
        <!-- Contact information goes here -->
    </section>
</body>
</html>

Crafting the Header

The header will contain your name and a navigation menu.

<header>
    <h1>John Doe</h1>
    <nav>
        <ul>
            <li><a href="#about">About Me</a></li>
            <li><a href="#projects">Projects</a></li>
            <li><a href="#contact">Contact</a></li>
        </ul>
    </nav>
</header>

The About Me Section

Here, you’ll introduce yourself. Include a brief paragraph and maybe a profile picture.

<section id="about">
    <h2>About Me</h2>
    <img src="profile.jpg" alt="John Doe" />
    <p>Hi, I'm John Doe, a passionate web developer...</p>
</section>

Showcasing Projects

In the projects section, list some of the work you’ve done. Use <ul> for the list and <li> for each project.

<section id="projects">
    <h2>Projects</h2>
    <ul>
        <li><a href="http://example.com/project1">Project 1</a></li>
        <li><a href="http://example.com/project2">Project 2</a></li>
        <!-- Add more projects here -->
    </ul>
</section>

The Contact Section

Include your contact information or a form for visitors to reach out to you.

<section id="contact">
    <h2>Contact Me</h2>
    <p>Email: [email protected]</p>
    <!-- You can add a contact form here in future -->
</section>

Adding Some Styling

While CSS is primarily used for styling, you can add inline styles directly in your HTML to change the look of your page. For example:

<style>
    body {
        font-family: Arial, sans-serif;
    }
    header, section {
        margin-bottom: 20px;
    }
    nav ul {
        list-style-type: none;
    }
    nav ul li {
        display: inline;
        margin-right: 10px;
    }
</style>

Place this <style> tag within the <head> section of your HTML.

Test and Iterate

This is how the full portfolio.html file should look like:

<!DOCTYPE html>
<html>
<head>
    <title>My Personal Portfolio</title>

    <style>
        body {
            font-family: Arial, sans-serif;
        }
        header, section {
            margin-bottom: 20px;
        }
        nav ul {
            list-style-type: none;
        }
        nav ul li {
            display: inline;
            margin-right: 10px;
        }
    </style>
</head>
<body>
    <header>
        <h1>John Doe</h1>
        <nav>
            <ul>
                <li><a href="#about">About Me</a></li>
                <li><a href="#projects">Projects</a></li>
                <li><a href="#contact">Contact</a></li>
            </ul>
        </nav>
    </header>

    <section id="about">
        <h2>About Me</h2>
        <img src="profile.jpg" alt="John Doe" />
        <p>Hi, I'm John Doe, a passionate web developer...</p>
    </section>

    <section id="projects">
        <h2>Projects</h2>
        <ul>
            <li><a href="http://example.com/project1">Project 1</a></li>
            <li><a href="http://example.com/project2">Project 2</a></li>
            <!-- Add more projects here -->
        </ul>
    </section>

    <section id="contact">
        <h2>Contact Me</h2>
        <p>Email: [email protected]</p>
        <!-- You can add a contact form here in future -->
    </section>
</body>
</html>

Open it up in a browser to see your work. It won’t look like a polished website yet, but you’ll see all your content structured according to your HTML.

Conclusion

Congratulations! You’ve just built a simple yet functional personal portfolio site using HTML. This is just the beginning of your journey into web development. As you learn more about HTML, CSS, and JavaScript, you’ll be able to enhance your site with better styling and functionality. Keep experimenting, keep learning, and most importantly, have fun with it!

In the next blog post we’ll cover CSS and JavaScript.

JavaScript

Using Puppeteer to automate ASC Analytics screenshots

TL;DR

I was trying to automate my manual process of logging into ASC (App Store Connect) and taking a screenshot of the crashes, and preparing a report that I do weekly. This blog post explains the exact steps of how I achieved that.

Why Puppeteer?

I’m not affiliated with it in any way, and I also tried Cypress and CasperJS but ran into rather weird problems during installation. Puppeteer just worked straight out of the box, and that’s why I continued down that path.

Install Puppeteer

On Puppeteer’s Github page it’s defined as:

a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. Puppeteer runs headless by default, but can be configured to run full (non-headless) Chrome or Chromium.

Here are a few examples of what you can do with Puppeteer (although they say that you can automate almost everything that you do manually in the browser):

  • Crawl a SPA (Single-Page Application) and generate pre-rendered content, and take screenshots
  • Automate form submission, UI testing, keyboard input
  • Create an automated testing environment: run your tests directly in the latest version of Chrome

To install it, make sure you have Node.js installed, and then run npm install puppeteer in your terminal.

A simple example using Puppeteer

Here’s a simple example to get you started; create a google.js file and paste this code in it:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://google.com');
  await page.screenshot({ path: 'google-screenshot.png' });

  await browser.close();
})();

This script will navigate to https://google.com and save a screenshot in an image named google-screenshot.png.

Execute the script in the command line with: node google.js and you should get a PNG image of how the https://google.com page would look like in your browser:

Mind you, the text you’re seeing is Croatian, as that’s my locale setting.

Looks a bit ‘small’, doesn’t it? That’s because Puppeteer sets an initial page size to 800 × 600 px, which defines the screenshot size. The page size can be customized with Page.setViewport().

Here’s the code example that’s using 1920 x 1080 px viewport size:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  await page.setViewport({
    width: 1920,
    height: 1080,
    deviceScaleFactor: 1,
  });

  await page.goto('https://google.com');
  await page.screenshot({ path: 'google-screenshot.png' });

  await browser.close();
})();

Run it again with node google.js, and the screenshot should now look like this:

For more options (as I won’t go into explaining every API call of the final code) make sure to check their documentation.

The Code™

Below is the quick and dirty code that JustWorks™ in logging into your App Store Connect account (please make sure to edit the script with your credentials), navigating to the Analytics screen and taking a screenshot.

I say quick and dirty because there are a myriad of improvements that could be done on this code:

  • Cookie support (so you don’t have to log in and insert the OTP every time)
  • Typescript (for type safety, and other benefits)

The code is also available on Github, and if you’ve got the skills and the desire, please make those changes and create a PR.

const ACCOUNT = '[email protected]';
const PASSWORD = 'YourVeryMuchSuperStrongPa$$word';
const LOGIN_FRAME = '#aid-auth-widget-iFrame';
const ASC_URL = 'https://appstoreconnect.apple.com/';
const ANALYTICS_URL = 'https://appstoreconnect.apple.com/analytics';
const APP_CRASHES_URL = 'theSpecificUrlToYourSpecificApp'; //figure this out by copying the URL manually

const puppeteer = require('puppeteer');
const readline = require('readline');

const log = (msg) => {
    console.log(msg);
}

const clickSignInButton = async (frame) => {
    log('Clicked the Sign In button');

    const element = await frame.waitForSelector(
        '#stepEl > sign-in > #signin > .container > #sign-in:not(disabled)'
    );

    await element.click();
};

const clickTrustBrowser = async (frame) => {
    log('Clicked the Trust Browser button');

    const selector = 'button.trust-browser';
    const element = await frame.waitForSelector(selector);
    await element.click();
};

const askForVerificationCode = () => {
    log('Asking for verification code');

    const readlineInterface = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
    });

    return new Promise(resolve => {
        readlineInterface.question(
            'Please enter your code: ',
            answer => {
                console.log(`Thanks, you entered: ${answer}`);
                readlineInterface.close();
                resolve(answer);
            }
    );
  });
};

const login = async (page, user, password) => {
    log('Login page');
    const frameElement = await page.$(LOGIN_FRAME);
    if (!frameElement) {
        throw new Error(`Missing frame ${LOGIN_FRAME}`);
    }

    const frame = await frameElement.contentFrame();
    if (!frame) {
        throw new Error(`Missing frame ${LOGIN_FRAME}`);
    }

    const ACCOUNTInputSelector = '#account_name_text_field';
    await frame.waitForSelector(ACCOUNTInputSelector);

    await frame.focus(ACCOUNTInputSelector);
    await page.keyboard.type(user);

    await clickSignInButton(frame);

    const passwordInputSelector = '#password_text_field';
    await frame.waitForSelector(passwordInputSelector);
    await frame.waitForTimeout(2000);

    await frame.focus(passwordInputSelector);
    await page.keyboard.type(password);
    await clickSignInButton(frame);

    const verifyDeviceSelector = 'verify-phone';
    await frame.waitForSelector(`${verifyDeviceSelector}`);
    const isVerifyDevice = await frame.$(verifyDeviceSelector);

    if (isVerifyDevice) {
        log('Verify phone step');
        const verificationCode = await askForVerificationCode();
        await page.keyboard.type(verificationCode);
        await clickTrustBrowser(frame);
    }
};

const main = async () => {
    const browser = await puppeteer.launch({
        // set this to false if you want to open a browser instance and see what your
        // script is doing, where it's clicking, what it's filling out, etc.
        headless: false 
    });

    const page = await browser.newPage();
    await page.setViewport({
        // settings for my personal need, set this as you wish
        width: 1440,
        height: 815,
        deviceScaleFactor: 1,
    });

    log(`Oppening ${ASC_URL}`);
    await page.goto(ASC_URL);

    await page.waitForSelector(`${LOGIN_FRAME}`);
    await login(page, ACCOUNT, PASSWORD);

    await page.waitForNavigation({ waitUntil: 'networkidle2' });
    await page.waitForSelector('.main-nav-label');

    await page.goto(`${ANALYTICS_URL}`);
    await page.goto(`${APP_CRASHES_URL}`);

    await page.waitForNavigation({ waitUntil: 'networkidle2' });
    await page.waitForSelector('#appanalytics');
    // sometimes the selector will load, but not the whole content, so you may
    // want to play with the waitForTimeout. The argument is in mili seconds
    //await page.waitForTimeout(5000); 

    await page.screenshot({ path: 'app_analytics_crashes.png' });

    log('Closing the browser page');
    await browser.close();
};

main().catch(error => console.error(error));

When you run this code, in the terminal you should see this output:

➜ node apple.js               
Oppening https://appstoreconnect.apple.com/
Login page
Clicked the Sign In button
Clicked the Sign In button
Verify phone step
Asking for verification code
Please enter your code: 866216
Thanks, you entered: 866216
Clicked the Trust Browser button
Closing the browser page

The OTP code to log in to Apple, in my case, was sent to me via an SMS.

The screenshot (blurred for obvious reasons), looks like this in my case:

That’s great, but what if my login requires CAPTCHA?

In my particular case, the login was rather simple (except for the fact that I had to select the frame and search within it for the element IDs).

But, what if your login requires you to enter the CAPTCHA? You know, those "are you human" popups that you get when logging into some website. Then I had a ‘great idea’ – what if you make a service where people actually read and type these CAPTCHAs for you?

⚠️ Now, let me be very clear – just because something is possible to do automatically, it doesn’t give you the right to use that in a bad way (spammers be gone!). Do with this information what you wish, but don’t come crying back saying "he made me do it".

Yes, no drumrolls needed, as almost everything today – this also already exists. CAPTCHA solving software exists and it’s a thing, and I found a blog posts that reviews 10 CAPTCHA-solving software.

One that caught my eye was 2captcha.com, and I found a blog post that did a thorough teardown of the service. The reason that one caught my eye was that it seems that they offer their solution through libraries for most of the popular programming languages (Python, PHP, JS,Go, C#, Ruby).

A trip down the memory lane

Sorry for the sidetrack (and you can safely skip this section 😅), but I just remembered how, years ago, I attended a conference where Photomath’s (the app that solves math problems and shows you the steps of how to solve them) CEO was talking about how they’re solving the OCR problem (they supposedly had a breakthrough when they applied ML). They built an awesome business, that’s actually useful, and I saw that they recently integrated into Snapchat via their Snapchat Solver 🤯 . I digress, yes, but with that good ‘tech’, I see them integrated into everything and that makes me proud (since, at the time, they were a Croatian-based startup).

Conclusion

Hope this straight-to-the-point post (with one slight detour 😅) showed you how you can make use of the amazing Puppeteer for your (legit) purposes.

Stay safe and code on 💪

Daily Thoughts, JavaScript

const life = change();

They (supposedly, originally, Heraclitus) say that the only true constant in life is change.

A JavaScript programmer in you might write that like this:

const life = change();

And, the trick in real life is that the function’s implementation regularly looks like this:

function change() {
    return Math.random();
}

However, when you’d try to output the value of the life constant (see it in action in JS Fiddle), it would be exactly that, a constant (no matter how many times you’d call it or output it).

Sidenote: if you’re puzzled by the fact that you can assign a function to a constant (or to a variable for that matter) before it was defined in the code, then go and learn about hoisting in JavaScript.

Now, you may write the above statement like this:

const life = function change() {
    return Math.random();
}

Now, if you call the life function (again, see it in action), it will return a different value every time you call it.

⚠️ JavaScript gurus among you may chuckle at assigning a function to a constant, but check this StackOverflow answer for its applicability – plus, you’ll learn (or, refresh your memory) about hoisting in JS.

Switching gears; the point of all this is that you can’t expect to be doing the same thing, and getting different results, and no matter what obstacles you face, the key to overcoming them is not in changing the event, but in changing yourself and how you react to it. And that itself is a process. A process that begins with the desire or acceptance to be teachable and improve for the better.

Hope you like this attempt at mixing programming with personal growth topics (for more, check my daily thoughts entries).

Stay safe, friends ❤️


This is why I write these ‘daily thoughts’ posts.

JavaScript, Vue.js

Getting started with Vue.js 3 by building a Giphy search application

TL;DR

In this post, you’ll learn how to use Vue CLI to build a Vue.js 3 application for searching Giphy’s GIFs by using their API.

Introduction – The War is Over

Let’s be honest; even those of us that actually don’t hate the JavaScript ecosystem can understand the frustration with the fact that new JS frameworks are popping up all the time.

This was especially true some 2-3 years ago. However, it seems that the craziness has settled down a bit and that the war is seemingly over. For now. These top four (top 5 lists are overrated 😉) frameworks established themselves:

  • Angular (71k)
  • React (165k)
  • Svelte (45k)
  • Vue.js (181k)

I added the number of Github `*’s (at the time of this writing), but I don’t want you to read into that too much 🤗

Analysis paralysis will, well, paralyze you!

You could argue that there are other options like Ember, Mithril, or good ‘ol jQuery even! However, this tweet says it all:

Developers are fighting over which frontend framework is better.. it’s not like users know or care. They mostly care about the User Experience. UX should be our focus on the frontend.

Personally, I stuck to the Angular bandwagon ever since version 1.0, but that started losing it’s ooomph after version 2.

I believe that Vue.js (due to its progressive and flexible nature) is a perfect fit for the teams that have to rewrite old codebases one step at a time. However, you can also use it as a full-blown framework if you wish.

Personally, React’s JSX just seems wrong, and some people, smarter than me, swear by that being TheRightWay™, and I guess we’re not here to discuss tastes…

General’ framework war’ kind of questions I tend to answer in the following way:

Please just stop with the analysis paralysis already.

Do your research, pick a framework (any framework for that matter) that the community is using, use it, and see how far it gets you.

All these talks about X being slow or Y being better just make no sense until you try it yourself for your use case and preference.

Besides, nowadays the speed will not be a deciding factor among the top JS frameworks.

With all this out of the way, fasten your seatbelts, take a venti (or trenta) sized cup of coffee, and let’s go do something practical! 💪

If you care about the hardcore metrics ⏱️ and want to check out some detailed comparisons between popular frontend frameworks, here are a few blog posts.

The Demo App

As said in the intro, we’ll build an application for searching (and showing) Giphy GIFs by using their API.

You can fork the complete source code on Github.

I’m using Vue.js 3, and I’ll be referring to it as just Vue.js in the rest of this post.

Prerequisites

Make sure that you have the following tools installed:

  • Node.js – here’s a free but outdated step by step guide/book for both Windows and Mac. Or, really, just go to the main website and download the executable for your machine
  • Git – here’s a fun getting started tutorial in case you’re new to it

Vue CLI

As their docs say:

Vue CLI aims to be the standard tooling baseline for the Vue ecosystem. It ensures the various build tools work smoothly together with sensible defaults so you can focus on writing your app instead of spending days wrangling with configurations. At the same time, it still offers the flexibility to tweak the config of each tool without the need for ejecting.

To install Vue CLI, run the following command (in your Terminal):

npm install -g @vue/cli

You can confirm that the installation went well if you run vue and get an output with usage information.

⚠️ Just for reference (in case you follow this tutorial at a later stage and something is not the same as I output it here), my version (get the output by running vue --version in your Terminal) as of this writing is @vue/cli 4.5.12.

Start a new app with Vue CLI

We’ll call our app, originally 🙈, giphy-search. So, to start a new app using Vue CLI, run:

vue create giphy-search

Make sure you select the option ❯ Default (Vue 3 Preview) ([Vue 3] babel, eslint) and press ENTER.

You should get an output similar to this:

Vue CLI v4.5.12
✨  Creating project in /Users/nikola/DEV/BLOG/giphy-search.
🗃  Initializing git repository...
⚙️  Installing CLI plugins. This might take a while...
🚀  Invoking generators...
📦  Installing additional dependencies...
📄  Generating README.md...
🎉  Successfully created project giphy-search.

After this command finishes, let’s cd into the project and run it:

cd giphy-search
npm run serve

You should get this output:

DONE  Compiled successfully in 3178ms

App running at:
  - Local:   http://localhost:8080/
  - Network: http://192.168.8.189:8080/

Note that the development build is not optimized.
To create a production build, run npm run build.

You should see the following page in your browser if you open http://localhost:8080.

Folder structure

Now, let’s open this project in the editor of your choice (I’m using Visual Studio Code), and you should see something like this:

This is an introduction tutorial, to get you running fast, so I won’t be going into any specific details and will be only focusing on the src folder.

Add content

OK, so let’s add something to our app.

But, where to start? 🤔

Well, one of the first things I do when I come to a project for the first time is to look at the generated output. Then I try to find the strings corresponding to that output within the source code.

So, if you search for the string Welcome to Your Vue.js App, you’ll see the string is within the App.vue file. This file contains the following code:

<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <HelloWorld msg="Welcome to Your Vue.js App"/>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    HelloWorld
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Without knowing much about Vue.js, you can see where you would change the Welcome to Your Vue.js App text. So, let’s change that to Welcome to GiphySearch. While you do that, also remove the contents of the style tag.

In the App.vue file, we have a template tag that contains a standard HTML img tag and an HelloWorld tag, which is a so-called component. Here’s the link to the documentation where you can learn more about components.

Add an input and a button

Our basic application should have one input field and one button.

To do this, adjust the template in the HelloWorld.vue file to contain only this:

<div class="hello">
    <h1>{{ msg }}</h1>

    <input name="search" />
    <button>Search</button>
</div>

Actions

Having a simple search input field and a button doesn’t do much. We want to click the button, and we want to output something to the console just to verify it’s working correctly.

So, this is how you define a function that will handle a button click in Vue.js:

<button @click="performSearch">Search</button>

But, if you open your DevTools now and click the Search button, you’ll get an error like:

runtime-core.esm-bundler.js?5c40:38 [Vue warn]: Unhandled error during execution of native event handler
  at <HelloWorld msg="Welcome to GiphySearch" >
  at <App>
warn @ runtime-core.esm-bundler.js?5c40:38
logError @ runtime-core.esm-bundler.js?5c40:211
handleError @ runtime-core.esm-bundler.js?5c40:203
callWithErrorHandling @ runtime-core.esm-bundler.js?5c40:157
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js?5c40:163
invoker @ runtime-dom.esm-bundler.js?830f:327
runtime-core.esm-bundler.js?5c40:217 Uncaught TypeError: _ctx.performSearch is not a function
    at Object.onClick._cache.<computed>._cache.<computed> (HelloWorld.vue?fdab:6)
    at callWithErrorHandling (runtime-core.esm-bundler.js?5c40:154)
    at callWithAsyncErrorHandling (runtime-core.esm-bundler.js?5c40:163)
    at HTMLButtonElement.invoker (runtime-dom.esm-bundler.js?830f:327)

That’s because we haven’t defined the performSearch function anywhere.

Let’s do that now. In the HelloWorld.vue file, add the following function definition inside the script tag, methods object property:

<script>
export default {
    name: 'HelloWorld',
    props: {
        msg: String
    },
    methods: {
        performSearch() {
            console.log('clicked');
        }
    }
};
</script>

We defined the performSearch function, which doesn’t accept any parameters and it doesn’t return anything. It just outputs clicked in the console.

Gather input

What if we would like to output (to the console) the string that someone typed in the input field?

Well, first we need to add a new attribute to the input field:

<input name="title" v-model="searchTerm">

The v-model is a directive that instructs Vue.js to bind the input to the new searchTerm variable.

Finally, change the performSearch function to this:

performSearch: function() {
    console.log(this.searchTerm);
}

Technically, you could write this a bit differently, passing the searchTerm variable into the performSearch function as a parameter:

performSearch: function(searchTerm) {
   console.log(searchTerm);
}

<button @click="performSearch(searchTerm)">Search</button>

I'd love to hear from you in the comments which approach is better in your opinion.

Giphy search API

Finally, we come to the cool part 🥶, and that is to fetch some data from the Giphy API and show it in our app.

How do we get this API? Well, if you do a simple Google search for giphy api and open the first link, you’ll get the documentation for their API.

You need to follow these instructions to create an account (and an app) to get an API key. You don’t need an SDK solution at this point (you may want to go that route if you’re going to make a mobile app that shows these images).

After you obtain your API key through their dashboard, you’re going to be able to make search (they offer a bunch more) API requests through their API explorer.

An API URL (obviously, with the fake API key) may look like this: https://api.giphy.com/v1/gifs/search?api_key=AbC&q=cats&limit=10&offset=0&rating=g&lang=en

Great, now we see what kind of a request we need to create to search Giphy’s GIF database for a certain term.

If you open this link in the browser, you’ll see that the service returns something like:

In the next section, we’ll cover retrieving this data within our app.

⚠️ Since Giphy’s rules now require you to add attribution, make sure you add it. I added the image in the footer with a link to their docs.

Vue.js HTTP requests

We have several options for making HTTP requests in Vue.js, and some may see this flexibility as a pro, and some may see it as a con. Either way, for more options, check out this post.

I chose to go with Axios, as it’s a very popular JavaScript library for making HTTP requests. It’s an HTTP client that makes use of the modern Promises API by default (instead of the not so nice JavaScript callbacks 🙂) and runs on both the client and the server (i.e., Node.js).

In your Terminal enter the following command to install Axios via npm:

npm install axios --save

Import Axios in the HelloWorld.vue file just after the script tag:

import axios from "axios";

The performSearch function should now look like this:

let API_KEY = 'getYourOwn';
let link = `https://api.giphy.com/v1/gifs/search?api_key=${API_KEY}&limit=25&offset=0&rating=g&lang=en&q=`;
let apiLink = link + this.searchTerm;

axios
    .get(apiLink)
    .then((response) => {
        console.log(response);
    })
    .catch((error) => {
        console.log(error);
    });

Just for reference, to put it all in one listing, the contents of the HelloWorld.vue file should be:

<template>
    <div class="hello">
        <h1>{{ msg }}</h1>

        <input name="search" v-model="searchTerm" />
        <button @click="performSearch()">Search</button>
    </div>
    <a href="https://developers.giphy.com/docs/api#quick-start-guide">
        <img src="giphy.png" alt="" class="giphyAttribution" />
    </a>
</template>






If you run the app and enter something in the input box, and then click the search button, you’ll see something like this in your console log:

You can see that we’re getting an object that in its data property has another data property (so meta 😎) that’s an array with 25 objects, which holds the information about the images we’ll show in our app.

Well, after a tongue twister in the previous sentence, this is all great, but we don’t want to be logging our objects to the console; we want to show them in our app! 🙂

To show the GIF, you can choose any of the properties under the images object, and use the url property. I used fixed_height_small in my example.

We don’t want to just show one image but all of the images. We can use the v-for directive for that:

<img v-for="g in giphies" :key="g.id" :src="g.images.fixed_height_small.url" />

For reference, here’s the full listing of HelloWorld.vue file:

<template>
    <div class="hello">
        <h1>{{ msg }}</h1>

        <input name="search" v-model="searchTerm" />
        <button @click="performSearch()">Search</button>

        <img v-for="g in giphies" :key="g.id" :src="g.images.fixed_height_small.url" />
    </div>
    <a href="https://developers.giphy.com/docs/api#quick-start-guide">
        <img src="giphy.png" alt="" class="giphyAttribution" />
    </a>
</template>






B e a utiful

At this point, if we take a look at the app and search, for example, for ‘cat coding’ we’ll get this:

Although the result doesn’t look sleek (ha!, far from it 😳), our code does exactly what it’s supposed to do. If you want it to look nicer, feel free to add more CSS.

Bulma is a cool framework that I used in the JWT authentication in an Angular application with a Go backend post in case you’re interested in taking a look.

Conclusion

In this tutorial, you learned how to get started with using Vue.js 3 by building an application for searching Giphy’s GIFs by using their API.

Please leave any comments and feedback in the discussion section below, and thank you for reading!

JavaScript

Intro to OOP in JavaScript

TL;DR

This post will not be a regular step by step post as I usually do it; this will rather be a collection of some notes from a recent lecture I did. However, I hope you still find it useful.

OOP – Object Oriented Programming

  • an object has a state (represented by properties) and a behavior (represented by methods)
  • functions on an object are called methods
  • a developer doesn’t need to know how an object’s methods work under the hood to be able to use them (encapsulation)
  • a lot of times it makes sense to model real-life objects as objects in the code

⚠️ interesting thing in JavaScript is that arrays are actually objects

You can test it like this: console.log(typeof []);

It’s an object because it has a property like length, and methods like pop and push

object literal

const person = {
    name: 'Nikola',
    age: 33,
    walk: function() {
        console.log("I'm walking");
    }
}

console.log(person.name);

There’s also this thing called ‘bracket notation’:

person['name']

person['walk']();

Here’s how you would output all of the object’s properties with jQuery:

$.each(person, function(i,o){
    console.log(i, o);
});

this in JavaScript

  • normal function calls
function f(){
    console.log(this); // Window object
}

f();
  • object methods
var obj = {
    a: "test",
    b: "test 2",
    log: function() {
        console.log(this.a);
    }
}

obj.log();
  • constructed object methods
var Person = function(name, surname) {
    this.name = name || 'Marko';
    this.surname = surname || 'Maric';

    this.log = function() {
        console.log(this.name + " " + this.surname);
    }
}

var pero = new Person('Pero', 'Peric'); // constructor function
pero.log();

JavaScript Class Syntax

Object literals fall short when you have to create multiple of them. Having to type each of them out and then in case of a change, change it in all the places would be unmanageable.

Class is basically a blueprint for objects that will be created with this blueprint. It contains some base set of properties and methods. The class syntax is new to JS in ES2015 and it’s actually something called ‘syntactic sugar’. It has the same functionality as the prototype syntax, and it’s actually still using prototypes under the hood.

class Pet {
    constructor(animal, age, bread, sound) {
        this.animal = animal;
        this.age = age;
        this.bread = bread;
        this.sound = sound;
    }

    speak() {
        console.log(this.sound);
    }
}

const djoni = new Pet('dog', 1, 'bulldog', 'vau vau');

console.log(djoni);

getters

class Pet {
    constructor(animal, age, bread, sound) {
        this.animal = animal;
        this.age = age;
        this.bread = bread;
        this.sound = sound;
    }

    get activity() {
        const today = new Date();
        const hour = t.getHours();

        if (hour > 8 && hour < 20) {
            return 'playing';
        }
        else {
            return 'sleeping';
        }
    }

    speak() {
        console.log(this.sound);
    }
}

const djoni = new Pet('dog', 1, 'bulldog', 'vau vau');

console.log(djoni.activity); // activity is a so-called 'computed property'.

setters

get owner() {
    return this._owner.
}

set owner(owner) { // MUST have exactly one formal parameter!
    this._owner = owner; // this is called a 'backing property'
}

djoni.owner = 'Nikola';
JavaScript

Making AJAX calls using the Fetch API

TL;DR

In this post we’ll do everything we did in the second post, but with Fetch API.

What’s this Fetch API?

The almighty docs say

The Fetch API provides an interface for fetching resources (including across the network). It will seem familiar to anyone who has used XMLHttpRequest (see the first post in the series on how to do that), but the new API provides a more powerful and flexible feature set.

I prepared this demo page so that you can test.

You’ll remember from the last post that in order to make an AJAX call with jQuery, you have to do something like this:

$('#result').load('http://nikola-breznjak.com/_testings/ajax/test1.php?ime=Nikola');

Go ahead and run that code on the demo page in the browser’s dev tools Console (consult the last post if you’re stuck).

Now, the very same thing with the Fetch API looks like this:

fetch('http://nikola-breznjak.com/_testings/ajax/test1.php?ime=Nikola')
  .then(function(response) {
    return response.text();
  })
  .then(function(text) {
    $('#result').html(text);
  });

Go ahead and try it in the Console. Change the ime parameter, and you’ll see that the text on the page will change to Hello [name], where [name] will be the parameter you entered. Note that in the example above I still used jQuery to set the content of the div with id result.

The docs have way more info on this but, as they say, the difference between fetch() and $.ajax() is in two main things:

  • The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500. Instead, it will resolve normally (with ok status set to false), and it will only reject on network failure or if anything prevented the request from completing.
  • By default, fetch() won’t send or receive any cookies from the server, resulting in unauthenticated requests if the site relies on maintaining a user session (to send cookies, the credentials init option must be set).

⚠️ At a later point you may want to read a bit more about Promises in Javascript.

Rewriting the mini project

I encourage you to try it for yourself and then check your solution to mine.

The mini project (which you can test here) would be rewritten like this:

var link = 'http://nikola-breznjak.com/_testings/ajax/test2.php';
fetch(link)
    .then(function(response){
        return response.json()
    })
    .then(function(result){
        var oglasiHTML = '';
        $.each(result, function(index, oglas){
            var klasaCijene = '';
            if (oglas.cijena < 100){
                klasaCijene = 'is-success';
            }
            else if (oglas.cijena >= 100 && oglas.cijena < 300){
                klasaCijene = 'is-info';
            }
            else if (oglas.cijena >= 300){
                klasaCijene = 'is-danger';
            }

            oglasiHTML += `
                <div class="columns">
                    <div class="column is-one-quarter">
                        <span class="tag ${klasaCijene}">${oglas.cijena}</span>
                    </div>
                    <div class="column">${oglas.tekst}</div>
                </div>
            `;
        });

        $('#oglasi').html(oglasiHTML);
    });

There are a few things that I’d like to point out here:

  • I used the response.json() because I know that this API returns a JSON response (you can check by opening that link in the browser).
  • I used the jQuery each function to iterate over the result.
  • I used the template literals to construct the final oglasiHTML in a much cleaner way than we did that in the previous post with using concatenation.

I want more examples

Say you have this demo page, and you need to make the newsletter signup form use AJAX instead of going to a new page when you click the Subscribe button. Right now the API (located at http://nikola-breznjak.com/_testings/ajax/email.php) doesn’t actually process what you send to it but try to send data with the request as well for practice.

How would you do it? Where would you start?

Well, here are a few search terms you could Google:

  • `fetch api submit data’

Again, here’s my solution, but I encourage you to try it yourself first.

$('form').submit(function(ev){
    ev.preventDefault();
    var url = $(this).attr('action');
    var formData = $(this).serialize();
    fetch(url, {method: 'post', body: formData})
        .then(function(response){
            $('#newsletter').html('Thank you for subscribing, please check your email.');
        });
});

There are a few things that I’d like to point out here:

  • I was able to use the form selector without any id because it’s the only form on the page. If there were more forms on the page, I’d have to distinguish them by using an id (which is always a good practice, even if it’s just one form)
  • I used jQuery’s submit function to set an event handler that will be executed when the form will be submitted (Subscribe button clicked, or ENTER pressed while in one of the fields)
  • I used the ev.preventDefault() to prevent the form from “doing its thing” and loading the ’email.php’ page, as its default behavior
  • I used $(this).attr('action') to extract the API URL from the form definition it the HTML (feel free to check it out in the element inspector)
  • I used $(this).serialize() to encode a set of form elements as a string which I then passed to body attribute of the fetch settings object. This function sends a request to the server using the POST method (using the method setting on the settings object), which (as we learned in the previous post) is the preferred way of sending some sensitive data that needs to be handled on the server
  • I used the html function on a div with the id newsletter to set its new content

Can I have one more, please?

Sure, but promise you’ll do it yourself first!

I do ?

OK then, let’s do this ?

Here’s a new demo page that you’ll use for this example.

⚠️ I’m using Bulma to make the site look pretty. As their website says:
‘Bulma is a free, open-source CSS framework based on Flexbox and used by more than 150,000 developers.’
I’ll just say it’s really great and easy to learn, so make sure you check it out.

In this challenge, you have to create a gif search application using the Giphy Search API. As the docs state, you’ll need to get the API key by creating an app. Once you do that, you’ll be able to search their API like, for example, this: http://api.giphy.com/v1/gifs/search?q=funny+cat&api_key=dc6zaTOxFJmzC. You can use this API key, but be sure to create your own if this one gets banned for too many requests ?‍

Once you fetch the gifs (or still images if you so choose), show them in the div with a class result (yes, class, not id ?).

Here’s the part where you roll up your sleeves, create it, feel proud of yourself ?, and then come here to check how I did it.

$('form').submit(function(ev){
    ev.preventDefault();
    var searchTerm = $('input').val();

    var apiLink = "http://api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&q=" + searchTerm;

    var output = '';
    fetch(apiLink)
        .then(function(response){
            return response.json();
        })
        .then(function(images){
            $.each(images.data, function(i, im){
                output += `<img src="${im.images.downsized.url}"/>`;
            });

            $('.result').html(output);
        });
});

As you may notice, very few things changed from the last post, and it’s quite easy to exchange the jQuery AJAX calls with Fetch API.

Conclusion

That’s all for this blog post. I hope it was useful and that you’ve seen how easy it is to make AJAX requests with the Fetch API. I also hope you liked the demo exercises ?
This wraps it up with these three post series. Have a great time and see you soon! ?

JavaScript

Making AJAX calls using jQuery

TL;DR

In this post we’ll do everything (and a bit more ?) we did in the first post, but with jQuery.

Why is jQuery better?

I prepared this demo page so that you can test.

You’ll remember from the last post that in order to make an AJAX call in pure JavaScript, you have to do something like this:

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
    if (xhr.readyState === 4){
        document.getElementById('result').innerHTML = xhr.responseText;
    }
};
xhr.open('GET', 'http://nikola-breznjak.com/_testings/ajax/test1.php?ime=Nikola');
xhr.send();

Go ahead and run that code on the demo page in the browser’s dev tools Console (consult the last post if you’re stuck).

Now, to do the very same thing with jQuery, all you have to do is this:

$('#result').load('http://nikola-breznjak.com/_testings/ajax/test1.php?ime=Nikola');

Go ahead and try it in the Console. Change the ime parameter, and you’ll see that the text on the page will change to Hello [name], where [name] will be the parameter you entered.

⚠️ I won’t go into the basics of using jQuery, as it’s been here for a very long time, and there’s a vast amount of tutorials written about it. Start with the official docs if you want to learn more.

The important thing to note here is that .load must be chained to a so-called jQuery selection, as mentioned in the .load() documentation. We loaded the data from the server and placed the returned text into the element with an id result.

Shorthand methods

By looking at the docs, you can see that there are more of these so-called shorthand methods for making AJAX calls, all of which could be done with the most versatile ajax function.

The example from before could be done using the get shorthand method:

    var link = 'http://nikola-breznjak.com/_testings/ajax/test1.php';
    var data = {ime:"Nikola"};
    var callback = function(result) {
        $('#result').html(result);
    }
    $.get(link, data, callback);

If you don’t want to pass any parameters, it gets even simpler:

    var link = 'http://nikola-breznjak.com/_testings/ajax/test1.php';
    var callback = function(result) {
        $('#result').html(result);
    }
    $.get(link, callback);

Rewriting the mini project

I prepared the same demo page as in the previous post, but with jQuery already included so you can test this by just pasting the code below in the Console. I encourage you first to try it yourself and then compare your solution with mine below.

var link = 'http://nikola-breznjak.com/_testings/ajax/test2.php';
$.getJSON(link, function(result){
    var oglasiHTML = '';
    $.each(result, function(index, oglas){
        var klasaCijene = '';
        if (oglas.cijena < 100){
            klasaCijene = 'is-success';
        }
        else if (oglas.cijena >= 100 && oglas.cijena < 300){
            klasaCijene = 'is-info';
        }
        else if (oglas.cijena >= 300){
            klasaCijene = 'is-danger';
        }

        oglasiHTML += `
            <div class="columns">
                <div class="column is-one-quarter">
                    <span class="tag ${klasaCijene}">${oglas.cijena}</span>
                </div>
                <div class="column">${oglas.tekst}</div>
            </div>
        `;
    });

    $('#oglasi').html(oglasiHTML);
});

There are a few things that I’d like to point out here:

  • I used the getJSON function to call my API located on the URL that’s saved in the link variable. This is because I know that this API returns a JSON response (you can check by opening that link in the browser).
  • I used the jQuery each function to iterate over the result.
  • I used the template literals to construct the final oglasiHTML in a much cleaner way than we did that in the previous post with using concatenation.
  • The core logic remained the same as in the first post, with the exception of not having to use the for loop and indexes to access each element of the array.

I want more examples

Say you have this demo page, and you need to make the newsletter signup form use AJAX instead of going to a new page when you click the Subscribe button. Right now the API (located at http://nikola-breznjak.com/_testings/ajax/email.php) doesn’t actually process what you send to it, but try to send data with the request as well for practice.

How would you do it? Where would you start?

Well, here are a few search terms you could Google:

  • `form submit jquery’
  • post data

Again, here’s my solution, but I encourage you to try it yourself first.

$('form').submit(function(ev){
    ev.preventDefault();
    var url = $(this).attr('action');
    var formData = $(this).serialize();
    $.post(url, formData, function(response){
        $('#newsletter').html('Thank you for subscribing, please check your email.');
    });
});

There are a few things that I’d like to point out here:

  • I was able to use the form selector without any id because it’s the only form on the page. If there were more forms on the page, I’d have to distinguish them by using an id (which is always a good practice, even if it’s just one form)
  • I used jQuery’s submit function to set an event handler that will be executed when the form will be submitted (Subscribe button clicked, or ENTER pressed while in one of the fields)
  • I used the ev.preventDefault() to prevent the form from “doing its thing” and loading the ’email.php’ page, as its default behavior
  • I used $(this).attr('action') to extract the API URL from the form definition it the HTML (feel free to check it out in the element inspector)
  • I used $(this).serialize() to encode a set of form elements as a string which I then passed to the jQuery post shorthand function. This function sends a request to the server using the POST method, which (as we learned in the previous post) is the preferred way of sending some sensitive data that needs to be handled on the server
  • I used the html function on a div with the id newsletter to set its new content

Can I have one more, please?

Sure, but promise you’ll do it yourself first!

I do ?

OK then, let’s do this ?

Here’s a new demo page that you’ll use for this example.

⚠️ I’m using Bulma to make the site look pretty. As their website says:
‘Bulma is a free, open source CSS framework based on Flexbox and used by more than 150,000 developers.’
I’ll just say it’s really great and easy to learn, so make sure you check it out.

In this challenge, you have to create a gif search application using the Giphy Search API. As the docs state, you’ll need to get the API key by creating an app. Once you do that, you’ll be able to search their API like, for example, this: http://api.giphy.com/v1/gifs/search?q=funny+cat&api_key=dc6zaTOxFJmzC. You can use this API key, but be sure to create your own if this one gets banned for too many requests ?‍

Once you fetch the gifs (or still images if you so choose), show them in the div with a class result (yes, class, not id ?).

Here’s the part where you roll up your sleeves, create it, feel proud of yourself ?, and then come here to check how I did it.

$('form').submit(function(ev){
    ev.preventDefault();
    var searchTerm = $('input').val();

    var apiLink = "http://api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&q=" + searchTerm;

    var output = '';
    $.getJSON(apiLink, function(images){
        $.each(images.data, function(i, im){
            output += `<img src="${im.images.downsized.url}"/>`;
        });

        $('.result').html(output);
    });
});

The only new, and potentially tricky, part here is the traversal of the JSON object that you get back from the API, but with just a bit training, you’ll get good at it.

If you open up this URL in your browser, you will see this

The returned JSON object consists of three properties:

  • data
  • pagination
  • meta

The data is an array of objects, and in one of its properties it has an array of images. In my example, I choose to display the downsized gif by accessing its url property.

Here’s a short gif of this in action:

Conclusion

That’s all for this blog post. I hope it was useful and that you’ve seen how much easier it is to make AJAX requests with jQuery vs. plain JavaScript. I also hope you liked the demo exercises ?
In the next blog post, we’ll see how to do this with the modern fetch API.

Page 1 of 131234»10...Last »

Recent posts

  • Discipline is also a talent
  • Play for the fun of it
  • The importance of failing
  • A fresh start
  • Perseverance

Categories

  • Android (3)
  • Books (114)
    • Programming (22)
  • CodeProject (35)
  • Daily Thoughts (77)
  • Go (3)
  • iOS (5)
  • JavaScript (127)
    • Angular (4)
    • Angular 2 (3)
    • Ionic (61)
    • Ionic2 (2)
    • Ionic3 (8)
    • MEAN (3)
    • NodeJS (27)
    • Phaser (1)
    • React (1)
    • Three.js (1)
    • Vue.js (2)
  • Leadership (1)
  • Meetups (8)
  • Miscellaneou$ (77)
    • Breaking News (8)
    • CodeSchool (2)
    • Hacker Games (3)
    • Pluralsight (7)
    • Projects (2)
    • Sublime Text (2)
  • PHP (6)
  • Quick tips (40)
  • Servers (8)
    • Heroku (1)
    • Linux (3)
  • Stack Overflow (81)
  • Unity3D (9)
  • Windows (8)
    • C# (2)
    • WPF (3)
  • Wordpress (2)

"There's no short-term solution for a long-term result." ~ Greg Plitt

"Everything around you that you call life was made up by people that were no smarter than you." ~ S. Jobs

"Hard work beats talent when talent doesn't work hard." ~ Tim Notke

© since 2016 - Nikola Brežnjak