What do you want to learn?
Leverged
jhuang@tampa.cgsinc.com
Skip to main content
Pluralsight uses cookies.Learn more about your privacy
Object-oriented Programming in JavaScript - ES6
by Mark Zamoyta
Stay updated by learning about the latest upgrade to the JavaScript language--ES6, also known as ECMAScript 2015. In this course, you will learn how to develop software using many kinds of classes and modules.
Resume CourseBookmarkAdd to Channel
Table of contents
Description
Transcript
Exercise files
Discussion
Recommended
Course Overview
Course Overview
Hi everyone, my name is Mark Zamoyta, and welcome to my course Object-oriented Programming in JavaScript - ES6. I am a freelance software consultant and developer in Santa Barbara, California. Many object-orietned patterns emerged in the ES5 version of JavaScript, but now in ES6 we have a new syntax to create classes and objects. In this course we are going to learn to work with classes and modules. This will be done the ES6 way. We'll be coding classes for many different types of objects. Classes for regular business objects, classes which can be extended, used for inheritance, a data service class to hold all of our application's data, user interface classes, and we'll create a class to hold an entire web application. By the end of this course, you'll have lots of experience working with classes and modules in the latest version of JavaScript. We'll even create a simple application made solely from ES6 classes. Before beginning this course, you should be familiar with fundamental JavaScript development. Don't worry if object-oriented programming is new to you, since we'll be starting from step 1 when it comes to object-oriented techniques. I hope you'll join me on this journey to learn modern programming with the object-oriented programming in JavaScript ES6 course at Pluralsight.
Object-oriented Programming in JavaScript - ES6
Course Overview
Welcome to Object-oriented Programmign in JavaScript - ES6. My name is Mark Zamoyta. In this course we'll be learning about objects in JavaScript, specifically the new class keyword that's used in ES6. We'll also focus on modules, modules give us a new way of organizing code and it's standardized in ES6. We don't have to worry about different module formats, such as required JS or common JS. And we'll be covering that in detail in this course. Let's take a look at how this course's modules break down. This is module 1, the introduction, but in course module 2 we'll deal with classes, and a class stands for a classification. It's a way to define an object, and we'll go over that in detail in course module 2. We'll take a look at building a Drone class and also a Car class, and we'll see how to set these up using ES6's class keyword. Next we'll look at the concept of inheritance. We have classes such as Drone and Car, but they might share some functionality. So we can create somewhat if an abstract class, in this case called Vehicle, and both Drone and Car can inherit from it. This makes our code simpler and we don't have to reinvent the wheel. Even though we'll never have a Vehicle object, we'll still have many Drone and Car objects, which inherit from Vehicle and Vehicle inherits from a JavaScript object named Object with a capital O. So we'll see how all objects ultimately derived from Object. In module 4 we'll look at creating a class for a Data Service. Having a single class to handle all your app's data is a great way to go, it keeps things simple and easy to test and you won't have your data scattered all throughout your source code. A Data Service class will consolidate everything. So inside this service we'll have a list of cars and we'll have a list of drone objects as well, and we'll also add some functions to access this data. Here we see two functions, which are methods within a class, called getCarByLicense. We can pass a license number and get back a single car or sort cars for a given make that we pass in, we can get back a list of all our cars sorted by the make, which would be the manufacturer. In module 5 we're going to switch gears a little bit. So far we've been working with business objects. These are the tangible objects that have to directly do with our business. But we can also create classes to model the user interface. So we'll create a button class that we can use to create buttons, and we're going to be using Material Design Lite, that's the user interface framework similar to Bootstrap. That makes it really easy to add good looking and functional user interface elements to our pages. We'll also create an Image class to show images and we'll create a Title Bar class to show a title bar. And this is from Material Design Lite too. We'll also a create a class to create a data grid. Here you can see a list of cars. And we want to make this really simple where we just pass in the headers and data. And finally for the user interfaces classes, we'll create a class that will let us work with Google Maps. Here you can see the cars plotted out in Central Park. In module 6 we'll take everything we've learned in this course regarding classes and object-oriented programming, and create a full application. Here we can see a simple home screen with an image and buttons and the title bar at top can be opened up. We'll call the application Fleet Manager and here you can see the menu that slides in from the left side when you hit the hamburger button. And this application will be responsive, as you increase the size of the web browser or view it on a larger device, our title bar will show links directly in the title bar, there's no need to press the Menu button, and that's the feature of Material Design Lite as well. So we'll be covering all these different types of classes in this course. But before we do this, let's take a look at the current state of ES5 classes and modules and see why we need to be working with ES6.
Why Use ES6 Classes and Modules?
So why would we want to move to using classes and modules in ES6? Well one thing we can do is look at some ES5 code and look at some of the complexity that's added just because of the fact that there's no standard for modules and there are no classes. We're looking at the jQuery code. This is jQuery version 2.2.3, and right off the bat we see an IIFE right here and we can see that this IIFE is needed in order to work with common JS. Common JS is one way of implementing modules, but its' not the only one. So all of this code right here, it looks a little complex, but what we'd like to do moving forward in JavaScript, is just chop it all out. We'd like to have a single module standard so that our libraries and code doesn't need IIFEs and this kind of complex logic to work with modules. Also if we scroll all the way down to the bottom we can see some more code. Again, here we have a big comment having to do with amd modules, and there's more complex boilerplate code right here, and even more below it. So the ultimate goal is to just use ES6 modules. We can do away with these other modules formats, and it'll make our code look a lot cleaner and be much easier to work with. I also want to look at this GitHub project. This is John Papa's toastr code. Toastr puts up toast messages in a spa, and you might be familiar with this, it's been brought up in a few Pluralsight courses by John Papa. And looking at this code we can see that it also deals with modules. It has an IIFE and we're passing it a define variable, which has to do with some kind of module function. If we scroll to the bottom, we can see the logic here to try to figure out how to work with modules and which module format to work with. Now this is all best practice. This needs to be done in order to get common JS and require JS modules to function properly. It'd be great if we could just standardize on ES6 modules. We don't really need the IIFE and we don't really need all this talk about modules. Also scrolling back up to the top, we can see this toastr variable here, and in ES6 this could possibly become a toastr class. That would make the code easier to work with. Instead of digging through the code in order to find out what's getting returned as an object, we'll be working with ES6's modules and classes only, but we will see how to work with things like jQuery, which does not work with ES6 modules at all. We can use the system JS package to get around that, as we'll see later in the course.
Tooling Setup
Let's look at the tooling we'll be using and the setup that I'll have when I develop code. I'm here at Node.js.org, and if you don't have Node installed, you're definitely going to need that. You can download the recommended version, here it's 4.4.5, or if you're on a Mac you can just download the latest version. And we're not so much interested in Node as we're interested in the Node Package Manager, NPM. So after this is installed let's take a look at how we'll be using it. So here I'm in Visual Studio code in a sample project from the course. What this project does doesn't really matter. But there are two things to notice here, first of all there's no folder named node_modules, but we do have a package.json file. Package.json is what NPM uses to determine which dependencies to load. Here we can see the dependencies section and we're going to need these packages. Also for devDependencies, those are only used in development, not in production, we're going to need this lite-server dependency. Now all of these packages are going to be put into a folder called node_modules. And the way we do that is from the command prompt. So I just pressed Shift+Control+C in order to bring up the command prompt from Visual Studio code, and that puts me in the right folder. And let's execute NPM or Package Manager, and we want to run the subcommand update. What this will do when we execute it is it'll look at package.json and install any packages we need in node_modules. Now there are some warnings about lodash and possibly a few other things, but that should be fine, they're not required. So when that completes, we can see that we have this node_modules folder now, and all of our packages are loaded. So the next thing we want to do to run the sample, I'll go back to the command line and I'll just clear it. We want to start up the browser and set up browser sync. With browser sync we're able to make changes to our source code and save it. When it saves it'll automatically be updated in the browser window. So there's a node command set up for this npm run is the subcommand and dev is the name of the command. When we run this, the lite-server will start and browser sync will get all set up. So our browser opened automatically and went directly to localhost port 3000. And what I'll do in this course is I leave the browser open on the right side of the development environment. So Visual Studio code will run right here on the left side, but we'll still get access to the browser here. In this case, the project is only writing out to the console, so I'll hit F12 to bring up the console and refresh. And I'll turn off the warnings and information and you can see the data that the sample project is printing out, Tesla, Uber, and Uber. We can control plus in order to increase the font size. So that's mainly how we'll be set up in this course. The editor on the left and a thin browser on the right side, and remember, just by saving a file the browser will get automatically refreshed. And if you're working with some other dev environment, that's fine too, you just need a way of refreshing your browser.
An ES6 Compatibility Chart
If we want to support older browsers, we need to be working with transpilers and polyfills. I'm here at kangax.github.io. The easiest way to get here is I just search for ES6 compatibility table and this is a great chart that really shows what transpilers are capable of. And we can also see for desktop browsers that the most current browsers do support 90% and greater of ES6 features. But for something like Internet Explorer, it only supports 15%, so that's why we need to use some kind of transpiler. In this course we'll be using Traceur, but you could use Type-Script or Babel or any of these other ones. From these numbers here, it seems like Traceur, Babel, and Type-Script are the best way to go for transpilers. We'll be working a lot with classes and if we scroll down the feature list on the left, we can see class mentioned here, and our transpilers do implement a large majority of the functionality for that, so that's great. But there is some functionality that transpilers can't do. If we look at this Subclassing section here, we can see that none of the transpilers can subclass at all. It'd be really cool to be able to use an array and subclass it, but we won't be doing that in this course because transpilers just don't allow for it, even though modern browsers do. So before you implement an ES6 feature, you might want to consult this chart to make sure it'll work on whichever transpiler or browser you're targeting. To learn more about ES6, you can check out my Pluralsight course, Rapid ES6 Training. This will go through the compatibility chart in more detail and if you already know JavaScript, this will quickly get you up to speed with ES6.
Class Basics
Introduction
Hi this is Mark Zamoyta. Welcome to this module Class Basics. Classes are fundamental to object-oriented programming. Class stands for classification and we create a class to classify our objects. You can think of a class as a blueprint or a stamp in order to stamp out objects and create objects. Let's see what we'll cover in this module. We'll start off by creating a simple module in ES6. If you saw the introduction, everything should be all set up, but we'll take a deeper look at what a module is and how it operates. Next we'll define what a class is and what objects are and how classes and objects relate to each other. Next we'll start building classes. And we'll we work with constructors on those classes and the properties which hold data for the classes. We'll also take a look at static properties, which are very different than the properties that get put on objects. We'll look at methods, which are functions that belong to objects and classes, and we'll look at static methods which are the methods that belong directly on a class, not on an instance. Finally, we'll look at getters and setters. Getters and setters let us execute some code when we access or set a property, and we'll see why this can be useful. So let's get started by taking a look at code to create a module.
Creating the Start-up Module
I'm here in the Visual Studio Code Editor, and to start off this project I created a folder and I added the index.html page. This is the same page that we created in the introduction, so if you need more information you can check that out, and you'll notice there's no package.json file, so let's quickly add our node packages. I'll hit Shift+Control+C and we have a command prompt. I'll type npm in it and I'll just give it the name drones and we can accept most of the defaults here. So we have package.json right now. So let's add some npm packages. We need the ES6-module-loader and we'll use traceur as the transpiler. Next we'll run npm install and for our dev environment we can specify save-dev, and we need the lite-server. So our lite-server is installed. Let's take a look at package.json and make sure everything's there. We have our ES6-module-loader, traceur, and our lite-server. Looking back at index.html, we already have scripts for traceur and the module loader. The only thing missing that we set up in the introduction is src/app.js. Let's create that. I'll create the src folder and I'll add the file app.js. And I'll log out a message. Now in order to kick this off, we need to run the lite-server from the command line. In order to do that, we'll go to package.json and in our script section we'll add a new item. We'll call it dev for development mode, and we just want to execute the lite-server. I'll save it and let's go back to the command prompt and we'll call npm run dev. So lite-server is running and it kicked off a browser session. Let's open up the development tools, make sure we're in the Console here, and we'll refresh. And we do get our message in app.js, and I'll just shut off the warnings and information messages. This should make it easier to see the console. So our server's running and now we can take a look at app.js and play with this. I'll resize this window. So now all we have to do is save our file and we can see the browser automatically get refreshed. And let me increase the size of the console as well. I can use Control Plus to do that. So here in app.js we're working with the module. We know this because we can look at index.html and you can see that we're calling system.import, passing it src/app.js. Now modules in ES6 have some unique characteristics, more importantly modules do not pollute the global namespace, so it's not necessary to create IIFEs. We can just start working with variables here and they get scoped to the module. For instance, let's let droneId = 5. Now in ES5 droneId would go onto the window object as a global variable, but since we're in a module, let's actually log out window, the global object in a browser, .droneId. So I'll save this and we should very quickly see what prints out in the browser console. We get undefined. So droneId is now scoped to the this module and there's one other important aspect about modules we need to know about, and that's that they're automatically put into strict mode. For example, let's take out this let statement and we'll just assign the number 5 to droneId and droneId hasn't been properly declared yet. I'll save the file. And you can see we get an error in the console, Variable undefined in strict mode. So our module is automatically being put in strict mode. And that's one of the important leaps forward in ES6. We want to have strict mode, we want to prevent variables from being declared at a global level, and we also don't want to have to bother with wrapping all of our code in IIFEs. So now that we know a little bit about modules, in the next video we'll take a look at classes.
Defining Classes and Objects
So let's define classes and objects and object oriented programming in general. We'll start off by looking at our application and the business behind it. For this course we're going to be working with a company that has a fleet of drones and possibly other vehicles. So a drone is a real world object that you can touch and manipulate, but we want to be able to represent this inside a computer, inside our software. And a lot of terminology has built up over what to call this within software, and let's look at some of those terms. We can call it a representation because it represents a real world object. Sometimes it's also called an abstraction. The drone doesn't literally live inside software, so we have to abstract it out in the way our software can understand. It's also commonly referred to as a model. We can model the drone within our programming language. And a very informal word for it is a thing. But if we want to start getting more formal, we can refer to it as an entity. And you'll hear all these words referring to objects within software. We also have business object and the term that we're going to be using mostly in this course is simply an object. So we have the real world drone, but its representation inside our software is referred to as an object. And that's what object oriented programming is all about. But when it comes down to it, we don't really program objects. Most of our programming is in the creation of what are called classes. Let's take a look at that. A class is not an object. A class can be thought of more as a blueprint. It's something we can use to stamp out objects. For example, a drone class would include properties such as the amount of flight time left for the drone, the number or rotors, the maximum speed of the drone, and it's current GPS location. A class can also contain actions to perform. We could code a method for this class that would send it to a given GPS coordinate, or we could have methods to start and stop the rotors of the drone. Class stands for classification, so essentially what we're doing is we're taking the drone and we're setting up its classification, all of its properties and its capabilities as actions. And once we have this class, we can use to create actual instances of the class, which are the objects. So here you can see that we use the Drone class to stamp out six different instances, and of course a program could have thousands or even millions of instances of a class. Each instance is called an object. So again, as programmers and developers, we program classes, but then we also specify within our source code logic exactly when we want to instantiate that class and create an object, and in the next video we'll take a look at the syntax for doing this.
Class Creation
Let's take a look at the syntax used to actually create classes and then instantiate objects. We create a class by using the class keyword and we give it a name. Here we're giving the class the name Drone with a capital D, and that's an extremely common convention. Always capitalize the name of your class and use Pascal casing. So every major word in the class name would be capitalized. Now this class is empty, all there is is a comment here, but in the rest of this module we'll see what we can add to the class to give it its properties and functions or methods. And the bottom line here, let drone = new Drone, we're using the new keyword to create a new instance of a drone. So it looks a little bit like a function call with the parenthesis on Drone and we'll see later in this module that we can actually pass arguments to Drone, but for now we just won't put anything in the parenthesis. And we assign this new object that gets created to the Drone variable. Let's take a look at this in action. So let's continue with a similar project to the last video where we did a demo. I emptied out app.js, so that's empty. Let's just make sure that our server is running. I'll execute npm run dev. So now lite-server is running. So let's create our first class. We use the class keyword and we'll name the class Drone. For the class body we'll leave it empty, but it needs to be wrapped in curly braces. So here's a complete class. It doesn't do much, but it will compile, and we will have a class named Drone. Normally a class would be in its own file and the name of the file would be similar to the class name, but in this case we're learning about classes and we'll implement that best practice later in this course. So for now let's just save it and make sure it compiles. Everything's fine. So now that we have a class named Drone, let's take a look at exactly what this is. Let's log out typeof Drone and you'll see that we get a function. So if you did a lot of work in ES5, you'll remember we had no classes, but we did have what were called constructor functions, which were normal functions and that's what the class keyword does in ES6. Essentially Drone will become somewhat of a function, but we don't need to think of it that way. We want to try to break free of ES5 and get to the point where we can think with classes. So even though typeof Drone is a function, it'll help us a lot mentally to just think of it as a class. So now that we have a class, let's instantiate it. We'll create an instance of this class, which will be our actual object. We'll create a variable called drone and we'll use the new keyword. So let drone = new Drone, and remember to add the parenthesis after Drone. And instead of logging out typeof Drone, let's log out typeof lowercase drone or local variable. I'll save it, and we can see that we get object. Now this makes sense, lowercase drone is actually an object. We can also use the instance of operator. Let's log out drone instanceof, and then uppercase Drone. So is this variable drone an instance of the class Drone? I'll save it and we get true. So we are able to tell at runtime whether or not a variable is an instance of a given class and that's very important. In the next video we'll take a look at constructing this drone by passing it some information and saving that as part of the object.
Constructors and Properties
So now that we have a class, let's look at how we can initialize information for a class instance, an object. Usually we do this in a constructor, we use that constructor keyword and that becomes a function. And just for now we'll print out a message. So by executing new Drone, as we do in the last line here, Drone's constructor will be automatically called. I'll save it and we get in Drone constructor. I'll just bump up the size here. Now it's very common to pass information into the constructor and we can do that with arguments, we'll call new Drone and let's pass it an id of A123. In the constructor we'll give that argument a name, id, and let's print out that id, and we get id: A123, and like any function we can pass several arguments. I'll also pass a name. So we're passing in id and name now and I'll print those out. And again we get A123 and Flyer for the name. Most likely we'll want to keep our constructors as simple as possible. And you normally don't want to do any heavy processing in a constructor. We want to keep the class simple and we want to be able to test it easily. So normally what we want to do in a constructor is create instance variables. And we use the this keyword to refer to the instance being created. So let's add an id variable onto this and we'll assign it to our argument. And let's do the same with name. So now id and name are going to be attached to the instance, to drone. Let's print these out. We'll print out drone.id and also drone.name. So we get drone: A123 and Flyer. So we can access these new instance properties by taking our instance drone and using that notation. We now have drone.id and drone.name available to us. And we could also access these properties with bracket notation. We'll access drone and we'll use brackets to store the string id and we'll do the same with name. And you can see we get the same result. Normally you would use dot notation when possible, but if you had some kind of dynamic code, you could access the properties as strings with bracket notation. So just remember to try to keep your constructor simple and this is a very familiar pattern, you pass information into the constructor and you save it on the instance variable this.
Static Properties
It's very important to understand the difference between instance variables at the instance level, those would be instance properties, or properties at the class level, which are called class properties. Let's look at some more instance variables. We'll create a new instance of drone, let's just call it drone2, and we'll give it an id of B456 and the name will be Twirl. So we'll have two drones, each with different instance variables. Let's print out the id's. So I'll print out drone.id, which should be A123, and drone2.id, which should be B456. I'll save it and that's what we get as a result. So each instance of drone will have its own set of properties, but we can also have properties directly on Drone, the class, and these are called static properties or class properties. The way we put a property directly on drone is the same way as we did in ES5. There's no new syntax yet for that, so right underneath the class I'll set a static variable on drone. Let's call it drone.maxHeight and we'll set it to 2000, for 2000 feet. So now the property maxHeight, for maximum height, is set directly on the Drone class, not the instance. Let's log out Drone.manHeight and we get 2000. But what if we try to access this on an instance of drone? So let's log out drone, lowercase, .maxHeight. MaxHeight is not on the instance, it belongs on the class as a static property. Let's see what happens. We get undefined. There is no maxHeight property directly on the instance, it's only on the class Drone. So to recap, instance properties only get placed on the instance of the class, when it gets instantiated and created as an object. But we can also add properties directly to the class itself, and these are called static properties or class properties. And in this case, Drone with a capital D, .maxHeight, maxHeight is a static property.
Methods
In addition to having properties on a class instance, we can also have methods. A method is a function that gets attached to the instance. I cleaned up Drone from the last example and let's add a method. Let's call the method fly, and it looks like a function. In the body let's print out the id. So we'll print out Drone id is flying. Now nothing's going to happen if we execute this right now. We need to call this method on an instance. So down below, let's call drone.fly and I'll save it and we get our result, Drone A123 is flying. Let's also call fly on drone2. And now we get our messages that each drone is flying. Now the important thing to remember, especially if you're coming from C# or Java, is that we need to access the instance property by using the this keyword. Here we're using this.id. It's very common to forget this, but in JavaScript we need it. If we take this. off, you can see we don't get an error in the editor, but if I run it, I'll save it here, we get id is undefined in each case. So when you're working with an instance of a class, you always need to use the this keyword with a property name. Also if we were going to call another method in this class instance, we would need to use this as well. So I reset it and we're back to getting our proper messages.
Static Methods
So we just saw that fly is a method attached to each instance, but we could also have methods attached to the class itself. These are called static methods. So let's create a static method on Drone. We use the keyword static and we give it a name. Let's call the static method getCompany and we'll just log out some information. So we can clearly see the difference between a static method and an instance method. It's the static keyword right here. So now getCompany is placed directly on Drone. We'll call Drone, uppercase, .getCompany, and we get our message in getCompany, and just like we tested with properties, let's try calling getCompany on lowercase drone, an instance. We get object doesn't support property or method getCompany. So because it's a static method, it only exists on the class, and also because it's a static method we can't access instance variables or instance properties. What if we tried to log out this.id? Well there just isn't any instance and our editor isn't showing us an error, but if we save it, or actually let's make sure we're calling the uppercase Drone.getCompany, and if we save it we get undefined. This.id just doesn't exist on class Drone, it only exists on drone instances. So you always need to be aware whether a method belongs to the class as a static method or as an instance of the class, which would not use the static keyword.
Getters and Setters
We can also use getters and setters within a class. Let's look at this example. We'll create a new Drone, passing it an id of A123, and inside the constructor we'll assign that id to this._id. When a variable begins with an underscore, that's a convention that usually means that it's private. Now there are no private variables in classes. We don't have that yet in JavaScript, but we can still use this underscore convention. This doesn't need to be named _id, it could be named anything. That's just a private field where we'll be holding the id. Now what if the programmer wanted to access that id? Most likely they wouldn't use _id. They would want to do something like console.log drone.id, giving the instance drone.id. But that's going to be undefined. This id is only local to the constructor and our backing field is _id. And this is where getters come in. We want to create a new property that behaves somewhat like a function. We can use the get keyword and we'll name it id. We'll make it look like a function, and we'll just return this._id. So we set up our id here and we'll return it here, this._id. And notice the getter is just called id, and that's why this drone.id will work fine. I'll save it, and we get drone id: A123. So this id here is a function that gets called. We can perform any kind of validation or checking, even alter the result. I'll log out in id getter and let's also add something onto the id. We'll just add a TEMPORARY marking. I'll save it and we get in id getter and the drone id is now A123, space, TEMPORARY. So it looks like here id is a function, but it's not. We can access it like a property as in drone.id without the open and closing parenthesis after it. And again, this is for a validation or logging or any case where you need to execute a function when all you really need is a property. We can also have setters. We'll use the set keyword and we'll also call this id without the underscore in front, and it gets passed the value that we're setting it to, this._id = value. So just like a getter, a setter is used to execute a function when we set a value. So we can have any kind of validation or error checking or change the data however we'd like. So after I create the drone instance, let's set drone.id equal to B456, and I'll save it, and we get drone.id: B456 TEMPORARY. So by setting drone.id to B456, our setter was invoked and this._id was set to the new value. And then when we logged out drone.id it needed to execute the getter function for id in order to print it out. So that's why we got B456 TEMPORARY. And just remember that the underscore in front of id, that's just a convention, it's not required at all, but normally when you see an underscore before a variable in a class, you can think of it as private, that's probably what the programmer intended.
Summary
In this course module we looked at ES6 modules and we saw how a module can simply be a file. It doesn't pollute the global namespace and it has its own namespace at the module level. We took a look at classes and objects, we defined them and made a sample class. We instantiated it as an object and we looked at constructors. A constructor is code that automatically executes when you create a new object and you usually pass parameters to it in order to initialize the object. We took a look at properties, which are pieces of information belonging to an instance, and we looked at methods which are functions that belong to an instance. We looked at static properties and methods, and those belong at the class level. We saw how we can access those by using the class name itself. Finally, we took a look at getters and setters. These let us execute some code when we either get a property or set a property. They can be used for validation, logging, or any other purpose where you need to execute a function when you access a property or set a property. In the next module we'll take a look at inheritance and we'll see how we can extend a class to create a whole new class.
Inheritance and Code Organization
Introduction
Hi this is Mark Zamoyta and welcome to this module titled Inheritance and Code Organization. We'll start off by looking at the concept of inheritance. Inheritance refers to class deriving functionality from other classes. And we'll see how this applies to the cars and drones we've been working with so far. We can actually create a new vehicle class that cars and drones can inherit functionality from. And when we use inheritance, we use the extends keyword, it's referred to as extending a class. So you'll have a base class and then we'll extend that base class to create new classes. Working with constructors can be a bit tricky, so we'll see what we need to do in order to get constructors to operate properly, and we'll look at inheriting properties from a base class, and we'll also look at inheriting methods and functionality from a base class. Finally we'll look into organizing our classes. The basic rule of thumb is that each class should be in its own file. So we'll set things up that way. So let's take a look at what inheritance is all about.
Inheritance Overview
In the prior module of this course we looked at modeling a real world object, a drone. We created a classes that would close resemble a drone in the real world, something you can touch. So we could make another class for a car. Now both of these objects have some things in common. For instance, they both have a license, they both have some kind of id so we can identify them in a database. They both have a GPS coordinate, a latitude and longitude number, and there are a lot of other properties and methods that these two objects would have in common, and that's where inheritance comes in. We can create somewhat of an abstract class called Vehicle, and Vehicle would hold all the properties and all the methods that both Drone and Car have in common. So we would say Drone inherits from Vehicle and Car inherits from Vehicle, that way the Drone class would only have properties and methods specific to a drone and the Car class would only have properties and methods specific to a car. Now since we're using JavaScript, there's another class that all classes derive from, and that's Object. Object gives us some methods such as to string or value of and others, which every object has in common in JavaScript. So that's also a form of inheritance. So what we would do is we would instantiate the classes Drone and Car. But we would never directly instantiate a Vehicle or an Object. We could if we wanted to, but mainly for business purposes we're going to need to keep track of drones and cars and because they inherit from Vehicle, we get all that functionality in those properties too. So let's take a look at how we set up inheritance in code.
Extending a Class
Let's take a look at inheritance and how we would implement this in code. So we have an abstract class, something that we're not really going to instantiate, and that's called Vehicle. And we create that like a regular class, like we saw in the last module we created a Drone class. We can go ahead and create a Vehicle the same way and this class will hold all the properties and methods that all of our vehicles have in common. All Cars and Drones will have these properties within Vehicle. So to inherit these properties and methods we use the extends keyword, we'll create class Drone and we'll specify that it extends Vehicle. We'll also create class Car and specify that it extends Vehicle as well. So we're definitely going to be instantiating Drones and Cars, but we shouldn't really have a need to instantiate Vehicles. Let's go to the source code and see how we can work with this. So I have a project and browser sync all set up and if you need to get this set up yourself, you can watch the introduction to this course. But essentially what I did was I ran npm run dev and that started up everything. I'll just show the console here and make it a little bit larger. So let's go ahead and create our Vehicle class, and this is where we'll be placing properties and methods later in this module. But for now, let's just go ahead and inherit from Vehicle. We use the extends keyword. So class Drone extends Vehicle. Now in most situations you're going to have multiple classes extending a base class such as Vehicle. So let's go ahead and create class Car extends Vehicle as well. I'll save this and it'll execute. Let's just make sure there are no errors. And we're fine. I'll just turn off the warnings and messages. So now we have three classes. Drone and Car inherit from Vehicle. Now let's create an instance of car. Let c = new Car. And let's log out some information about our new variable c. We'll log out c instanceof Car, and this will let us know if c is an instance of Car. We get true. And that's pretty obvious, we saw that in the last module. But is c also an instance of Vehicle? Well it inherits from Vehicle so it should come out true as well. And it does. Now we also have implied inheritance where Vehicle itself will inherit from Object. So when we console.log c instanceof Object we should also get true. And we do. So when we instantiate a class, it's an instance of that class as well as any parent class, any class that it gets extended, and it's also an instance of Object.
Inheriting Constructors
Let's take a look at working with constructors. We have pretty much the same application we ended the last video with, both Drone and Car classes derive from Vehicle, and then we're instantiating a Car object at the bottom. So let's add a constructor to Vehicle, and we'll just log out a message, constructing Vehicle. Let's execute this. So we get our constructing Vehicle message. When we instantiate Car we're setting it to the variable c, even though the Car class has no constructor, it still executes the constructor on Vehicle, its base class. Now what if Car had a constructor? And we'll log out a similar message. So we have a constructor on Car and we also have a constructor on the base class Vehicle. Now this would be fine in most other object oriented languages, but in JavaScript, in ES6, it's going to cause an error. Let's see what we get. The error we get is derived constructor must call super. So by derived constructor we mean this constructor in Car and Car is derived from Vehicle, so we must execute this constructor first in Vehicle and we do that by calling a special function called super. That goes ahead and makes sure that Vehicle's constructor gets called first. And again, this might be strange if you're coming from other languages, but in JavaScript this is a requirement, you need to call super. So I'll execute this now and we get both of our constructor messages, constructing Vehicle, so Vehicle's constructor executes first, and then constructing Car. So what if Car has a constructor and Vehicle doesn't? We'll remove Vehicle's constructor and we'll take out the call to super. I'll execute it and again we get errors, derived constructor must call super. So even though Vehicle has no constructor, it does have an implied constructor. JavaScript's going to add one automatically. So we still need our call to super, and now we get our constructing Car message, like we expected. And of course we can pass arguments into a constructor. Let's pass a license number into Car. We'll give it a license of A123, a simple string, and then in our constructor for Car we'll take the license number and let's pass it along to super, then we'll put a constructor back in Vehicle, and let's store the license number in Vehicle. We know that any vehicle is going to need a license number, whether it's a Car or a Drone or anything else that derives from Vehicle, so we'll use the this keyword again, this.licenseNum = the past licenseNum. I'll just take out our log message here and at the bottom we'll log out a new message. We'll log out c.licenseNum. I'll save it and we get our license number, A123. So Car's constructor accepted the license number and it passed it along to Vehicle's constructor. And by using the this keyword, we can store the license number directly within Vehicle. If we wanted to we could also store the license number directly on Car, but we want to make use of inheritance. We want license number to be available for all vehicles.
Inheriting Properties
Let's take a look at using properties in classes and derived classes. So I simplified the example from the last video. There's an empty constructor in Vehicle and there's a constructor in Car, but all it does is call super. And we instantiate a car, we're not passing in any arguments. So let's add a property to Vehicle. Make sure you use the this keyword and we'll call the property gpsEnabled and we'll set the flag to true. So we have this.gpsEnabled = true. And this property only exists on Vehicle. Let's log it out. We'll log out c.gpsEnabled. So gpsEnabled does not exist on Car, but we can still log it out because it's in the base class. We get true. Now what if we wanted to override this value in our Car class? We could do that after the call to super. We'll set it to false and I'll save it. Now our result is false. And the important thing to remember, as mentioned before, is we always need this call to super first. If we take it out and add it after this.gpsEnabled, setting it to false, let's see what happens. We get errors again. This is not allowed before super. So again, we're getting crystal clear error messages. Super always has to be the first thing in a constructor body. So when you're working with properties it always helps to know whether or not the property exists in the base class, in this case Vehicle, or in the derived class, which is Car here. And the derived class, here Car, is always free to change the variable from the base class, set it to a new value.
Inheriting Methods
Lets' take a look at using methods with inheritance. So I simplified our example, we have a class Vehicle and a class Car which extends Vehicle, and we're creating a new car and assigning it to c. Let's add a method to Vehicle, it'll be called start to start the vehicle. So we'll log out a message, starting Vehicle, and let's call that method on Car. I'll execute it and we get our starting Vehicle message. So even though we're creating an instance of Car, the method start is still called on Vehicle because of inheritance. But what if we have that same method name used in Car? I'll just copy it and paste it into Car and we'll say starting Car. I'll run it. So we get the message starting Car, but the start method in Vehicle has been completely overridden. Sometimes you might want to do this, but other times you might want to execute start within Vehicle, the base class, and we can do that using the super keyword again. I'll call super to refer to the base class and the method is start. I'll run it. So now we get both messages. We get starting Vehicle and starting Car. In constructors you saw that super always had to be called first. But when we're overriding a method or calling the base method, we don't have that restriction. We can place it after our log statement and I'll execute it, and now we get the messages in reverse order, starting Car and starting Vehicle. So the restriction to call super as the first item only belongs in a constructor. So let's also look at static methods. What if vehicle has a static method, and we'll call the method getCompanyName. So this will be the company name that owns all the vehicles. And I'll just log out a message, My Company. So with the static method we saw in the last module, we access that with the name Vehicle itself because it's static, it doesn't belong to an instance of Vehicle, it belongs directly on Vehicle. But what if we log out Car.getCompanyName? So Vehicle has the static method, but we're trying to execute that method, getCompanyName, on Car. I'll run it and it works fine. So a static method on Vehicle is accessible by any derived class, in this case Car. And let's see what happens if we override that static method. I'll just copy it and paste it into Car and I'll just change the message. So now we have a static method, getCompanyName on both Car and Vehicle. I'll run it. We get My Other Company. So like we expected, it's picking up getCompanyName from Car. And let's see if we can call super on a static method. I'll make the call to super.getCompanyName and I'll run it. And that works fine too. So static methods get inherited in a similar way to normal methods that are used on instances. Now what happens if we try to call getCompanyName, not on Car or Vehicle, but rather on the instance that we're creating? C.getCompanyName, and here we can see c is being set to a new instance of Car. I'll run that. And we get an error, object doesn't support property or method getCompanyName. So it must be used directly on the class name itself, such as Car or Vehicle, and that's fine.
Organizing Classes into Files
So now that we're working with multiple classes, in our case Vehicle as a base class, and we also have a Car class and a Drone class, which inherit from Vehicle, we need to organize our code better. I've had everything in this one app.js file only for demonstration purposes. But in a real application, even a small one, you want to break out each class into its own file. So let's do that. First of all, we have our working folder here and the only files directly in the working folder are index.html and package.json, and that's typical. You'll see that pretty much all the time. And where things start to vary is where do you put your source code? I'm using an src directory here, but you'll also see app or even the name of the app used as the main source code directory. And there's no hard and fast rule and you'll see it done in many different ways, but for this course we'll use a source directory and app.js will go directly in the source directory, as we've been working with it. Now for all of our business objects, such as Vehicles, Cars, and Drones, let's create a new folder to hold those. We can call it classes, and I'll create three new files to hold our three classes (Typing). So we'll look at app.js, but we can get rid of this line that instantiates Car, and I took out the Drone class earlier, but I'll just move these into the proper files. And also I'll add the Drone class. So now our app.js is empty and we have three classes, Vehicle, Car, and Drone, but our module loader has no way of finding these files to load as modules. If we look at index.html again, you can see that we're importing src/app.js. So it'll find app.js just fine, but in app.js if we want to work with Cars, Drones, and Vehicles, we're going to need to import those. Now before we import, it helps if we export our classes. So I'll go to Vehicle and I'll just add the export keyword before we define the class. So we export class Vehicle, in Car we'll export class Car, but because we're using Vehicle we need to import that. So the ES6 syntax for importing a module is import and then the identifier of what we want to import, from, and then the file name, vehicle.js. And we'll do the same thing with the Drone class, and make sure we export class Drone. So now Vehicle is the only class that doesn't have to import anything. It's the base class. It inherits from Object by default, there's nothing we need to do. And Car imports Vehicle and exports Car, and Drone imports Vehicle and exports Drone. So let's go to app.js and we'll import Car and Drone so we can work with them (Typing). And these classes don't do anything right now, but let's instantiate them (Typing). I'll log out c and d for Car and Drone. So I'll make sure all these files are saved, and you'll notice we get some not founders and an XHR error. That's a 404 Not Found. And looking at the imports, you can see that I left off the folder name. We placed all these in classes. And our other files are okay, if we look at let's say car.js, it's looking in the local folder right there, so that's fine. So it'll execute app.js and we get our two empty objects. So all of our imports and exports are working fine. The key things to remember are to get the folder name right for one, and also specify the file name. Very often you'll see a default extension used and you can leave off the .js, but we're not setting that up now. So we need that .js in order for the file to be found correctly. Also notice that we didn't need to import Vehicle, just by importing Car and Drone, that's enough for us to work with those classes, even though those classes rely on the base class Vehicle. If we tried to instantiate a Vehicle, I'll run it, and you can see we get the error Vehicle is undefined. So we would just need to import that as well if we wanted to use it directly. Another thing to note is the naming convention that I'm using. We have a class called Car with an uppercase and the file is car with a lowercase, and that's fine. And the same goes with Drone and Vehicle. But if we had a more complex name, like instead of Car, what if it was named SelfDrivingCar? The convention that some people are using is to name it self-driving-car with dashes, like so. So you would use snake case for your file names. Some file names don't differentiate between upper and lower case, they're case insensitive. So that's why I would use snake case here. But as far as class names go, that can go ahead and be Pascal case where each major word is uppercase. So I'll just switch this back to Car and I'll switch the file name back and everything should be good.
Summary
In this module we looked at inheritance, using the extends keyword. Classes can extend the base class in order to get functionality out of it. We looked at inheriting constructors and we saw the need to call super in the base class. We looked at inheriting properties and we also looked at inheriting methods and functionality of the base class. Finally we organized our classes into files. We saw how we could use ES6 imports and exports in order to make sure that each class, as its own module, could access the base class.
Creating a Data Service Class
Introduction
Welcome to this module, Creating a Data Service Class. My name is Mark Zamoyta. So far we've looked at creating classes that represent real world objects, such as drones and cars, and we also looked at how we can create somewhat of an abstract class, and that was the vehicle class we created in the last module. We're probably never going to need to instantiate the Vehicle class, but it's very important because our real world classes, Drone and Car, depend upon it. So in this module we'll take a look at a very different kind of class, this will be a data service class. So let's see what we'll cover. We'll start off by creating the data service class itself and this will essentially be a collection of all of the cars and drones that our company deals with. We'll look at loading the data. Normally you're going to get your data from some kind of web service or the website itself and we'll just take the approach that the data exists in a JSON file. So once we have our data, it's definitely not going to be in the format we need. So we're going to start off by creating constructors for our cars and drones to make sure they get the information they need from the data, then we'll instantiate these objects. So once we have a car or a drone object, we'll save it in the proper collection in the data service class. They'll be two major collections, and by collection I mean an array. Arrays are very powerful in JavaScript and ES6 extends them so that they're even more powerful than ES5. Next we'll look at handling errors within the data. Most data feeds I've worked with in real life contain errors. So we're going to need a simple error class to handle things. And then we'll look at validating that data and notifying the user of any errors that occur. We want to make sure all the fields are there that need to be there and that fields are of the right data type. So once our data is all accurate and loaded into our data service class, we want to create methods in order to query and sort that data. And we also want to create methods to filter the data. We're going to use this later in the course, but filtering data is a very popular operation on websites, and we'll see how to do this within a data service class. So let's get started and create the data service class.
Creating a Data Service Class
Let's go ahead and create a data service class. This data service class will be responsible for retrieving information, specifically on cars and drones, that we use in our application. So I'm starting off with the project from the last module and we can see we have our Vehicle and this class is empty. We also have a Car class, which extends Vehicle and we have a Drone class that extends Vehicle. So we'll be populating these business classes in this module, but for now let's make a fleet data service class. I'll create a new folder called services and we'll create a file there. I'll call it fleet-data-service.js. We're going to need imports for the cars and the drones, and I'll add those in a second. For now let's just create the class statement itself. And we'll need to export this. And let's add our imports. We'll import Car and Drone. The purpose of this class is to manage these Cars and Drones, which we'll get from some kind of data feed. So let's create a simple constructor that'll hold Cars and Drones within this class. Remember to use the this keyword to access the object calling on the constructor, and the first property will be called cars and we'll set that to an array, and we'll do the same with drones. Arrays give us a lot of functionality, we can do searching, sorting, filtering, and they are very flexible in JavaScript. One thing we could also do in ES6 is subclass arrays, but unfortunately a lot of these transpilers just don't work with subclassing arrays, it's a complex feature to implement in the transpiler, but we can get away with using plain arrays here. So we have our FleetDataService all set up and we'll be populating it throughout the rest of this module.
Loading Data
So we have our data service all set up, now we just need some data. We're not going to get into backend systems in this course, so what I'll do is I'll just create a JavaScript file that holds a data object and we'll load that as a module. So I'll add a file to source and I'll call it fleet-data.js, and I'll just paste in some data. So this file would be treated as a module. We'll export a variable fleet, and there are two types of objects in fleet, one is a drone, type of drone, we have a few of those, and we also have a type of car. Now drones and cars are slightly different. They do both have a license and a type, but a car typically has a make and model and for a drone we're just listing out the model number. A car has miles, the amount of miles traveled, and also a latitude and longitude, the GPS coordinates. And looking at the drone, instead of miles it has the air time in hours. So that would be the amount of time the propellers were running, and then I also added a base to the drone only, not to the car. So these are two separate objects. And we're going to want to load these two separate objects into fleet-data-service, and obviously we'll store the cars here in cars and the drones in drones. Now let's take a look at app.js. This is where we can import our data. We'll import fleet with a lowercase f because we're not importing a class, we're just importing this object literal right here, fleet. So we'll import it from the file in the current directory, and we don't need to create cars and drones here, that was from a previous module. And let's just log out fleet and make sure we have it. So I saved everything and it ran, and we can see our array of objects here. If we open up the first one it's a type of drone. So it looks like we're getting fleet okay. So we're getting our data, now we need to send that into the data service. So let's import our data service. We'll import FleetDataService from the file in services. So let's instantiate our data service now. So we'll call the empty constructor to create a new one and we'll create a new method on dataService called loadData and we'll pass it fleet. So we'll have to parse that object within the FleetDataService in order to organize it. And we'll be validating the data and setting up methods to query it, sort it, and filter it. So let's go into our FleetDataService and implement loadData. ES6 has an easier way of looping through arrays. We know our fleet is as big array and we can loop through it with the for of statement. So I'll set a variable called data, we don't know whether it's a car or a drone. So we can use the switch statement, and we'll set up cases for car and for now we'll just add this object to this.cars. We'll call this.cars.push and we'll send it the data we're looking it. And the other case we have is drone. So we'll work on validating this soon, but let's just print out cars and drones after we load the data. We'll log out dataService.cars, and let's see what we get. Well it's not found because I'm looking up a folder when actually we should be looking in the current folder for the services directory. So I saved it and we have our array and we get four items and the model type is a car. So we have our data loaded, it's not validated, it might be inaccurate, and also our core classes are still empty. We don't have any properties or methods on Car or Drone. So let's work on getting these set up in the next video and we'll make sure all the data is valid.
Creating Core Classes
So let's get our core classes set up, by core classes I mean the Vehicle class and the Car and the Drone classes which inherit from Vehicle. We're looking at the data and we can see here that we have a drone and a type of car right here. Now the three fields they both have in common are license, a model, and the latitude and longitude. Now they also have a type in common, but that's not going to be stored in the class. The type is actually going to be the class name, so we don't need to store that. So let's go to our vehicle.js file and let's add a constructor using those three property names. We'll pass it license, model, and latLong for latitude and longitude, and we'll store them on the instance. So these are the three properties that all cars and drones will have in common. Let's look back at our data and the drone right here has unique fields for the air time and hours and the base, base of operations. So let's add those to the Drone class. And we'll just initialize those to null for now. The class we are extending, Vehicle, is going to require a call to the constructor, and we'll need to pass in the license, model, and GPS coordinates, latitude and longitude. So those will have to be passed into this constructor. There are several different ways we can go with this. We could pass all the values into the constructor, but in this case we're just going to pass what's required for the base class, Vehicle, so as we load data we'll have to set the air time in hours and the base, those two properties will need to be set by our loading logic. Now let's do the same thing for the Car class. The car type is right here and it has a make that doesn't exist for drones, and it also has a miles property that doesn't exist for drones. So let's add the constructor to Car, we'll pass the properties that'll get passed along to super for vehicle, and we'll set this.miles equal to null and we'll set this.make equal to null. So Car is all set up, and looking at Drone, Drone is all set up as well. And looking at Vehicle, both Cars and Drones will have a license, model, and latitude longitude property. In the next video we'll take a look at loading and instantiating all of the car and drone object as we load the data.
Populating Classes
So our classes are all set up, now we just need to instantiate them at the right place. So I'm in fleet-data-service.js and here's our load data function. We don't want to push data because that's the raw object that we're getting. Since we know that the data.type is being set to car, we can go ahead and instantiate a car right here. Let's call a function called loadCar, passing it data. And we can assign this to a local variable, then we can push that car into the cars array. So let's write loadCar and we're passing it car data, and here's where we can instantiate our car, let c = new Car, passing it car.licence, car.model, and car.latLong. The two other fields we need to set our c.miles and c.make, and we'll return the new car we created. And if we look back at our main switch statement here, we're only going to be working with cars. I'll leave it as an exercise for you to do something similar for a drone. So I'll just keep pushing the drone data for now, but mainly we're interested in looking at cars. I'll go to app.js and we're loading our fleet, but let's log out the license numbers of the cars. So we'll use a for up statement and we'll loop through dataService.cars. Actually this should be let car, because we're looping through cars, but then we'll log out the license, so we'll log out car.license, and we're getting loadCar is undefined, and that's from a very common mistake. We'll go back to the fleet-data-service.js file and I'm calling loadCar, but we need to prefix it with this.loadCar. And we look at our results and we have the four license numbers for the four cars we have. So everything is working fine. And again, to load the drones, if you'd like you can go ahead and do that yourself, but the process is similar to working with cars. In the next video we'll make sure that the car data is valid before we convert it into a class.
Handling Data Errors
When working with data feeds they're usually prone to errors. You're going to get values you don't expect, you might even get entire objects you don't expect. So let's add some error handling to our data service. The first thing I'll do is I'll go to the constructor in FleetDataService and I'll add a property called errors, as we get errors we'll dump them into this array. When we find an error we want to continue processing the data feed, that way the user of this service will have the option to either check out the errors or even ignore the errors and work with the data that did load properly. So what exactly are we going to store in this errors property? Let's create a class. We'll create it in services and I'll just call it data-error. So we'll just export class DataError and we'll make a constructor and we'll pass the DataError a message and the actual data we're having trouble with, and we'll store these locally in the instance. So DataError is a simple class holding a message and data. Now let's import this into FleetDataService. We import data-error.js in the same directory. Now looking at this file, one thing that could go wrong is our switch statement, we have no default. We're looking for cars and drones and let's add a default which will store the data in errors. We'll create a local variable e and we'll instantiate a new DataError and we'll give it the message invalid vehicle type, and we'll pass it data, then we'll push this into the errors array. So now we'll load cars and drones and everything else will get pushed into the this.errors property, right here. Now another place where things could go wrong is in loadCar. What happens if some kind of exception is raised when we create this car. We can watch for that and store any problem in the errors array. So we'll push a new error and we'll just say error loading car, and passing it the car that was originally passed into this function right here. And if we don't return the car right here, we'll return null. So I saved it and it looks like everything saved and we're still getting our license numbers from the prior video, that's good. Let's take a look at app.js. And instead of looping through cars, let's loop through the errors, and we'll let e of dataService.errors, and we'll log out e.message. There shouldn't be anything right now and nothing shows up in the console. So let's make some errors. We'll go to fleet-data.js and we'll just chagen drone, I'll just add a d in front of it and save it, and we get an invalid vehicle type. That's fine. And in the next video we'll take a look at validating the car data.
Validating Data
I'm in fleet-data-service.js and let's look at validating our car data before we load it. If we look in loadData at our switch statement for the car, you can see we're loading the data directly, but we need to do some validation before we load the data. So we'll create a method called validateCarData, passing it the data. If that returns true, then we'll go ahead and load the car data, but if it returns false we should probably log an error. We'll create a new DataError and we'll leave the message as invalid car data. And we'll pass it the data we used to attempt to load the car, and we'll make sure we push this into errors. So let's create validateCarData right here. We're passing it the data, let's just call it car, and the first thing we should do is make sure that any required properties exist on car. Let's make a list of the required properties. So these would be license, model, latLong for latitude longitude, miles, and make. And we want this to be an array so we'll split it, passing it a space as the delimiter. And then we can loop through these requiredProps using ES6's for of statement. So we'll call each property field and we want to make sure that car has that field. So if it doesn't have the field, we need to raise some kind of error. Let's call this.errors.push and we'll push a new DataError, invalid field, and we're using ES6's template syntax, the back tics here, so we can interpolate a variable, and we'll use field. And we also pass car to the DataError. So we're pushing the error, but we might want to perform more checks. And we're also going to have to return either true or false because we wrapped this in an if statement to see whether it's successful or not. So let's create another local variable here. We'll call it hasErrors and we'll initialize it to false. So after we push the error we can set hasErrors to true. Now there's a lot of validation we could do here. Checking for required fields is one thing you'd want to do, but each field in itself could have its own checks. You could check a valid format for the latitude and longitude, you could make sure the license number is actually something that would be valid, and we're not going to go through all these cases, but let's just check one. Let's make sure that miles is actually some kind of number, the number of miles driven. If that's not a number, then we have a problem. So we'll check for is not a number on the number object that's new in ES6, and we'll pass it Number.parseFloat(car.miles). So remember, we're checking for is not a number when we parse miles. So in this case we'd need to add an error if this is true. We'll push a new DataError, invalid mileage, and we'll also pass it the car. We'll set hasErrors to true. And finally, we'll return hasErrors or actually we'll return a negated hasErrors, so if there are no errors we'll return true. If there are errors we'll return false, and that fits the name of the function, validateCarData. So let's go back to where we call this, right here. If validateCarData is true, we'll go ahead and load the car, but if you remember loadCar also returns null if there's any kind of exception thrown. So let's be sure to check car before we push it onto the array. So there's our validation code. Let's try this out and make sure it works. I'll save this and go to app.js and let's create some errors. You can see here that we're already logging out our errors, so that's good. Let's go to the fleet-data.js file, we'll go down and find a car, here's one, and let's just take out the license, I'll comment it out. I'll save it and you can see we are getting our errors now, there's an invalid field license, the license is missing, and we get our invalid car data error as well. If we put that back and save it, the errors disappear, so that's good. The other thing we're checking is miles, what if we enter an invalid number here? I'll just add some characters and save it and we do get an invalid mileage error. So our validation is working. Going back to the fleet-data-service, we would need to implement the same thing for drone, of course. And I won't walk you through that now, you could do it as an exercise if you'd like.
Querying and Sorting Data
Now that we have all our data loaded and it's been validated, we're sure we have quality data. And we're going to need to access that data somehow. If we look back at fleet-data-service, we could always access the cars and drones fields right here, but in some cases you might not want to do that, you want to make things easier on people who consume this data service. It might be really valuable to have a method on the data service that can look up a car by the license number. So back in app.js let's take a look at the method that we might want. We want a method called dataService.getCarByLicense, so instead of getting cars and looping through each one looking for the license, we can let our data service do all the work. This might be a method that gets called often. So this would be convenient. And we'll pass in a license, I'll just look at the data here, and I'll grab this one, and let's just log out the car. So getCarByLicense should probably return undefined if a license doesn't exist. If it does exist it'll return the entire car object. So let's create this method, getCarByLicense and we're passing it the license. So we'll just go ahead and return the car that we find. We'll look in this.cars and since that's an array, we can use the find method. Find will loop through the entire array performing a function, and the array is filled with cars, so we'll create a function being passed to car and we'll return car.license is identically equal to license. That's the argument being passed in. If nothing's found, undefined will be returned. So let's save this and we'll go back and save app.js, and we found an object, and it is license number AT9900. So this method is working fine. So we can retrieve cars by the license number, but what if we wanted to return all the cars sorted by the license number? Let's create a method for that. I'll just comment this out for now, and we want something like this. We'll create a variable cars, we'll call a method on our DataService, and we'll just call it getCarsSortedByLicense. And we don't need to pass it anything because we're going to be looking through all the cars. And then we'll just print out the licenses and make sure they're sorted. So again, we'lll do a for of statement. I'll copy the new method and we'll add that to the data service. So to sort an array there's simply a sort method on it, let's use it. We'll return this.cars.sort and we'll pass it a sorting function, which will take car1 and car2. We need to compare these two cars to determine which license comes first. If car1.license is less than car2.license, we'll return -1. Car1.license is greater than car2.license, we return 1, otherwise we return 0. So if we return -1, that means car1 comes first alphabetically. If we return a number greater than 0, then car2 comes first alphabetically. If they're the same we return 0. So I'll save this and make sure app.js is saved too. And we get our licenses, and we're actually getting our car objects. So we're getting back an array of cars, so let's call it car, let car of cars, and we'll log out car.license and that will give us just the licenses. Now we can see that they are alphabetical now. AT2000 and they increase up to AT9900. So there are probably lots of different ways you'd want to access and sort this data. And it's valuable if you can add helper methods within your data service for this. This will prevent application programmers from constantly writing loops or functions like this one.
Filtering Data
Another popular operation we can perform on data is filtering. You want the user to be able to start to type some kind of string and filter out the cars that match that string, and that's a filter operation. So let's filter out the car data, we'll take a look at it, and we have some makes here, Tesla, Uber, another Uber, and a Lyft. These are just fictional self driving cars. But let's filter out by make. If we start looking for something like a capital U, we should get the Uber or if we searched for just an e, we should get Tesla, Uber twice, and Lyft should be ignored, there's no e in it. So let's go to app.js and we'll just comment this out. And how do we want to filter? We know we're going to get a group of cars, there could be more than one, so let's let cars = dataService.filterCarsByMake and let's pass it the U, like we saw. It should get the two Uber cars, and we'll just log out car.make. So we need to create this filter method. And we'll pass it a filter string called filter. This should be pretty straightforward since there is a filter method on arrays already. We'll return this.cars.filter and let's use the new fat arrow function in ES6. Instead of creating a function here, we'll specify the input parameter, car, that's what cars is made up of, individual car objects, and we'll use the fat arrow symbol, goes to, and we'll just check if car.make.indexOf(filter) is greater than or equal to 0. So this will return true or false, depending upon the filter, if filter appears in the make. I'll save this and we do get our two Uber cars. Let's change the filter, I'll go back to app.js, and we'll look for lowercase e, and now we get Tesla, our two Uber cars, and it leaves out Lyft. So our filter is working fine. So that's all the operations we're going to perform on our data service for this course module. A real data service might have dozens or even hundreds of these access methods, as well as methods to update and insert cars, drones or whatever other data we're keeping track of.
Summary
In this module we created a data service class and we looked at loading data from a JSON file. This could have just as easily come from some kind of web service or website. We looked at how we can instantiate objects as we're loading the data, and we looked at handling errors in the data. Even though there's an error in an object, we still want to continue loading the data. We'll just report the error and move on. We looked at validating data and again we skipped any invalid objects and moved on to the next one. And we looked at how we could query and sort data by creating methods in the data service class. Finally we looked at filtering data in the data service class. this is an important operation to help the users find data quickly while typing. So in the next module we're going to switch gears a little bit. We're going to be looking at creating a user interface by using objects, and that's a totally different use of objects than we've seen so far. But we'll see how classes can be used for user interface objects just as easily as we use them for business objects, such as cars and drones we've been working with.
User Interface Classes
Introduction
Hi, my name is Mark Zamoyta and welcome to this module titled User Interface Classes. So far we've been using classes mainly for business objects, for drones and cars, which are the main entities within our application. But we could also use classes for the user interface. Each HTML element we use can become a class and we can create a base element class so that all UI elements share the same functionality. So we'll see how to do that in this module and let's take a look at the actual user interface elements we'll create. We'll start off by creating a Button class. We'll be using Material Design Lite, which works very similarly to Bootstrap, and we'll go over how to get set up with that and how to create elements in that style. Next we'll create an Image class. We want to be able to easily load up an image and display it in a webpage. Next we'll create a Title Bar class, which is also part of Material Design Lite. Here we can see a simple header with a hamburger menu. We won't worry about links and routing in this module, that'll be covered in the next course module, but for now we'll get our title bar set up and it will be responsive. If the screen is wide enough, menu items will appear on top and right aligned. Next we'll create a Data Table class. We'll just create a grid of data. Here we can see car information. We'll pass it the headers we want along with the data we want to display. And finally, we'll create a Map class using Google Maps. Google's API works with global variables, so we'll see how to integrate those into our application, which works mainly with classes. So let's get started and get set up with Material Design Lite.
Material Design Lite
Let's take a look at Material Design Lite. I'm at getmdl.io. Material Design Lite is a lot like Bootstrap, it styles elements by adding lots of small classes to them. In Bootstrap the classes being with bs and in Material Design Lite the classes being with mdl. And just like Bootstrap uses JavaScript, MDL uses JavaScript as well, mainly for some of the more advanced features, but we can get away with styling components with just mdl classes. Let's take a look at some components, I'll click on the Components link and along the left side you can see various components that we have to work with. Let's start off by looking at Buttons and these are favorite action buttons, but let's scroll down and look at something we would use in our app. I like this one with the ripples. When you click on it ripples come out from the exact position where you click. And it looks pretty cool, so we'll turn this button into a class in this module. And what we can do is simply, if we scroll down a bit, we can just grab the button tag right out of here. Here we see examples of the mdl classes I was talking about. We have an mdl-button, mdl-JavaScript-button, mdl-button-raised, and so on. Another component we'll work with is the Layout. If we scroll down a bit, here we can see a navigation layout where we have a menu on the left side. It scrolls in and out and contains the lengths. I'd actually shrink this down so it's wider. You can see that the links appear here in the upper right as well, and if I zoom back in as if it were a tablet or something, we only get access to the links in the left side menu. So in this module we'll be working with a simpler version without a picture. We use this one right here, it simply has a title and the left side menu. And just like with the button, we can just copy and paste the HTML right out of this example. And we'll be doing that later. So feel free to check out all of these components. I don't think there are as many as in Bootstrap, but you can get styling very easy by using Material Design Lite. In the next video we'll take a look at setting up our application so that we can use this.
Setting up Material Design Lite
Let's set things up so we can start working with Material Design and we're also going to need to set up jQuery. We want to use that to manipulate the DOM, so I'll open up a console window and the first thing I'll do is I'll install Material Design Lite. Npm install -save material-design-lite. And you can see here we have version 1.1.3. And if we quickly look at the Material Design website, I'll click on Getting Started. I'm at getmdl.io. I'll click on Getting Started and if we scroll down a little bit, we can see we need the CSS and the JavaScript right here. Now I just installed it with npm, so I'll include similar links and a script tag for that. I'll add the links right below the title, so the first link is going to fonts.googleapis.com and grabbing an icon family, and the second link is looking in node_modules and that's what's grabbing material.css. Now we also need the JavaScript file and I'll add that after our ES6 module loader. So we're looking in node_modules/material-design-lite/dist/material.js. So that's what we need to start working with Material Design Lite. Now we also want to work with jQuery. We want to manipulate the DOM and make sure that it works in a browser independent way. So let's go ahead and do an npm install -save and we'll enter jQuery. So we have jQuery@ version number 2.2.3, that's fine. Now we want to use jQuery as a module and jQuery came way before the ES6 module loader was invented. So jQuery doesn't use ES6 modules. It uses either of the two major standards RequireJS modules or CommonJS modules. So the ES6 module loader isn't going to cut it for us if we're going to use jQuery. What we need to use is SystemJS. SystemJS will load ES6 modules as well as CommonJS and RequireJS modules. So let's grab that with npm. We'll do an npm install -save systemjs. So we're looking at systemjs version 0.19.27. So now that we have systemjs, let's take our ES6 module loader here and I'll replace it with the script for systemjs. Node_modules/systemjs/dist/system.js. And systemjs actually extends the ES6 module loader, so our system.import stays exactly the same. But what we do need to do is we need to tell system that jQuery is a loadable module. So I'll specify system.paths and we'll create a path for jQuery and this will be what we'll use to import jQuery as a module, and we'll set that to node_modules/jquery/dist/jquery.js. And we'll see shortly how we can load jQuery as a module into ES6 modules. Systemjs will handle that for us. And notice that we don't have to add the script tag for jQuery, we can skip that since now systemjs will handle loading jQuery as a module when we need it.
The Base Element Class
Let's create a new folder so that we can organize all of our user interface classes. I'll go to src and add a new folder. We'll call it iu, that's simple enough, and we'll add a file in here. Now the first class we're going to have to create is going to be base element. A webpage is made up of elements, which are simple tags that you see such as div or p for paragraph, and there are dozens of them, and all of these elements are going to have common functionality. So we'll create a class that will hold this functionality and all user interface classes will derive from this. So we'll call this base element class, base-element and we'll export class BaseElement. Remember we use Pascal casing, uppercase words, for class names, but for file names we use snake case, all in lowercase with dashes, such as base-element. Now we know we're going to be using jQuery and we already have things set up to use jQuery as a module. So we'll import the $ from jQuery. The $ is the common symbol to use for jQuery, so we'll use that. And notice we don't have curly braces around the $. That's because jQuery has a default export and that's what we want, the default export. So what are some of the features that all elements are going to have in common? We know they're going to all extend base element, so first of all, let's create a constructor. And let's store the jQuery object here for the element, we'll call it element, and we'll initialize it just to null. Let's also leave a comment that this is a jQuery object. That way if developers look at this they won't get confused and think that, hey maybe it's a DOM object. They'll know that we're storing things as jQuery wrapped objects. Now an element, before it becomes part of the DOM, is simply a string. Let's create a method called getElementString. Now each component is going to have its own string that has the tag and any classes, any attributes, that kind of thing. So we want to make sure that this gets overwritten. One way we can do that is we can throw an exception here. If it doesn't get overwritten by an element, we'll leave the developer a nice message that lets them know that they need to override this. Please override getElementString in BaseElement. Another method we're going to need is a method to actually create this jQuery wrapped object. Let's call it createElement and we'll let s = getElementString and then we can actually assign this.element and we'll use jQuery here, we simply pass it the string to create the element in jQuery. We're also going to need a method to append this element to some other element. Let's call it appendToElement and we'll pass it the element we want to append to. And here's where we'll call this.createElement and we'll call el.append, this is a jQuery method, and we'll pass it this.element, and that's all we need for now. I'll save this and our base element is all set up. Now when we create any kind of element, which is simply a DOM tag, we could have that class extend the base element and we'll get access to all this functionality. We just need to make sure that we override getElementString.
A Button Class
So we have our base element set up now and all of the user interface components we create will derive from this, well extend it. One thing you want to check though is I think I might have forgotten a this. Make sure when we call a function in the class we call this. function name, this.createElement, and this.getElementString. And this is in base-element.js. So let's create our first component that we can look at. We're not going to instantiate base-element, but we will instantiate let's say a button. So I'll create a new file and I'll call it button.js. Let's import the base element and it's in the current directory, the same directory as this file, and we'll create our class and export it, export class Button extends BaseElement, and we'll create a constructor and we'll pass it the title for the button. The title will be the text that appears in the button. We need to call super and we'll save the title. Now if we look back at base-element, we need to override getElementString. This will create the string for the tag. And we can create this string by looking at the example on the mdl website. I'll go back to Buttons and this is the rippled button we want to create. So I'll copy the button tag and we'll return that big string. We'll use an ES6 template string. I'm using the back tic, normally found under the Escape key, and this will let us put new lines into the string. So we can use interpolation, instead of having the string button we can grab our title. So I specify this.title inside $ and curly braces. And that's one of the great features of using template strings, we can do this interpolation. Now in addition to all of these classes listed, we might want to style this as well. All we have are mdl classes, but for now let's see if we can get this button to show up. We'll go to app.js and I'll delete all of the code we had from the prior testing we did, and here's where we're going to want to import jQuery. So we'll import the $ from jQuery. And system.js knows exactly how to get that for us, even though it's not necessary an ES6 module. Let's create a new Button and we'll just set the title to Click Me. And now that we have the button instantiated, we want to add it to the body of the document and our base element has the functionality for that. B.append, let's just take a look at that for a second. The method is appendToElement and we pass it the element. So appendToElement and we'll pass in the body. We have to wrap it as a jQuery object. I'll save it and we'll see what we get. Well we get a reference error on Button and that's because we didn't import it. We'll import it from ui/button.js. And yes it is kind of a hassle to be working with imports for everything, but it does solve the big namespace problem we've been having in JavaScript. We can work with lots of different libraries that have buttons and if there was ever any kind of conflict we could just give an alias to button. We could say something such as Button as MyButton. But since we don't have any conflicts, we can just use Button as is. I'll save it and we get our button. If I click on it, you'll notice that we don't get the ripples, and there's a reason for this. Material Design Lite has no idea that we dynamically added this button to the page. So we need to notify it. Let's go to base-element.js and we'll create a new function. We'll call it enableJS to enable JavaScript on an object. The function we'll call is componentHandler.upgradeElement and this is part of Material Design Lite, this comes with the JavaScript file we loaded and I'll specify this.element and I'll get the unwrapped DOM object. So I'll specify index 0 on this.element. So after we append to element, let's call enableJS. I'll save it and we'll see if it works. So now the ripples are working fine. Everything's good. Now there's no other styling in this button, but we can add that later. For now we have a very easy way of creating user interface elements. If we look at the button code again, all we need to do is set things up in the constructor, in this case we just set a title, and then we override getElementString. And as you saw, we can just copy and paste this element string directly from the mdl website.
An Image Class
So we have a button component, let's go ahead and make an image component. We won't need to use mdl for this, we'll just use straightforward HTML. So I'm in button.js and I'll just copy all thse. We used this as the basis for an image component. I'll create the file, call it image.js, and we'll name it Image and it extends BaseElement. For the constructor we'll pass in a fileName and we'll store that off, and we can just replace the whole button tag right here with an image tag. And we'll give it a style, just setting the width to the full width of the viewport. And for the source we can go ahead and use the file name that was passed into the constructor, this.fileName. So I'll save this file and we'll go to app.js and we'll make use of this new control. We'll create the new Image and let's create an images folder and I'll call the image drone. I think it's a jpg. I'll load this in a second. Then we'll call i.appendToElement and we'll also append this to the body. Now I'll create the images folder and I'll drop in the image. So I dropped in drone.jpg. Going back to image.js, we're going to need to look up one folder here because we want to get out of the source folder. And I'll make sure we import image and I'll save it. So we get our button on top and then right underneath that we get our image. And it does take up the full width of the viewport.
A Title Bar Class
Now let's create our title bar. This is the title bar we saw earlier that had the left side menu that pops up when you click the button. Or if the screen is wide enough, it'll show the links directly on the control. We'll look at this in a second, but for now let's create our file. We'll go to UI and add a new file, we'll call it title-bar.sj. And I'll just copy the Image class and paste that and we'll name it TitleBar extends BaseElement, and we'll pass in the title. This will be the application title. And for the getElementString we'll do the same thing we did earlier, we'll go to the mdl website. So I'm back at getmdl.io and I'll click on the Layout button and we'll scroll down and grab the one we want. We'll take this one, the fixed header. When I click on the little hamburger menu we get our menu slide in from the left, so that's what we want. I'll scroll down and I'll copy the source code right here. Going back to the title-bar.js, I'll paste in the code. So here's the markup. Let's find the title, and we use interpolation to get the title put in there, $, curly braces, and we'll add this.title. Now I'll save this and go back to app.js. In app.js I already imported TitleBar right here and we create a new TitleBar and we'll just call it Our Application for now. We call tb.appendToElement and we wrap the body as a jQuery element. I'll save it. And we can see a few problems. The title isn't showing up properly and the menu button seems to be out of place. But we are getting Our Application. So there's probably one other location where we need to set the title and the markup. Let's look for that. It's right here. So now our title, Our Application, that's showing up properly in both places. So the only thing now is our menu button is a little bit off. And this is actually because we're not specifying that we're using HTML5. So let's add a DOCTYPE tag. I'll save it and that fixes our problem. We click the button and the menu opens up fine. So let's look at how we can set these links. Right now there are four links, but we want to be able to specify our own links. Let's go back to title-bar.js. In the constructor we're setting a title, but let's also keep track of links and an array. This.links = and we'll just create an empty array. Let's create an addLink method, we'll pass it a title and the href for the link, and we can just push these into links, this.links.push, and we'll push a new object that contains both the title and the href. Notice that I'm using ES6 syntax here. We used to have to put title: title, like so, but now in ES6 we can just specify title. And it's implied that you want the property name to be the same as the variable name, title, and the same with href. So links will get populated and then when we get the element string, let's create a string that contains our links. We'll let links equal an empty string, then we'll add a for of loop. So we'll loop through each link and we'll add this anchor tag. For the href we'll specify the link.href, we're using interpolation, and the same with the link.title. So as we build up this links variable, it'll look very similar to what we have right here. So let's cut this out and we'll use interpolation to get links put in there. And we have the same thing down below. So hopefully this will work. We'll take a look at calling addLink. So I'll save this and we'll go to app.js and after we create our TitleBar, we'll call tb.addLink and I'll just add a bunch of links. I won't specify a link to actually navigate to yet. And I'll save this. The browser refreshes and let's open the menu. And we get our application and we get our for links. So this looks good. If we open up the window really wide, our links show up in the title bar, so that's great. Now this title bar is pretty complex. If we look at the HTML again, there's really a lot we could do to customize this. But in this video we saw how we can do things such as change the title and add links to it. If you wanted to change the style or any other properties, you would follow these same techniques.
A Data Table Class
Let's add a data table to our list of UI objects. I'm here at getmdl.io and let's click on Components and we'll see what Material Design Lite has in terms of a data table. If I scroll down, I'll click on Tables, and here we can see a simple data table. So let's use this to show our list of cars and drones. If we look at the markup for it, we can see the table tag has a bunch of classes, prefixed with mdl. So we'll be using those. And then there's a class here for the header and for our rows we use the same non-numeric class here. So this looks pretty simple, let's just copy it. So we'll create a new file in UI and we'll just call it data-table.js, and I'll paste in the boilerplate code. So we'll import the base element, then we'll export class data-table, and of course that extends BaseElement. We'll just call super in the constructor. So let's take a look at how we want to use this data table before we paste in that markup we just copied. Let's go to app.js and here's our TitleBar from the last video. Let's just remove that. We'll import the DataTable and for starters, let's just go ahead and create the DataTable. So what do we want to pass to the constructor in order to create this table? Well we're going to need two things, we're going to need the headers and we're going to need the data to show in the rows. So let's pass those in. So let's start off by drawing a table for cars. What do we want headers to be? Well we can list out all the headers we want. If we look at the Car class, let's just pick some of these fields. We have license, model, miles, and make. We'll just leave out the latitude and longitude for now. So let's add those as headers. So we'll put it in the order license, make, model, and miles. And we'll just turn this into an array. So we have our headers for the table, now let's grab the data. We'll use our dataService, and when we construct DataTable we'll just send in dataService.cars. And then we're going to need to show this, so we'll call dt.appendToElement, and we'll attach it to the body. So now we know for DataTable we're going to need to accept an array of headers and the data for cars, an array of car objects. So I'll add headers and data, and we'll just store these off. So we want to leave this table flexible enough to handle any type of data, whether it be the cars or the drones, or any other data we'll have in our app. So now that we have all our information, let's go ahead and create the string we need. If you remember, looking back at base-element, we need to override getElementString. And what we'll do is we'll just return, we use a template literal notation, and I'll paste in the markup I copied earlier. So we have our table tag and all of our mdl classes. We'll just remove the selectable one, we don't want to make it selectable right now, we want to keep it simple. And for our table headers, we're going to generate these automatically, so let's replace this with interpolation. We'll create a new variable and we'll call it thTags for the headers. Likewise, if we look at our tbody, we're going to dynamically generate all these rows. So let's do the same with these, we'll replace this with trTags for table rows. So now it's just a matter of creating these two variables. I'll initialize them both to empty strings, and let's create these now from the data. So I pasted in this loop, we'll do a for of loop and we'll use the variable h to loop through each one of this.headers. Remember headers came in in the constructor and we stored it right here. So when we build up our thTags we'll += another template literal string. And I'm going to go ahead and leave this nonnumeric class here. This just makes sure that everything is left justified in a table cell. And that's fine for our purposes. In a more serious data table, of course you'd want to have things left, right, or center justified. So we'll just user interpolation to get our header put right here. So thTags will get built up and will place it right here. And building up the trTags is a little bit more complex. I'll paste in the code for that. So to build up trTags, we'll do a for of loop, we'll use the variable row and we'll loop through all of our data. And we're building up a table row so we start with the trTag and we'll end it with a /tr tag. And inside each row, we're going to need to create a td field, a table data field. So I'll have another for of loop and we'll use the property variable and we'll loop through the headers. Now the headers were initially created in uppercase, so we have to get the field by accessing row from our outer loop, and we'll use bracket notation and we'll call property.toLowerCase. So we're using our headers in order to grab the data, we just have to convert it to lowercase. Now you're not always going to be able to do this, but in the interest of time, it works fine for our purposes. So we start building up our trTags variable. We already added the tr and now we're going to be adding the td tags. And we'll use interpolation to put the field in there as well. So by the end of this outer loop, trTags will be all set up to go. And we place it right here in the tbody tag. So let's see if that's good enough. I'll save it and I'll see if we get what we expect. Well we're getting our headers, License, Make, Model, and Miles, but I probably did something wrong loading the data. So I'll go back to the app.js file and we don't pass the fleet to the constructor, we actually call dataService.loadData and we pass the fleet here. So I'll save this and our browser syncs up and we get our data. We have our License, Make, and Model and Miles for each car. So this looks good. So our data table is working. We just need to set the headers, set up our data service, and pass the information to the data table. Normally a data table would take many, many man hours to create and we only have 10 or so minutes, so just remember when you use it as is it's going to convert this to lowercase in order to get a property name. And that might be one of the first things you'll want to implement is passing along not just the headers, but another field that specifies the property name for each header. But for now everything's working fine, we can show the cars perfectly.
Setting up Google Maps
The last UI element we'll create is a Google Map. We want to be able to plot the location of cars and drones on the map. So I searched for Google Maps API simple map and we'll take the first link here. And here we have the simple map page. If we scroll down we can see the JavaScript and the JavaScript plus HTML that we'll use to generate a Google Map. Looking down further we can see the script tag that we're going to need. Now you can go ahead and copy and paste this into the project, I already did that. But an important part of this is that we're going to need to generate an API key. And the way to generate that key is to, well first of all you can scroll up, and there's a big Get a Key button, you can follow this and create your own key. You'll see my key, but that'll be invalidated by the time this course ships, so it most likely won't work. But looking at the code again, you can see that this script looks at maps.googleapis.com and we specify the key and we also specify a callback to a function. We want to make sure that maps get initialized first before our application starts. You could do this asynchronously too, but in the interest of time, we'll just wait for the callback before we start up our application. And of course there's code that we need to execute to create the map, but we'll see that soon. For now let's just make sure that we're setup to load Google Maps. So here you can see my script, it has the async and defer attributes, and we have our source setting, and you can see the giant key. You should generate your own because if you use this one it won't work, and then for our callback we're setting it to googleMapsCallback. So in our script section here in the body, we'll create a function called googleMapsCallback. Google Maps works by global variables. We need to access to the Google global variable. So we'll just wait for this callback and at that point we'll boot up our app.js file. And if I save this and make sure the browser refreshes, everything worked fine in the browser. So we're still showing our data table from the last video, but let's show a Google Map now. We'll see how to do that in the next video.
A Google Map Class
So we have Google Maps initialized, let's go ahead and create our UI class for it. In src, we'll go to UI and we'll add the new file here. We'll call it Google-maps.js. And I'll just paste in the code for this class. So we'll export class GoogleMap, and let me just quickly rename this so it's Google-map.js, that makes more sense, and we'll extend BaseElement. In our constructor we'll send the centerOfMap, that'll be an object with latitude and longitude values, we'll see that in a second at the Google Maps website. And we'll also pass in the data. We'll store the centerOfMap onto the instance as well as the data. And if we look below, we have getElementString right here and the important thing to note about this is that we're using a simple div, but we need to set the width and the height of the map. So I do that here in the styling. And also the way Google Maps works is it's going to need an id, so we'll create a simple id of map and that's how Google will know where to draw the map. So we have our string, but now we're going to need to have Google create its own map object and display it in this div. And we can do that by overriding createElement. We'll call super.createElement, and then we need to set a timeout. The way our framework works here is that we call createElement to create the element obviously, so by the time this is called, the element doesn't exist in the DOM. All we'll have is a string here. So we need to wait a cycle in order for the DOM to be created and for Google Maps to be able to find our id of map, right here. So that's why I'm calling setTimeout and I'm using the fat arrow symbol to make sure we get access to the this keyword and that's set properly to this class. And we'll wait 0 milliseconds, so on the next cycle after the DOM gets created, we'll call this code. Now if we left this setTimeout out of this function, we'd get an arrow when we try to create our map because the DOM wouldn't be created yet. Google won't know where to place the map. Now before we look at this code here, this is the code that creates the map, but let's look at it first on Google site. I'll click on JavaScript and here we see how to create a map. We create a new instance of Google.maps.Map and we pass it the element and we want to center the map on a latitude and longitude value, and we also want to set a zoom value right here. Looking back at the source code, here's where we do that. We set our map variable here, we'll create a new window.google.maps.Map, and we pass it the id map. We set a zoom value of 13, that'll give us a close-up look, and we want to center it on the centerOfMap field that we loaded in the constructor. So once we have our map created, the next thing we want to do is put markers down for the location of the cars or drones in our data. Let's go back to the website and see how we do that. I'll scroll down a bit on the left side here and I'll click on Simpler markers, and here we see a simple marker in Australia. In order to create that, let's look at the JavaScript code. So the map's being created right here and then below that we're creating a marker. We create a new instance of Google.maps.Marker and we set the position to the latitude and longitude. We specify the map we're working with, and we have an optional title. Looking at our code, you can see that we're going to loop through this.data. This will be our car or drone data. And we'll look at our vehicle and we'll look at the latitude, longitude field. And remember that data is separated with spaces. So we'll use ES6 destructuring to take the latitude and longitude and assign them to the lat and long variables here. And I'm just logging out the latitude just to make sure that these values come through okay. You don't need to do this. Then we call new window.google.maps.LatLng in order to create the proper latitude and longitude value, then we create our new marker. We set the position and we specify map as our map. Once our marker is set up, we call marker.setMap, passing it the map. So the section above, this is where we create the map and this loop here is where we plot the location of all of our vehicles on the map. So I'll make sure this is saved and let's look back at our files and we'll go to app.js and I'll just take out the information that we were using for our data tables, and we'll create a variable called map, we'll have to append that to the body, but let's let map = new GoogleMap, and then before I forget let me import this and we need to pass GoogleMap the centerOfMap and also our information. Let's pass it the cars, and we just need to create centerOfMap now, I'll paste that in. So centerOfMap is a simple object with latitude and longitude values. So we'll create the GoogleMap and we'll append it to the body. I'll save it and we look at our website and we're getting the map. You can see our four cars and they're plotted within Central Park, Manhattan. And this is a fully functional Google Map now, we can zoom in and look around, and we don't have any information associated with these cars, but that's fine. We could also go ahead and plot the drones. Let's do that. Instead of passing in cars, I'll pass drones. The browser syncs up and we get the location of our three drones, which are also in Central Park. So we're all set up to work with Google Maps. It's a little bit difficult working with them because it doesn't use ES6 modules or any other modules, it uses global variables, but we saw here how we could create a GoogleMap class and easily post the map into our webpage.
Summary
In this module we took a look at Material Design Lite, it's a frontend framework similar to Bootstrap, and we looked at how we could load this into our project. Next we created a base class for elements. All elements on a webpage are going to share some functionality, so by using a base class and inheriting from it, we're able to simplify our code a lot. Next we created a Button class, an Image class, and a TitleBar class. We saw how the title bar is responsive and it comes with a side menu, which swipes in when you press the button. If you have a wide screen, the links from the menu will appear on top, aligned to the right. We also created a DataTable class where we passed it headers and data to display. And finally, we created a map class using Google Maps. We saw how the Google API uses global variables and we saw how to integrate that into our module and class based ES6 project.
Building an Application Using Classes
Introduction
Hello, my name is Mark Zamoyta and this module is titled Building an Application Using Classes. Now normally if you're going to build an application, you'd want to use some kind of bigger framework, such as Angular. But for a fast and dynamic website, you could do it by building your own classes, and that's what we'll explore in this module. We'll start off by creating an application class. This class will hold our entire application and that's a best practice that you'll see in many different frameworks. You start off with one base application class and everything is included within it. Next we'll take a look at setting routes. The application will have a menu and we'll be using Material Design Lite for that, and when we click on different menu items we want to route to different pages. So we'll get that all set up. We'll create a homepage and we'll create a car page to view the cars, and finally we'll create a map page using the Google Map element that we created in the last course module. So by the end of this module you'll have a functioning web application and the entire application is based on classes. All of the user interface elements are classes and each application page itself is a class. So let's get started building this.
An Application Class
So let's start putting together a framework so we can build a website, and this website will consist of almost all ES6 objects. Now if we're going to build a framework, experience shows that it's very good to have a single application object. So let's create a class for that. First I'll go to src and I'll add a folder called framework, and let's add a file called application-base.js. When we think about modules, we need to think about code reuse and within our careers we'll be building many dozens, may even hundreds of applications. So each one will have its own application object, but we can put all of the shared functionality into an application base class. So when we create a new project, we'll just derive our application from application-base, we'll extend it. So let me paste in some boilerplate code. So our only import is TitleBar, that's the TitleBar we created in the last module, and we'll use that throughout our application. That'll be a mainstay on the whole website. So we'll export class ApplicationBase and in the constructor we'll just pass the title of the application, and we'll store that on the instance right here. We'll also set up our TitleBar. We'll create a new TitleBar, passing it the title. So now that we have the ApplicationBase, let's actually create the application. And we'll create it by extending this. So we've been using app.js for testing, and that's been fine, but now we want to create a real application. I'll cut out a lot of these imports. We're going to need jQuery and our data service with the fleet information. We're not going to need Button, Image, TitleBar, DataTable, GoogleMap, those can go. But we are going to need to import the application base we just created. So I'll export class and we can call the class just plain old App and we need to extend ApplicationBase. So we just need to remember that if there's code that we need to share between applications, it would helpful to put that functionality in ApplicationBase, that would be good code reusability. And everything in this class, App, needs to be specific for this application only. Now one thing that we're going to need in our application is our dataService. And we don't want to use the let keyword, we want to use this to put it on the application instance. So we have our dataService and you don't want to forget to call super on this as well. And remember we pass ApplicationBase the title, and let's just call the title of this application Fleet Manager to manage our fleet of drones and self driving cars, and we can go ahead and cut out the rest of this code from the last module and we have our Application class, but there's one more thing we need to do within our Application class. You'll notice that while we have the App class, we did not instantiate it. So let's go ahead and instantiate our App class. Sometimes this is called bootstrapping. It means instantiating that one App class so that we can use it as the basis to load all our other classes and objects. So what we can do is we can create a variable called application and set that to new App. But we might to use this application object throughout the application. There might be functionality in here that some of our web pages are going to need, especially when it comes to routing from page to page. So let's use ES6 to export application. Now any one of our web pages can import application and have access to our global object. Another thing we need to do is actually show the application. We'll create a method called show and we'll pass it the body. So the application will be shown in the entire viewport, in the entire webpage. Let's create this method show. We'll go to application-base and we'll pass it the element to use as a parent, and we'll just show our titleBar for now. We won't add any routes or default routes yet. So we'll call this.titleBar, which we set up in the constructor, append to Element and pass it the parent element. So I'll save these files and let's see what we get. We get a ReferenceError dataService is undefined. Let's see what's going on with that. I just forgot the this keyword before dataService. In the last module dataService was just a local variable, but now it's on the instance. So I refresh and we get our Fleet Manager banner. The title bar seems fine and we have no links, but our side menu is working fine as well. So in the next video what we want to do is we want to create pages. The page will exist below this title bar and we'll set up our links right here and also up here when the page is wide enough, and we'll use those to route to different pages that we'll build in this module.
Setting Routes
So we have our title bar showing, the next thing we need to do is add some links, which are essentially routes. Clicking on a link will route to a different page, which shows under the title bar. So I'm in app.js and let's see how we'll do this. We can create a new method and we'll call it addRoute and the first argument can be the name of the route, which will also appear as the link text. So the first one will be Home, next we'll pass it an object and I'm just going to pass null for now. We'll fill this in later in this module. And the third argument will be whether it's the default route. So let's say true for home, that's the page we want to show first. And let's add some more routes. So we have routes for Home, Cars, Drones, and Map. Now adding routes is something that all of our applications are going to need, so let's go ahead and put that in ApplicationBase so that it's a reusable method. And I'll paste in the code for addRoute. So we pass it the id, the pageObject, which right now is just null, we'll see how we populate this soon, and the defaultRoute will default to false. So the first thing we do is we take our titleBar and we add a link to it. We pass it the id as the name of the link, and we set all this up in the prior module on UI elements. Next we're going to keep a map of all our routes in a field called routeMap, let's create this. And right now we'll just set it to an empty object. So this will be a list of key values pairs. The key will be the id and the value will be the pageObject. So when we see that id, we'll know where to route, we'll route to this page. And if defaultRoute is set to true, let's just set this.defaultRoute equal to the proper route, an let's initialize that as well. And we'll initialize it just to null. So now I'll just save everything, and let's take a look at our application. We have our Home, Cars, Drones, and Map links. If we widen the application they should show on the title bar. There we go. So our routing is somewhat set up, we still can't show any pages, but in the next video we'll start to build our home page.
The Home Page
So we're building our Fleet Manager application and we have our routes and our links here, but none of them work yet. Let's create our home page. So the way we'll do this is we'll go to app.js and you can see that we added a route called Home, and we want to pass it our home page. And what is a home page? Well we'll be creating it, but it's going to be a new instance of HomePage and remember true means that this will be the default route, we want this to show as soon as we start our application. So let's go to addRoute, which is right here. So our pageObject is going to be our new home page and that will be set in this map right here, so when we see the id home, we'll show that page. Now before we do, let's actually create the page. I'm going to go to the src folder and I'll create the page here. Now you could create it in its own folder where you keep all your pages, but we're only going to be adding three or four pages, so I'll just add them under src. I'll name the file home-page.js. And I'll just paste in the code for this. So I pasted in HomePage, it extends BaseElement. In the next module we'll actually change this to extent a page class instead, but for now BaseElement should be fine. We'll add a simple constructor that we'll change in the next video, and if we look down we have our getElementString. We simply want to create a div and we'll style it so that whatever we place here is centered. Mainly I put that in to get our buttons centered when we add buttons. And looking up a bit, we can look at createElement. We'll call super.createElement to make sure it gets created properly, and then we'll add an image. This should be familiar from the prior module. Remember we're adding it to this.element, which will become this div right here. By calling super createElement, we'll guarantee that that element exists. So we'll add a full screen image and then we'll add our buttons. We'll create the styleString for the button and we'll create the new Button here, label it Self Driving Cars, we'll set the style string, and then we'll append it to this.element, that's how we build our page. And we don't want to handle clicks on it just yet, we want to make sure we have activateRoute set up, so this is commented out for now. And then we'll also add a second button for Drones. So our home screen consists of a large image and two buttons to go to the Self Driving Cars or to the Drones page. And we'll be creating those next in this module, but for now let's at least get this thing running without errors. One error we're going to have is we created HomePage and we accessed that in app.js, right here. So let's make sure we import that. Okay, now we're also passing this as the default route. We want it to show immediately, but how do we accomplish that? Well all the routing takes place in ApplicationBase, so let's go there. After we show the element, we do this once at startup, let's check and see if we have a defaultRoute, and if we do have a defaultRoute, let's route to it. So if this.defaultRoute is set to true, we'll call this.activateRoute passing it the defaultRoute. So now we just need this activateRoute method. So here's the code for activateRoute. We'll create a local variable called content and what we want to do is we want to look inside our titleBar and we want to find an element, we'll call the jQuery find method on it, that has a class of page-content and in the last module of this course, let's take a look at the titleBar that we created. This is the code we copied from the mdl website, but if we look down at the very bottom, this is our page-content right here, and they even put a big notice in here, your content goes here. And this is where we want to put our pages. So going back to application-base, we look for that class, page-content, and assign to content. Next we call content.empty, and empty is a jQuery function that will empty out all of the children. So this will clear the route essentially, and once we're sure that the route is cleared we can go ahead and look up our route in the routeMap, and we'll call appendToElement, content. So we find our content here, and then we'll append our home page directly to that content tag. So I'll save everything and let's see if this works. So we get TypeError, object doesn't support property or method setStyleString, so that's something I forgot to do. Let's go to the Button class and we created a new function called setStyleString. Let me add that in. So we create a new instance property called styleString and initialize it to empty space, and then when we create our Button tag, we set a style, and we use interpolation in order to get this.styleString placed in there, and the setStyleString method simply sets this.styleString equal to the style that we pass in. So let me save this and see what we get. And we get our home page. We have our full screen image and we have our two buttons, Self Driving Cars and Drones. If we expand it wider, we get the wider image and our buttons are side by side. So the home page looks good. Now we didn't set up the on click events for these buttons yet because we don't have the cars page and we don't have a drones page. We also don't have pages for Cars, Drones, and Map here. So in the next video, let's get our cars page working so that this button will work and we'll also be able to navigate to it from right here.
The Car Page
So we have our home page, now let's make our Cars page. I'm here in app.js and we'll go ahead and do exactly what we did for home page. We'll go to the Cars route and we'll add a new CarsPage and we'll make sure we import that, we'll create it in a second, and we'll create the page in the src folder, cars-page.js, and I'll paste in the code for this. Now one thing I decided to do was instead of extending the base element, we're going to extend a Page object and Page comes from framework/page.js. So let's quickly create this file, it's a very simple file. I'll go to framework and I'll add page.js. So I pasted in the code and Page basically extends BaseElement, so we still have access to the BaseElement in our pages, we're just going through this Page class now, and we can pass Page a title, a pageTitle, and we'll just store that locally. Now we're not going to do anything at all with the Page class in this course, but it is very important to have something like this because every page is going to have something in common, whether it's a footer or breadcrumbs or some kind of sidebar, all of that can be placed here in the Page class. But for now we'll just pass it a title and forget about it. You can use this as you develop your pages further. So I'll go back to app.js or actually I'll go back to cars-page.js, and we see that cars-page is extending Page and for super we'll calling Cars. Let's fix up our home page really quick. We'll also extend Page and for the name we'll pass Home for home page. And instead of importing BaseElement, we need to import Page, that's in framework/page.js. So now we can take a look at CarsPage for real. We have our constructor and if we look at getElementString, the page is also a div, just like the home page, and I just set a simple margin for a style. And we'll just print out of the word or the title Cars as an h3 tag. So when we create the element, we'll go ahead and call super.createElement, and then we'll add a DataTable with all our cars. We'll create a new DataTable, passing it the headers and we need to reach out to application.datService.cars. If you remember, application is our global application, so we need to import that. And we can see it here, import application from app.js. And then we append our data table to this.element, and that creates our car page. So the next thing we need to do is make sure that our links work. Right now they won't work. So let's go to application-base.js. And if we scroll down we have our show method. And here's where we can assign the click handler to all of our links in the titleBar. So let's take a look at this code. We're going to look in the titleBar and we're going to find an element, we'll call the find method on it, and we're going to find mdl-navigation__link. And this is a class that's used in our titleBar for all the links. And when we find them we can assign a click handler. Notice that we're using the fat arrow notation for ES6. If we used function it wouldn't work because if we used the function keyword, this would be set to the actual button or link, but by using the fat arrow we can guarantee that this will be set to the instance of this class. So we look for the route and we find it by looking at event.target.innerHTML, that's the name of the route that shows up in the link, and then we call this.activateRoute, passing it the route. And activateRoute was added earlier. This is where we find the content within our titleBar and we look it up in our routeMap table. So whenever we click on the titleBar link, this code will execute. We'll activate the proper route. Now there's one thing left to do. We need to go to our title-bar.js file and we need to take out this href right here. We want to take over control of these links. We don't want the browser to link for us because we're using the on click method. So I'll just take out this href here. So now we have a simple anchor tag and our code will set up the click event. I'll save it and one last thing we need to do, I just found this error, is we need to trim our string for the link. If we go to title-bar.js, you can see here that there could be space before or after the link.title. So when we access this to route to it, we just need to trim that string. So I'll go to application-base.js and here's where we grab our inner HTML. So let's just call route.trim. So I'll save this, we'll execute it, and I'll click on Cars. And now we get our Cars table. If I go back to the menu and click on Home, we get our home page. So this is working well, I'll make it wider and let's see if our cars link works on the top. That's fine. And home works as well. Now we still don't have our buttons working, so let's fix that. I'll go back to the home page and I'll uncomment out these lines that I commented before. So now the click handler, we'll use the fat arrow function, and we have one line of code. We'll call application.activateRoute Cars, and this is for the self driving car button. And we'll do the same for drones. We'll call application.activateRoute Drones. Now we don't have Drones set up yet, we only have Cars, but let's see if the Car button works. I'll save it, I'll scroll down and click Self Driving Cars, that works fine now. So our application is starting to come together. The Drones link will be very similar to Cars and I'll leave that up to you as an exercise to implement, but in the next video what we'll do is we'll create our Map page.
The Map Page
So our app is almost complete, we have the Home page, the Cars page, and the Drone page, that's up to you as an exercise if you'd like, and let's now implement the Map page. We'll create a new, we'll call it MapPage and we'll import it before we forget, and then we'll create map-page.js. And I'll paste in the code for this. So MapPage extends Page. We'll set the title to Map and for our getElementString, again, we have a div setting a margin of 20 pixels and the title will be Map. And as we saw in the last module, it's very easy to create a Google Map. We already have a UI component for it. So we'll set the center of the map, we'll set it to a latitude and longitude for Central Park, that's where we know where our cars are based, and I'll create a new Google Map passing it the centerOfMap and I'll grab the application.dataService.cars. This is our array of cars. Or you could also pass an array that contains both the Cars and Drones. You can create that if you'd like. And we want to make sure we take our map and append it to this.Element. So our routing is already all set up, we don't even have to touch that now. Let me save this and let's take a look at the app. I'll click on Cars, that works, and I'll click on Map, and we get our map of cars. Let's also make sure we can access it from the side menu, and that works fine. So that's our application. We created our application solely out of ES6 classes. We did use the jQuery library in Google Maps, but for the most part everything we've built is based on an ES6 class and thus a module. If you were going to build an advanced website, you'd probably want to use a framework such as Angular, but for learning about ES6 classes, this is a great demo project. It shows you what you can accomplish with them.
Summary and Course Wrap-up
In this module we started out by creating an Application class and this is yet another use for a class in ES6 and when it comes to JavaScript frameworks, several of the major ones do use a single class for an entire application. Next we took a look at routing to different menu items. We want to be able to route to different pages and we saw how the Material Design Lite title bar let us work with that. We created a home page and we created a car page and finally we created the map page, which uses the Google Map class that we created in the last course module. And that brings this course to a close. Thanks for watching. Hopefully now you're able to use classes in several different ways, whether they're simple classes for business objects, virtual classes that we wouldn't instantiate, but rather we would extend them, whether it's a Data Service class to hold all your business objects, or classes for front ends, such as classes to hold UI elements or your entire application. If you'd like to learn more about ES6 and some of the more intermediate to advanced features, you can check out my prior course, Rapid ES6 Training. This won't walk through projects the way this course did, but it will rapidly go through most of the new features of ES6.
Course author
Mark Zamoyta
Mark started in the developer world over 25 years ago. He began his career with a Bachelor of Science in Computer Science from St. Johns University. After spending 10 years on Wall Street working...
Course info
LevelBeginner
Rating
(162)
My rating
Duration2h 27m
Released13 Jun 2016
Share course