On this tutorial, we’ll discover ways to create an “AI Quotes Generator” app with JavaScript. This app will display learn how to fetch information from the OpenAI API and generate partaking quotes for various classes or customized moods.
HTML Construction
The HTML construction will encompass the next parts:
- A button on the prime proper will open a modal that lets customers add their OpenAI API key.
- An enter that lets customers add a customized temper
- A number of choices populated with classes
- A button which, when clicked, will generate quotes from the OpenAI API.
As we’ve accomplished earlier than, we’ll use the Bootstrap framework to do a variety of the heavy lifting the place the UI is worried. The HTML Construction will seem like this:
1 |
<div class="position-absolute top-0 end-0 mt-2 me-3"> |
2 |
<button
|
3 |
id="api" |
4 |
kind="button" |
5 |
class="btn btn-primary" |
6 |
data-bs-toggle="modal" |
7 |
data-bs-target="#myModal" |
8 |
>
|
9 |
Add API Key |
10 |
</button>
|
11 |
</div>
|
12 |
<div class="container mt-5"> |
13 |
<div class="message alert alert-danger text-center" position="alert"></div> |
14 |
<div
|
15 |
class="modal fade" |
16 |
id="myModal" |
17 |
tabindex="-1" |
18 |
aria-labelledby="exampleModalLabel" |
19 |
aria-hidden="true" |
20 |
>
|
21 |
<div class="modal-dialog "> |
22 |
<div class="modal-content"> |
23 |
<div class="modal-header"> |
24 |
<h5 class="modal-title" id="exampleModalLabel"> |
25 |
Your API Key stays saved domestically in your browser |
26 |
</h5>
|
27 |
</div>
|
28 |
<div class="modal-body"> |
29 |
<div class="form-group"> |
30 |
<label for="apikey">API KEY</label> |
31 |
<enter kind="textual content" class="form-control" id="apikey" /> |
32 |
</div>
|
33 |
</div>
|
34 |
<div class="modal-footer"> |
35 |
<button
|
36 |
kind="button" |
37 |
class="btn btn-secondary" |
38 |
data-bs-dismiss="modal" |
39 |
>
|
40 |
Shut |
41 |
</button>
|
42 |
<button kind="button" class="btn btn-primary">Save</button> |
43 |
</div>
|
44 |
</div>
|
45 |
</div>
|
46 |
</div>
|
47 |
<h1 class="header text-center display-2 fw-bold">AI Quote Generator</h1> |
48 |
<!-- Foremost -->
|
49 |
<div class="d-md-flex h-md-100 my-5 align-items-center"> |
50 |
<div class="col-md-6 p-0 h-md-100"> |
51 |
<div class="d-md-flex align-items-center h-100 p-5 text-center justify-content-center category-wrapper"> |
52 |
<div class="pt-5 pb-5"> |
53 |
<p class="fs-5"> |
54 |
Create the proper quote based mostly in your present temper.. |
55 |
</p>
|
56 |
<enter
|
57 |
id="enter" |
58 |
title="temper" |
59 |
kind="textual content" |
60 |
placeholder="Enter your present temper" |
61 |
class="form-control mb-4 mx-auto w-75 text-center" |
62 |
fashion="width: 60%; show: inline-block" |
63 |
/>
|
64 |
</div>
|
65 |
</div>
|
66 |
</div>
|
67 |
<div class="col-md-6 p-0 h-md-100"> |
68 |
<div class="d-md-flex align-items-center h-md-100 p-5 text-center justify-content-center vstack"> |
69 |
<p class="fs-5"> |
70 |
..or select from our customized classes |
71 |
</p>
|
72 |
<div
|
73 |
class="quotes row justify-content-center mt-8 mb-4" |
74 |
>
|
75 |
<!-- classes will go right here -->
|
76 |
</div>
|
77 |
</div>
|
78 |
</div>
|
79 |
</div>
|
80 |
<!-- Finish Foremost -->
|
81 |
<div class="quotes-container text-center mt-4"> |
82 |
<button
|
83 |
id="generate" |
84 |
class="generate-btn btn btn-primary" |
85 |
kind="submit" |
86 |
>
|
87 |
Generate Quotes |
88 |
</button>
|
89 |
<div class="d-flex justify-content-center mt-3"> |
90 |
<div id="loader" class="spinner-border" position="standing"> |
91 |
</div>
|
92 |
</div>
|
93 |
</div>
|
94 |
</div>
|
95 |
</div>
|
96 |
<div class="container text-center mt-5 mb-4"> |
97 |
<div id="end result" class="row"> |
98 |
<!-- generate quotes will go right here -->
|
99 |
</div>
|
100 |
</div>
|
We are going to populate the quote classes dynamically with JavaScript, and as soon as we get the quotes from the OpenAI API, they are going to be populated within the outcomes container.
For this mission, we’ll additionally use Bootstrap’s jQuery instruments to allow the modal performance. Please embody the CDN hyperlinks within the header.
1 |
<!-- Bootstrap CSS -->
|
2 |
<hyperlink href="https://cdn.jsdelivr.internet/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="nameless" /> |
3 |
<!-- jQuery -->
|
4 |
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> |
5 |
<!-- Bootstrap JavaScript (requires jQuery and Popper.js) -->
|
6 |
<script src="https://cdn.jsdelivr.internet/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="nameless"></script> |
7 |
<script src="https://cdn.jsdelivr.internet/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js" integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy" crossorigin="nameless"></script> |
CSS Styling
Due to Bootstrap, we gained’t want quite a lot of customized types. However let’s add the customized CSS types we do want, together with the DM Mono net font.
1 |
@import url("https://fonts.googleapis.com/css2?household=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&show=swap"); |
2 |
|
3 |
physique { |
4 |
font-family: "DM Mono", monospace; |
5 |
}
|
6 |
.form-group { |
7 |
margin: 2rem 0; |
8 |
}
|
9 |
label { |
10 |
margin-bottom: 1rem; |
11 |
}
|
12 |
@media (min-width: 768px) { |
13 |
.category-wrapper { |
14 |
border-right: 2px strong #999; |
15 |
}
|
16 |
}
|
17 |
|
18 |
#loader, |
19 |
.message, |
20 |
.radio-group enter[type="radio"]{ |
21 |
show: none; |
22 |
}
|
23 |
|
Storing API KEY on Native Storage
For the applying to operate as anticipated, customers should add their API KEY to native storage to make sure persistence and safety. To attain this, we have now the “Add API KEY” button on the prime proper. The button is configured with the data-bs-target
attribute set to myModal
indicating that clicking it’ll set off the modal with the id “myModal” to be displayed.
1 |
<button
|
2 |
id="api" |
3 |
kind="button" |
4 |
class="btn btn-primary" |
5 |
data-bs-toggle="modal" |
6 |
data-bs-target="#myModal" |
7 |
>
|
8 |
Add API Key |
9 |
</button>
|
As soon as the modal is proven to the consumer, we’ll use jQuery to connect an occasion listener for the proven.bs.modal
occasion. Within the occasion operate, we’ll do the next:
- Get the API key worth from the consumer.
- save the worth to native storage,
- cover the modal
1 |
$("#myModal").on("proven.bs.modal", operate () { |
2 |
|
3 |
const saveButton = doc.querySelector("#myModal .btn-primary"); |
4 |
const apiKeyInput = doc.querySelector("#apikey"); |
5 |
|
6 |
saveButton.addEventListener("click on", operate () { |
7 |
const apiKeyValue = apiKeyInput.worth; |
8 |
localStorage.setItem("API_KEY", apiKeyValue); |
9 |
$("#myModal").modal("cover"); |
10 |
});
|
11 |
});
|
So once you click on the “Add API KEY” button, the modal will likely be displayed like this:
The following step is to outline and map our customized classes to the interface. Listed below are the classes. Be happy to outline your individual.
1 |
const classes = [ |
2 |
"motivation", |
3 |
"life", |
4 |
"hope", |
5 |
"funny", |
6 |
"love", |
7 |
"philosophy", |
8 |
"sadness", |
9 |
];
|
Let’s get the component with the category quotes that can home the classes.
1 |
const quotes = doc.querySelector(".quotes"); |
Subsequent, we’ll use the map()
methodology to generate an HTML markup of a label and an enter radio component for every class; the radio enter component will comprise the id
and worth
of the class, whereas the label will comprise the title
of the class.
1 |
const mappedCategories = classes.map((class) => { |
2 |
capitalizeText = class.charAt(0).toUpperCase() + class.slice(1); |
3 |
return `<enter |
4 |
kind="radio"
|
5 |
class="btn-check"
|
6 |
title="temper"
|
7 |
id="${class}" |
8 |
worth="${class}" |
9 |
autocomplete="off"
|
10 |
/>
|
11 |
<label
|
12 |
class="btn btn-secondary align-items-center justify-content-center fs-5"
|
13 |
for="${class}" |
14 |
>${capitalizeText}</label>`; |
15 |
});
|
16 |
quotes.innerHTML = mappedCategories.be a part of(""); |
Now the app appears like this:
OpenAI API Configuration
We’ve applied the add API Key performance. Now, let’s get the important thing from the OpenAI web site. If you happen to don’t have an account, go to the OpenAI website and join free.
When you present the required particulars, navigate to the documentation. Click on API KEY on the highest left , create your API key , copy and paste the important thing and retailer it in a safe location.
Utilizing the API Key
Create an async operate known as getData()
, which takes two parameters, specifically immediate
and API KEY
,
1 |
const getData = async (immediate, API_KEY) => { |
2 |
|
3 |
}
|
Contained in the operate, we need to use fetch()
operate to make a request to the OpenAI API and show the generated response to the consumer. Contained in the getData()
operate, add a try-catch
block with the next code.
1 |
const getData = async (immediate, API_KEY) => { |
2 |
|
3 |
attempt { |
4 |
const response = await fetch("https://api.openai.com/v1/chat/completions", { |
5 |
methodology: "POST", |
6 |
headers: { |
7 |
Authorization: `Bearer ${API_KEY}`, |
8 |
"Content material-Kind": "utility/json", |
9 |
},
|
10 |
physique: JSON.stringify({ |
11 |
mannequin: "gpt-3.5-turbo", |
12 |
messages: [ |
13 |
{
|
14 |
role: "user", |
15 |
content: `Generate 10 quotes about ${prompt}`, |
16 |
},
|
17 |
],
|
18 |
temperature: 0.7, |
19 |
}),
|
20 |
});
|
21 |
const information = await response.json(); |
22 |
|
23 |
return information; |
24 |
} catch (error) { |
25 |
|
26 |
return error; |
27 |
}
|
28 |
};
|
That is all of the code we have to get information from OpenAI . Let’s break down the code.
- We use the
fetch()
operate contained in the attempt block to make an asyncPOST
request to the desired url. - Within the request physique, we specify
gpt-3.5-turbo
because the mannequin for use; - The OpenAI API additionally expects an Authorization header containing the
API KEY,
and the physique of the request ought to be aJSON
object containing parameters such because themannequin
,immediate
, andtemperature
(signifies randomness of the responses; increased values point out extra randomness of the responses). - Lastly, we return the
response.json
object. In case of any errors, we additionally return theerror
object.
The info response appears like this;
As you possibly can see, the info we have to show to the consumer is contained within the selections
array. The info will likely be formatted earlier than being exhibited to the consumer.
The getData()
operate will likely be known as when the consumer clicks the generate quotes button. Let’s add an occasion listener to the generate button.
1 |
const generateBtn = doc.querySelector(".generate-btn"); |
2 |
generateBtn.addEventListener("click on", async (e) => { |
3 |
e.preventDefault(); |
4 |
|
5 |
}
|
When the press occasion happens, we need to execute a operate that does the next:
- Get the API KEY from native storage.
- If no API KEY is discovered on native storage, we’ll show an error, letting the consumer know they need to add their API KEY.
- Get the immediate from both a class or a customized immediate.
- Go the immediate and the APIKEY to the
getData()
operate. - Present a spinner when the applying is fetching the info.
- After getting a response, cease the spinner.
- Format the info and show it on bootstrap playing cards.
- In case of any error, we’ll show the suitable error message.
Replace the occasion listener operate as follows:
1 |
const generateBtn = doc.querySelector(".generate-btn"); |
2 |
generateBtn.addEventListener("click on", async (e) => { |
3 |
e.preventDefault(); |
4 |
const key = localStorage.getItem("API_KEY"); |
5 |
|
6 |
if (!key) { |
7 |
displayError("","Please add your OPENAI API Key, The KEY will likely be saved domestically in your browser"); |
8 |
return; |
9 |
}
|
10 |
|
11 |
let immediate = ""; |
12 |
let radio = doc.querySelector('enter[name="mood"]:checked'); |
13 |
|
14 |
if (doc.querySelector('enter[name="mood"]:checked')) { |
15 |
radio = doc.querySelector('enter[name="mood"]:checked'); |
16 |
immediate = radio.worth; |
17 |
} else { |
18 |
CustomInput = doc.getElementById("enter"); |
19 |
immediate = CustomInput.worth; |
20 |
}
|
21 |
|
22 |
if (!immediate) { |
23 |
displayError(immediate,'Please select a class or present a customized temper"'); |
24 |
return; |
25 |
}
|
26 |
|
27 |
loader.fashion.show = "block"; |
28 |
|
29 |
const information = await getData(immediate, key); |
30 |
|
31 |
if (information.selections) { |
32 |
const container = doc.getElementById("end result"); |
33 |
// information from aync
|
34 |
const quotesArray = information.selections[0].message.content material.cut up("n"); |
35 |
const mappedArray = quotesArray.map((quote) => { |
36 |
const trimmedQuote = quote.exchange(/^d+.|"$/g, "").trim(); |
37 |
|
38 |
return ` <div class="col-sm-6 mt-5 mb-4"> |
39 |
<div class="card">
|
40 |
<div class="card-body">
|
41 |
<p class="card-text">${trimmedQuote}</p></div> |
42 |
</div>
|
43 |
</div>
|
44 |
`; |
45 |
});
|
46 |
|
47 |
container.innerHTML = mappedArray.be a part of(""); |
48 |
|
49 |
} else { |
50 |
displayError("",information.error.message ) |
51 |
|
52 |
}
|
53 |
CustomInput.worth = ""; |
54 |
});
|
Let’s break down what’s taking place above:
- First we stop the default nature of the browser by setting
e.preventDefault()
-
const key = localStorage.getItem("API_KEY");
will get the API KEY from native storage. - If no secret’s discovered, we’ll go an error message to the
displayError()
operate. -
let immediate = "";
declares an empty string that can retailer the worth of the immediate from the consumer. -
if (doc.querySelector('enter[name="mood"]:checked')){...} :
checks if the consumer has chosen a class and if true, the worth of the chosen radio enter is assigned to the immediate. - Within the
else
assertion, the immediate would be the customized enter worth if the consumer has entered a customized temper. - If the worth of the immediate is
null
orundefined
, it signifies that the consumer has not offered a immediate (both by choosing a temper class or coming into a customized temper), we’ll show an error message. - After all of the validation has handed, we’ll show a spinner component with
loader.fashion.show = "block";
getData()
Operate
Subsequent, we’ll name the await getData()
operate and go the immediate and the API key. Because the operate is an asynchronous operate, we use await to make sure the execution is delayed till the info fetching is profitable.
As we noticed earlier, the info object returned by the API appears like this:
The content material we want is contained within the selections[0].message
object.
-
information.selections[0].message.content material.cut up("n");
creates an array of the quotes known as quotesArray by splitting it the place n seems. - The
quotesArray
now incorporates all of the quotes, all we have to do is use themap()
methodology and for every quote, take away any main or trailing whitespace withquote.exchange(/^d+.|"$/g, "").trim();
and return an HTML markup for every quote represented by a Bootstrap card. - Lastly, we set the
innerHTML
of thequotes
container to the concatenated HTML markup of every quote. - In case of an error from the API,
displayError("",information.error.message
) shows the returned error message.
Error Dealing with
Relatively than repeating the error dealing with course of, the displayError()
operate will deal with that. It takes the valueText
and a messageText
as parameters, checks if the valueText
is null or undefined and shows the error message contained in messageText
.
The error message is displayed for 4 seconds after which hidden from the consumer.
1 |
operate displayError(valueText,messageText) { |
2 |
const message = doc.querySelector(".message"); |
3 |
if (valueText === "") { |
4 |
|
5 |
message.textContent = messageText; |
6 |
message.fashion.show = "block"; |
7 |
}
|
8 |
setTimeout(() => { |
9 |
message.textContent = ""; |
10 |
message.fashion.show = "none"; |
11 |
}, 4000); |
12 |
return; |
13 |
}
|
For the ultimate clean-up, let’s be certain that if the consumer begins typing in to the customized enter subject, any beforehand chosen class (through radio buttons) will likely be deselected.
1 |
const inputField = doc.getElementById("enter"); |
2 |
inputField.addEventListener("enter", () => { |
3 |
const radio = doc.querySelector('enter[name="mood"]:checked'); |
4 |
if (radio) { |
5 |
radio.checked = false; |
6 |
}
|
7 |
});
|
Conclusion
Whew! That was so much to absorb, however the finish result’s price it, and also you’ll have realized so much. We lined the steps wanted to make your individual AI Quote Generator App with JavaScript. Now, you possibly can combine AI into your utility to generate distinctive content material!
Let’s remind ourselves of the ultimate product.
Trending Merchandise
[product_category category=”trending” per_page=”8″ columns=”2″ orderby=”date” order=”desc”].