Lesson 7 - Objects, JSON, And Enhancing the JavaScript Diary
In the last lesson, Data storage in JavaScript, we described various types of data storage in JavaScript and the differences between them.
In the previous lesson, Data storage in JavaScript, we started to program our JavaScript diary. In today's object-oriented programming tutorial, we're going to continue with it. We'll look at the JSON format and local data storage.
JSON
JSON is an abbreviation of JavaScript Object Notation and, to put it simply, JSON is a JavaScript object written as a string. We can save objects converted to JSON to files or to other storages simply and then load them back from them. In addition to XML, it's also a popular data format for APIs, when our application asks a server for some data and the server sends it back as JSON objects.
Syntax example
Let's consider we have an instance, for example a specific user with a name and age. We'd create such an instance like this (assuming that the class has the appropriate constructor):
const simon = new User("Simon", 19);
If we converted this instance to JSON, the JSON would look like this:
{ "name": "Simon", "age": 19 }
We declare JSON objects using braces. Then the names of its properties follow, with colons and their values.
We don't write a comma after the last value of the block. Some JS engines may have a problem with it otherwise.
The great advantage of JSON is that it's part of the JavaScript language. So
we don't need any libraries and once we load JSON data, we have it as objects
instantly. So, if we store the JSON above into a variable, there will be an
object with the name
and age
properties:
const simon = { "name": "Simon", "age": 19 }; document.write(`${simon.name}, ${simon.age}`);
The script will really print the JSON data on the page:
This is, however, not a User
instance, because
JSON objects are anonymous objects. JSON format does not
support object methods, and only supports the string
,
number
, object
, boolean
,
null
, and array
data types for properties. Therefore,
JSONs are only data, not objects containing any logic.
In JavaScript, you can sometimes encounter similar anonymous objects, they are used in places where it wouldn't be worthwhile to declare a fully-featured class. We could even add methods to these objects in JavaScript code, but never in JSON files directly. We definitely should not use anonymous objects instead of standard instances.
Let's show one more JSON, this time a more complicated one with a nested object and an array:
{ "name": "Simon", "age": 19, "skills": ["programming", "graphics", "swimming"], "car": { "license_plate": "13ABC", "abs": true }, "wife": null }
The JSON above defines, in addition to the user's name and age, his skills, car, and wife. It's still a data definition of one particular instance. Unlike objects, we declare arrays using brackets. Notice the nested object representing a car. So far Simon has no wife. Again, notice the missing commas after the last values of the blocks.
Parsing JSON
Of course, JSON often comes to us as a string rather than declared in a nice
and neat JS file. We load such JSON using the JSON.parse()
method.
Let's show our JSON example with the user, this time declared as text:
const simon = JSON.parse(`{ "name": "Simon", "age": 19 }`); document.write(`${simon.name}, ${simon.age}`);
Again, the output is the same as in the previous example:
Converting objects to JSON
And how, on the other hand, will we convert some of our object to a JSON
string? To do that, we use the JSON.stringify()
method. Let's
really create a User
class to have a complete example:
class User { constructor(name, age) { this.name = name; this.age = age; } greet() { alert("Hello!"); } }
We added the greet()
method to the above class to make sure it
really won't work in JSON. Let's create a User
class instance and
convert it to a JSON string:
const simon = new User("Simon", 19); const json = JSON.stringify(simon); document.write(json);
Our object will be printed as JSON:
We can see that there's no information about which class the user was created
from, and all the logic is missing. The code is all on one line because by
default, JSON.stringify()
saves space and doesn't add white
characters to the resulting string.
LocalStorage
We'd be already able to save our diary entries to JSON. We'll use localStorage to save them. It's a storage in the user's web browser (hence local) where data can be stored as a string that can be accessed later. We have 10MB of space available for our application, which is really a lot of text
Using the storage will help us handle data persistence in our application. Of course, if we refresh the page now, our entries will be lost. That's why we'll convert our entry array to JSON and save it to localStorage. We'll then parse it from the storage back to the array when the page is loaded. So the data will remain in the application.
Working with localStorage
localStorage is very easy to use. We use the
setItem()
method to store a string under a string index. Similarly,
to read a value under a key, we use getItem()
.
Let's store the string "data"
under the "property"
key:
localStorage.setItem("property", "data");
Now we'll print what's under the "property"
key, which will
result in getting the "data"
value:
document.print(localStorage.getItem("property"));
The result:
Enhancing the Diary
Finally, we can proceed to the conclusion of today's lesson and modify the
diary to save and load data from/to localStorage
. We'll store our
entries
array in the Diary
class simply under the
entries
key.
The Constructor
So instead of creating an empty entry array in the diary constructor, we'll load the entries from the local storage:
constructor(language = "en-US") { const storageEntries = localStorage.getItem("entries"); this.entries = storageEntries ? JSON.parse(storageEntries) : []; this.language = language; this.nameInput = document.getElementById("name"); this.dateInput = document.getElementById("date"); this.confirmButton = document.getElementById("confirm"); this.printElement = document.getElementById("task-list"); this.setEvents(); }
We store our entries from localStorage
to the
storageEntries
variable. Next, we need to check if any entries are
stored at all. If not, the storageEntries
variable will be
null
. That's why we used a ternary operator,
thanks to which the entries
property is set to either the parsed
array from localStorage
or to an empty array.
So we got loading the entries from localStorage
, now we'll add
saving them at the time a new entry has been added. We'll store the entries in
both the entries
array and the storage. Working with the storage
only in the entire application would require constant conversion of object
entries to strings and back. Therefore, we'll use the storage only for loading
the entries at the start and to storage newly edded entries.
We'll add one line to the setEvents()
method, saving a new entry
to the storage as well:
setEvents() { this.confirmButton.onclick = () => { const entry = new Entry(this.nameInput.value, this.dateInput.value); this.entries.push(entry); localStorage.setItem("entries", JSON.stringify(this.entries)); // the line added this.printEntries(); }; }
We can try to run the project, add tasks, and reload the page. The tasks now stay in the app and that means that you have your first real object-oriented application. Congratulations! That's all for today, we've solved our storage problem quite easily. In the next lesson, Improving the Object-Oriented Diary In JavaScript, we'll finish the diary