What do you want to learn?
Skip to main content
by Mark Zamoyta
Resume CourseBookmarkAdd to Channel
Table of contents
Why Use ES6 Classes and Modules?
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
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.
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.
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.
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
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
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.
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.
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.
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.
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
Creating a Data Service Class
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.
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.
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.
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.
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
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
Setting up Material Design Lite
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
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
A Google Map Class
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
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.
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
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...
Released13 Jun 2016