AngularJS – 9bit Studios https://9bitstudios.com Web Design, Themes, & Applications Sun, 13 Aug 2017 14:41:43 +0000 en-US hourly 1 https://wordpress.org/?v=4.8.2 Develop Apache Cordova Applications with HTML5 & JavaScript https://9bitstudios.com/2015/12/develop-apache-cordova-applications-with-html5-javascript/ https://9bitstudios.com/2015/12/develop-apache-cordova-applications-with-html5-javascript/#respond Sat, 19 Dec 2015 22:59:14 +0000 https://9bitstudios.com/?p=1124 In a prior discussion we looked at how to set up a project so that we could develop an Apache Cordova application using HTML5 and JavaScript. In that section we mostly covered how to set up, build, and run the project — which consisted of the same application in the www folder that Apache Crodova bootstraps when you create a new project. In what follows we will look at the approach for actually writing own code for our app and will look at how an app in Apache Cordova gets initialized. We will also look at how we can extend the Apache Cordova platform by using plugins to give ourselves additional features and functionality that will make for an all around better user-experience (UX) for the users of our app.

Now that we have our project set up and all our platforms added all that we have left to do now is create our application by creating what basically amounts to a website that runs HTML and JavaScript in the “www” folder. How should one develop for Apache Cordova? Personally, I would delete all of the boilerplate files and folders and start from scratch. That is what we will do here. Just take a quick note of how things are referenced in the index.html file and do the same for your own files.

In doing this, I have modified the index.html file in the “www” folder to the following…

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
        <meta name="format-detection" content="telephone=no">
        <meta name="msapplication-tap-highlight" content="no">
        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
        <link rel="stylesheet" type="text/css" href="css/index.css">
        <title>Cordova Application</title>
    </head>
    <body>
        
        <div id="main">Initializing...</div>
        
        <script type="text/javascript" src="cordova.js"></script>
        <script type="text/javascript" src="js/index.js"></script>
    </body>
</html>

Note: Do not worry about that “cordova.js” reference or the fact that this file is nowhere to be found in our “www” folder. Apache Cordova will utilize this in the proper context when the app runs (so leave it in there).

So as far as the index.html goes, there is nothing too fancy. I have a css file (index.css) and a js file (index.js). That is all you need to get started.

Next, let’s look at our JavaScript in the index.js file. There is really only one thing you need to make note of when you develop Apache Cordova applications in JavaScript. There is a special event that Apache Cordova uses to tell the platform that the everything is loaded and the device you are using is ready to start running JavaScript in your Cordova application. This is what is known as the “deviceready” event. It only fires when you are running an app within a Cordova context (i.e. on a device or an emulator of a device). In a lean version of JavaScript for a cordova application would look like the following (in index.js)…

var app = {
    init: function() {
        document.addEventListener('deviceready', this.main, false);
    },
    main: function() {
        document.getElementById('main').innerText = 'App is ready...'
    }
};

app.init();

Here we are calling the “init” function which will add a listener for the “deviceready” event. When this event fires the “main” function will run, which in this case just changes the inner text of a div element So if we run this using

$ cordova emulate android

or

$ cordova emulate ios

from the root of our project we will see our device boot up, our app launch, and if all goes well we will see the text “App is ready…” so we know that our event is firing.

But it gets kind of annoying to have to fire up the emulator every time we want to test something out or make small changes in doing development. So it would be easier to be able to open the index.html file in the browser. But the problem with this is that the browser does not natively support the “deviceready” event. If you try to open index.html in a browser you see that the text remains as “Initializing…” The “deviceready” event does not get fired by the browser. So to get around this we have to set a flag so that we conditionally check if we are trying to run our app in a browser. So update the code in index.js to the following…

var config = {
    DEV: true    
};

var app = {
    init: function() {
        if(config.DEV) {
            this.main();
        } else {
            document.addEventListener('deviceready', this.main, false);
        }
    },
    main: function() {
        document.getElementById('main').innerText = 'App is ready...'
    }
};

app.init();

Now that we have set a flag in config to check whether we are just intending to run in “dev mode” in the browser, we will just call the main function directly and that will be the place that we kick off our application. We essentially bypass the “deviceready” step.

Now if you run this in the browser, we see that the “App is ready…” text is rendered to the page. If we change the config.DEV flag to “false” and run

$ cordova emulate android

or

$ cordova emulate ios

we can see that our main function is still running to kick off our app.

Even though the “deviceready” event is kind of the official Apache Cordova way of making sure that everything is initialized, if you forget to change the “DEV” flag to “false” and directly call the “main” function while running on a device, things will probably still work.

Splash Screens, Icons, & Using Plugins

Apache Cordova gives you a great framework out of the box to create a mobile app that can run on different platforms (as we have seen). But as great as it is, often there are things that you run into that may not be as easy to implement as they would be if you were creating an app using the native tools and languages. Fortunately, if you run into something that is not natively supported in Apache Cordova, there is a vibrant plugin ecosystem (and community behind it) that has worked to extend the support and functionality of the framework. You may have noted that there was a “plugins” folder in our Apache Cordova project. This is where installed plugins go.

Let’s take a look at a plugin that allows us to display a “splash screen” while our app launches. This is a common feature that you may have seen in many apps. We will use the cordova-plugin-splashscreen plugin. This plugin will allow us to specify paths to images that we will use as our splash screens. Because there are so many different resolutions across iOS and Android phones and tablets, we are going to have to specify a number of different resolutions of images that we will use for our splash screens. It will also allow you to show the Rather than list out what each of these resolutions need to be I have included a sample Apache Cordova project here as a download with some blank images of the sizes that they need to be to support this feature on most (nearly all) modern devices (iPhones, iPads, Android phones, and Android tablets). You can find these in the “res” folder in the “www” directory.

This cordova-plugin-splashscreen plugin will also allow us to specify the images that will be used for the icon of our app that shows up on the user’s home screen when the app gets installed. Up until now, Apache Cordova has just been using its own default image (that little robot looking thingy). But with this plugin we can change the icon, to what we want it to be! Awesome!

To install our plugin we just need to run the following from the root of our Apache Cordova project…

$ cordova plugin add cordova-plugin-splashscreen

If for some reason that does not work for you, you can try running the following

$ cordova plugin add https://github.com/apache/cordova-plugin-splashscreen.git

Once the plugin is installed we can now specify the paths to our splash screens and icons. We will do this in the config.xml that resides in the root of our project. As mentioned prior I have posted a sample Apache Cordova project that is available for download. In that project we can see the config.xml file that looks like the following…

<?xml version='1.0' encoding='utf-8'?>
<widget id="com.sample.sampleapp" version="0.0.1" xmlns="https://w3.org/ns/widgets" xmlns:cdv="https://cordova.apache.org/ns/1.0">
    <name>SampleApp</name>
    <description>
        A sample Apache Cordova application...
    </description>
    <author email="dev@cordova.apache.org" href="https://cordova.io">
        Apache Cordova Team
    </author>
    <content src="index.html" />
    <plugin name="cordova-plugin-whitelist" spec="1" />
    <access origin="*" />
    <allow-intent href="https://*/*" />
    <allow-intent href="https://*/*" />
    <allow-intent href="tel:*" />
    <allow-intent href="sms:*" />
    <allow-intent href="mailto:*" />
    <allow-intent href="geo:*" />
    <platform name="android">
        <allow-intent href="market:*" />
        <!-- Android Icons -->
        <icon src="www/res/android/icons/ldpi.png" density="ldpi" />
        <icon src="www/res/android/icons/mdpi.png" density="mdpi" />
        <icon src="www/res/android/icons/hdpi.png" density="hdpi" />
        <icon src="www/res/android/icons/xhdpi.png" density="xhdpi" />
        
        <!-- Android Splash Screens -->
        <splash src="www/res/android/screen/splash-land-hdpi.png" density="land-hdpi"/>
        <splash src="www/res/android/screen/splash-land-ldpi.png" density="land-ldpi"/>
        <splash src="www/res/android/screen/splash-land-mdpi.png" density="land-mdpi"/>
        <splash src="www/res/android/screen/splash-land-xhdpi.png" density="land-xhdpi"/>
        <splash src="www/res/android/screen/splash-port-hdpi.png" density="port-hdpi"/>
        <splash src="www/res/android/screen/splash-port-ldpi.png" density="port-ldpi"/>
        <splash src="www/res/android/screen/splash-port-mdpi.png" density="port-mdpi"/>
        <splash src="www/res/android/screen/splash-port-xhdpi.png" density="port-xhdpi"/>   		
    </platform>
    <platform name="ios">
        <allow-intent href="itms:*" />
        <allow-intent href="itms-apps:*" />
        <!-- iOS Icons -->
        <icon src="www/res/ios/icons/icon-60@3x.png" width="180" height="180" />
        <icon src="www/res/ios/icons/icon-57.png" width="57" height="57" />
        <icon src="www/res/ios/icons/icon-57@2x.png" width="114" height="114" />
        <icon src="www/res/ios/icons/icon-60.png" width="60" height="60" />
        <icon src="www/res/ios/icons/icon-60@2x.png" width="120" height="120" />
        <icon src="www/res/ios/icons/icon-76.png" width="76" height="76" />
        <icon src="www/res/ios/icons/icon-76@2x.png" width="152" height="152" />
        <icon src="www/res/ios/icons/icon-40.png" width="40" height="40" />
        <icon src="www/res/ios/icons/icon-40@2x.png" width="80" height="80" />
        <icon src="www/res/ios/icons/icon.png" width="57" height="57" />
        <icon src="www/res/ios/icons/icon@2x.png" width="114" height="114" />
        <icon src="www/res/ios/icons/icon-72.png" width="72" height="72" />
        <icon src="www/res/ios/icons/icon-72@2x.png" width="144" height="144" />
        <icon src="www/res/ios/icons/icon-small.png" width="29" height="29" />
        <icon src="www/res/ios/icons/icon-small@2x.png" width="58" height="58" />
        <icon src="www/res/ios/icons/icon-50.png" width="50" height="50" />
        <icon src="www/res/ios/icons/icon-50@2x.png" width="100" height="100" />
        
        <!-- iOS Splash Screens -->
        <splash src="www/res/ios/screen/Default~iphone.png" width="320" height="480"/>
        <splash src="www/res/ios/screen/Default@2x~iphone.png" width="640" height="960"/>
        <splash src="www/res/ios/screen/Default-Portrait~ipad.png" width="768" height="1024"/>
        <splash src="www/res/ios/screen/Default-Portrait@2x~ipad.png" width="1536" height="2048"/>
        <splash src="www/res/ios/screen/Default-Landscape~ipad.png" width="1024" height="768"/>
        <splash src="www/res/ios/screen/Default-Landscape@2x~ipad.png" width="2048" height="1536"/>
        <splash src="www/res/ios/screen/Default-568h@2x~iphone.png" width="640" height="1136"/>
        <splash src="www/res/ios/screen/Default-667h.png" width="750" height="1334"/>
        <splash src="www/res/ios/screen/Default-736h.png" width="1242" height="2208"/>
        <splash src="www/res/ios/screen/Default-Landscape-736h.png" width="2208" height="1242"/>  		
		
    </platform>
    <preference name="SplashScreen" value="screen" />
    <preference name="SplashScreenDelay" value="4000" />
</widget>

As we can see, we have specified paths to a number of different images of different resolutions. with these settings the cordova-plugin-splashscreen Apache Cordova plugin will automatically figure out which is the best resolution to use depending on the

We can also see that in the <preference> elements down at the bottom we have specified “SplashScreenDelay” preference down at the bottom of the config.xml file. This specifies to show the splash screen for 4 seconds (4000 milliseconds). You can change this value to the one that you prefer.

Now if you run your application with

$ cordova emulate android

or

$ cordova emulate ios

You will see a splash screen displayed before the app launches. It’s blank for now until you bust out your graphic designer skills and create a nice looking one. If you click on the “home” button on the emulator device you can see on the home screen that the icon we have in our project is now used as the app icon. Like with the splash screen, obviously for a production app we would want to make nice looking icons consistent with the theme of our app. But the functionality is there and all you have to do is replace the icons and splash screens in the sample project with your own app branding.

So sample project and try it out. Note: After downloading the project you will need to 1) add the platform(s) that you want to work with with $ cordova platform add and 2) install the splash screen plugin with $ cordova plugin add cordova-plugin-splashscreen.

Summary

Now that we have walked through how to set up some app initialization code from here, everything that you want to do is entirely up to you. There are really no limitations on what you can do here. Apart from the unique “deviceready” event there is nothing special that you need to do to code for Apache Cordova (and even hooking into the “deviceready” event probably isn’t even absolutely essential). You can just create things in the same way that you would if you were going to put this web application on the web and have users run it in their browsers. There is no special magic needed. You can add more CSS files, images, other JavaScript files, or whatever else you want to and start building. You can use popular front-end frameworks like Bootstrap or Foundation. Or, if you prefer, you can import a framework specifically designed for mobile such as jQuery Mobile. You can use popular JavaScript development frameworks like Backbone.js, Ember.js, AngularJS, or React. Ultimately the choice is entirely up to you. I cannot choose for you.

From there, the sky’s the limit. You can do whatever you like to create whatever kind of app you want. But since there are so many different types of applications for so many different purposes, it is ultimately up to you to decide on what technologies you want to use and what is the best way to implement your app. So get out there and develop it up. Until next time, cheers and happy developing!

]]>
https://9bitstudios.com/2015/12/develop-apache-cordova-applications-with-html5-javascript/feed/ 0
Create Mobile Applications for iOS and Android with HTML5, JavaScript, and Apache Cordova https://9bitstudios.com/2015/12/create-mobile-applications-for-ios-and-android-with-html5-javascript-and-apache-cordova/ https://9bitstudios.com/2015/12/create-mobile-applications-for-ios-and-android-with-html5-javascript-and-apache-cordova/#respond Sat, 12 Dec 2015 22:49:53 +0000 https://9bitstudios.com/?p=1101 Obviously at the time of this writing in the 2nd decade of the 21st century, you are no doubt well acquainted with the immense proliferation and impact that mobile apps have had in and on our world. It was not long ago that we were all sitting at our computers at our desks for the large majority of the time we spent in our various avenues of technological communications over the Internet — complete with our with our giant monitors, whirring computer fans, buzzing drives, and the garbled noise of dial-up modems. Now, mobile phones and tablets probably make up as much, if not more, of our time spent working, playing, and communicating online. Everyone has a ~$100 to $600 smartphone (i.e. computer) in their pocket. And with this rise of mobile, apps found on the popular stores — like Apple’s iOS App Store, Google Play (Android), Amazon and a host of others — have completely transformed the way that we do anything and everything because well, there is an app for anything and everything. Every single facet of life probably has an app to manage some aspect of it. Need to remember to breathe? There is an app for that! Yeah, that is a joke (probably)… but there are plenty of “real” apps out there (e.g. iBeer) that are just as pointless. Fun and goofy, yes, but pointless. And they all post their pointlessness to Facebook and/or Twitter.

Whether you want to develop an app to manage some minute aspect of life or just create a fun game, or whatever else, you can download the SDK for the platform(s) that you want to release your app on, do some tutorials, and start building. That’s all well and good, but if you want to port your application to another platform (e.g. release an iOS app also for Android), you will essentially have to try rebuild the same app using the languages in tools of a different platform. For example, iOS apps for iPhone and iPad have traditionally been developed on a Mac in the Xcode IDE using the Objective-C programming language (though Apple has recently released a new language called Swift that can also be used to build iOS apps). Android apps, by contrast are built using Java and Eclipse or the more recently released Android Studio. Native Windows Phone apps are built using C# and XAML in Visual Studio. Other platforms have their own setups. If you want to take this approach you definitely can, but it should be fairly apparent that developing and maintaining a number of different codebases for multiple platforms quickly adds up to a lot of time, effort, and often money as the sizes and complexity of your apps increase.

At the same time as the rise of mobile in the past decade, something else has occurred: the rise of HTML5 and JavaScript web applications. Beginning at around 2009 – 2010 one of the more noticeable developments was the rise of websites and web applications making use of more open technologies such as HTML5 and JavaScript to bring interactivity and 2-way communication between clients (usually browsers) and servers. With Flash on the way out due its proprietary nature, resource hogging, security issues, poor SEO — along with its being snubbed by Apple for support on iPhones and iPads for basically all these reasons — meant that something had to fill the void. HTML5 and JavaScript moved into this space and developers started turning to it for creating interactive “app-like” experiences.

Wouldn’t it be awesome if there were a way to write a single application in HTML and JavaScript with a single codebase and have a way to port this application to all of the different mobile platforms? I am so glad you asked.

Apache Cordova — also commonly known as “PhoneGap” (the only difference between the two is propriety) — allows us combine combine these open technologies together with the SDKs of the various mobile application platforms to make native mobile applications using technologies such as HTML and JavaScript. You can basically create a small static HTML and JavaScript website or web application and this code will be imported into native iOS, Android, Windows Phone projects and configured specifically for the respective platforms. Thus, you *do* still have to have the SDK of the platform installed to build, run, and release the apps. So to create an iOS app you will still need a Mac and Xcode. To create Android apps you will need to install the Android SDK (which comes with the Android Studio IDE). If you don’t want to mess around with Android Studio, you can just try downloading the old standalone SDK tools here and updating from there. But the important aspect of using Cordova is that the HTML and JavaScript that each project will pull in and build will be the same across all platforms. Want to release an update with some slick new features or fix some bugs that have been reported by your users? You need only change your code in your HTML and JavaScript. You don’t have to try and rewrite the same code and/or functionality in a different language on each platform. From there all you need to do is run a few simple commands from the command lines and your HTML and JavaScript will get built and converted into an application that can run on the platform(s) you are targeting.

In what follows we will explore what it looks like to use Cordova to create a mobile application in HTML and JavaScript that can run on a number of different platform. For our examples, we will build our app for iOS and Android, but the process is essentially the same for building for other platforms (e.g. Microsoft Windows Phone, Amazon Kindle Fire). You would just need to download the SDKs for those platforms if you wanted to run your app on those types of devices as well.

So let’s get to it…

Installing Node.js

The first thing that we will need to do before we do anything else is install Node.js. Apache Cordova runs on Node.js so it is a prerequisite. If you’re not entirely familiar with Node.js there is a primer on Node.js here. It has become a huge part of JavaScript development in the last half-decade and is only becoming more and more prominent.

To install Node.js all you have to do is head on over to their website and follow the directions for your operating system (Windows, Mac, or Linux).

Installing Apache Cordova

Once we have Node.js installed, next we will need to install Apache Cordova. We just do this over npm. npm comes with the Node.js installation, so you already have it on your machine from installing Node.

To install Cordova open up a command window or terminal and run the following…

$ npm install -g cordova

In case you are not familiar with it, the “-g” flag means “global.” You will want to do this so that you can run Cordova commands from any directory on your system.

Creating an Apache Cordova Project

Now you can go to a directory where you want to create your Cordova project and open a command window/terminal. We will be initializing a new Cordova application here with the Cordova “create” command. For more information on the configuration options you can choose when installing and setting up your project, please see the Apache Cordova documentation here. Run the following command from your command line…

$ cordova create ninebit com.ninebitstudios.ninebitapp NinebitApp

The command above will create a vanilla Cordova project called “NinebitApp” and ocnfiguring it with a unique ID that uses reverse domain name notation in a directory named “ninebit.” What you will want to do is change these values to what you want for your own application.

$ cordova create   

For example…

$ cordova create yourdirectory com.yourdomain.yourapp YourAppName

but just plug your own values that you want in there.

You should see the directory with the name you specified created with with a number of files and directories in there like “platforms,” “plugins,” “www”, and a “config.xml” file. The config.xml file will have some of the values that you specified in your mobile. The actual location where you will write your HTML and JavaScript is in the “www” directory. If you explore that directory and open the index.html file you will see a boilerplate web application which basically just displays an image on the page. Even though it does not do much, this is an app that can run on any of the platforms we want to specify. We will add these platforms next.

Adding Support for Android Devices to the Project

Now that we have our project created we need to “add” the platforms that we want to release on to our project. Open a command window in the root directory of the project (the one with the config.xml file).

To add Android to our project run the following…

$ cordova platform add android

You should see a directory called “android” in the platforms folder. This folder contains a native Android project that is preconfigured to take the HTML and JavaScript in the “www” folder and run it in an Android device. If you opened Android Studio and clicked on “Create New Android Project” you would see almost the same files and folders in the project that gets created when creating a native Android application. There will be few extra files and folders in the Cordova Android project specific to Apache Cordova.

Because we now have a native Android project, we can run it on the Android emulator. In the root directory we can now run following…

$ cordova emulate android

This should launch the Android emulator and automatically run your application after it boots up. Note that you might need to set up a device for Android project to use before you try to run the command above. To do this, you will need to launch the Android Virtual Device Manager (AVD Manager.exe). This application resides in the root folder of where you installed your Android Studio SDK. You can also launch AVD Manager in Android Studio under the “Tools” menu under “Android.” However you open this application, once you are in there just click “Create New” and pick whatever device you want. Once you get the device set up you can try to run the command above again.

Adding Support for iOS Devices to the Project

Similarly, if you want to add support for iOS

$ cordova platform add ios

As was the case with Android above, you should see an “ios” directory. This folder contains a native Xcode project that is preconfigured to take the HTML and JavaScript in the “www” folder and run it in an iOS device.

Remember that you *must* have the SDK of the platform you are trying to add installed on your system. If you do not the Cordova console should inform you of this error.

For iOS you will want to make sure that you have the proper folder permissions in all of the directories and folders you will be working in. I had some issues trying to emulate when running all the cordova commands with sudo. So make sure on your Cordova project directory you do something like the following…

$ sudo chmod -R 777 foldername

so that you can run all the commands without using sudo

The first thing that you’ll want to do is add the iOS platform to your project.

$ cordova platform add ios

You can then build for iOS by running the following command. If you are able to do so without errors, you’ll know that everything is all good.

$ cordova build ios

Note in /platforms/ios/cordova/lib there is a list-emulator-images tool that you can run to list out all of the emulator images that you have installed. These are the different devices that you can emulate your apps…

$ ./platforms/ios/cordova/lib/list-emulator-images 

You will see a list like the following…

  • iPhone-4s
  • iPhone-5
  • iPhone-5s
  • iPhone-6-Plus
  • iPhone-6
  • iPad-2
  • iPad-Retina
  • iPad-Air
  • Resizable-iPhone
  • Resizable-iPad

If you want to specify a target, you can run the simulator on a specific device by targeting a specific device. For example…

cordova emulate ios --target="iPhone-4s"
cordova emulate ios --target="iPad-Air"
cordova emulate ios --target="iPhone-6"
cordova emulate ios --target="iPhone-6-Plus"

Otherwise, the iOS development tools will just use what is set as the default device (usually the latest version of the iPhone that is currently in production).

And of course, if you want to emulate on an iOS device in Xcode you can run the following command in your project root…

$ cordova emulate iOS

This should launch the iOS simulator and run your application.

Summary

In the above discussion we looked at all of the steps, tools, and environments necessary to create an Apache Cordova project and get things set up such that you can begin developing cross-platform mobile applications. In a future discussions we will take a deeper look at how write the code for out apps as well as how to package everything in your application up once you are finished developing and submit it to the respective stores for iOS (App Store) and Android (Google Play). But those are other discussions for some other days. So until next time, happy mobile developing!

]]>
https://9bitstudios.com/2015/12/create-mobile-applications-for-ios-and-android-with-html5-javascript-and-apache-cordova/feed/ 0
An Angular 2 Primer https://9bitstudios.com/2015/09/an-angular-2-primer/ https://9bitstudios.com/2015/09/an-angular-2-primer/#respond Tue, 08 Sep 2015 13:44:30 +0000 https://9bitstudios.com/?p=1069 Angular 2 is the much anticipated latest release of the Angular platform maintained by a team of engineers over at Google. The first iteration of Angular (which we may refer to as Angular 1 or 1.X at times) was and has been very well received within the JavaScript community. Many saw it as an improvement on smaller single-page application libraries like Backbone.js and Knockout.js because it essentially did “more” for you. You got a lot of functionality out of the box with the result being that you had to write less boilerplate code than you would with using a more basic library. Directives allow you to define custom attributes for any element that you want and Angular also boasts other great features such as native data-binding, an AMD like module loading system, and services using a solid promise implementation for asynchronous data transfer. All of this and more comes pre-packaged within Angular and is coupled with a great community behind it, an abundance of resources, as well as great support from the team over at Google. Because of all this, it is not too surprising that many companies and organizations have turned to it as a solution for building their web and mobile applications.

If there were a few points of criticism of Angular as a framework, it usually surrounded a slightly steeper learning curve when compared to other libraries, and the observation that perhaps there was a little “too much magic” going on under the hood without the developer really fully understanding what is happening 100% in all circumstances. This is always a double-edged sword with a framework that gives you a lot of functionality out of the box. On one hand, it is great to have a system where writing a relatively small amount of code will give you a lot of dynamic features and functionality with minimal effort. On the other hand, this sometimes requires that you follow a very defined and specific pattern in the way that you structure your code and if you ever want to do something that is perhaps a bit unorthodox from the accepted way, it might not be so easy and a framework that is a bit more rigid when it comes to custom implementations not always desired — especially when those hard-to-track-down bugs surface at inopportune times. Like with so many things, there will always be pros and cons to any technology you encounter.

Nonetheless, the reception and response to Angular 1 was/is overall very positive. It was often paired with Node.js development using the web application framework Express.js and the NoSQL database MongoDB to create what was known as the MEAN stack (MongoDB, Express, Angular, Node.js). Many readers may be familiar with this and other technology paring acronyms (such as LAMP: Linux, Apache, MySQL, and PHP or Python). When certain groupings of technologies complement each other well and play together nicely, it always makes for a better development experience all around.

Angular 2 Announcement and Pushback

In the latter part of 2014 Google announced that they were developing Angular 2. Usually these sorts of announcements are met with are met with a lot of excitement. After all, who wouldn’t like a shiny new version of something that you already like. However there was one overarching dark cloud the loomed over the Angular 2 announcement that was met with a lot of frustration within the web development community: Angular 2 would be essentially a rewrite of the codebase and drastically different enough from Angular 1.X such that there would be no foreseeable upgrade path to move from Angular 1.X to Angular 2. This meant that existing applications in Angular 1.X would essentially have to be rewritten. On top of this, it was not immediately clear how long Angular 1.X would continue to be supported with new releases and bug fixes.

Naturally, of course, some developers were upset by this. Only time will tell what the full impact of this decision and approach will be. Nonetheless, we move forward towards the future regardless of how much of the past comes along with it. That’s what we are aiming to do in the following discussions and tutorials.

NOTE: At the time of this writing it is still not entirely clear whether any of this will change and the Angular team will put an upgrade path in place. Also note, at the time of this writing in mid-2015, Angular 2 is still in “developer preview.” This means that many of the concepts and code samples presented in this book are subject to change. I will do my best to keep up to date on the latest changes as they roll out, but please note that there might be some sensitivity involved with functionality between different versions of Angular 2 and its surrounding tools. If you encounter anything in this regard, please do not hesitate to contact me via the above channels.

Introduction to Angular 2

Angular 2 incorporates many modern features of the latest iterations of JavaScript and tools within the JavaScript community being released circa halfway through the second decade of the 21st century (2010 – 2020). This includes many syntactical and functional features of ECMAScript 6 — the latest iteration of JavaScript up from ECMAScript 5, Node.js based dependency management with npm , TypeScript — the type checking JavaScript utility syntax that has been built and maintained by Microsoft, and a number of others. In short, the fingerprints of many of the newest emerging technologies within the web development world during this window of time are present on the Angular 2 framework. Similar to those that have come before, each release of Angular 2 will attempt to be a more malleable and useful option for developers looking to solve the common problems that they all face.

So let’s look all of these pieces that are essential for developing an Angular 2 application…

Node.js

Angular 2 uses Node.js and more specifically npm for installation of the various dependencies it requires. Having been first published by Ryan Dahl in 2009, Node.js — the open source software that allows the common web language of JavaScript to run outside the browser — has absolutely grabbed ahold of imaginations in the world of technology in its first half-decade of life. The explosion of Node.js applications and the exponentially increasing number of published modules of npm, the repository where Node.js modules are stored, are a huge indicator of its success as a platform.

So be sure head over to the Node.js website and install it for your OS. You will definitely be needing it as we go along.

ECMAScript 6

Angular 2 uses many implementations found in ECMAScript 6, or ES6 for short. It would be beneficial to take a brief look at some of the new features found in this latest version of JavaScript up from ES5. For example, ECMAScript 6 implements a more syntactical conceptualization of classes in JavaScript. While JavaScript classes are not and never have been classes in the strictest technical sense as they are in the traditional object-oriented languages Java, C++, and C#, many JavaScript applications can still be written in a way that incorporates a very class-like implementation using functions, constructors and prototypal inheritance. For example, you will often see something the following in a JavaScript application (ES5):

// ES5
function Animal() {
    this.color = 'brown';
    this.size = 'medium';
    this.age = 1;
}
Animal.prototype.sound = function() {
    console.log('Grunt');
};
Animal.prototype.move = function() {
    console.log('Running');
};

var beast = new Animal();

Inheritance can even be implemented in this approach…

function Dog() {
    Animal.call(this);
}
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog

var fido = new Dog();
fido.color // brown;

By calling the Animal function and passing in the Dog function as the context, and new instance of a Dog will get all of the properties and methods found on Animal. We then do a little bit of additional cleanup with some of the prototype to make sure that the pointers are pointing at the right place.

This is all fine, but it seems a little bit convoluted. Fortunately for us, ES6 implements these same objects as follows using the “class” key word and the constructor function. What is shown below is exactly the same as the above.

// ES6 Class
class Animal {
 
    constructor(){
        this.color = 'brown';
        this.size = 'medium';
        this.age = 1;
    }
 
    sound() {
        console.log('Grunt');
    }
    move() {
        console.log('Running');
    }
}

We can implement inheritance using the “extends” keyword. We override some of the default properties that we found on Animal.

class Dog extends Animal {    
    constructor(color, size, age) {
        this.color = color;
        this.size = size;
        this.age = age;
    }
 
    sound() {
        console.log('Woof');
    }
}
 
var d = new Dog('Black', 'medium', 5);
console.log(d.size); // medium
d.sound(); // Woof
d.move(); // Running

We can even go further than this and extend the Dog class. The implementation below makes use of the “super” keyword to call the constructor of the parent object…

class Pomeranian extends Dog{    
    constructor(color, size, age) {
        super(color, size, age)
    }
 
    sound() {
        console.log('Yap');
    }
}
 
var p = new Pomeranian('Brown', 'small', 5);
p.sound(); // Yap
p.move(); // Running

Another component of ES6 that you will find prevalent in Angular 2 is module loading. Modules in ES6 solve the problem of scope and dependency management. In the past with ES5 a variable or function just declared in the open — whether in a script block in an HTML file or in an external .js file — like so….

function sayHi(name) {
    return "Hi, " + name;
}

would be considered to be bad practice because you were polluting the global object (which is often the window object in the context of the web). As the size and complexity of your application grew the maintainability of it would become a lot more difficult because you would have to worry about potentially overwriting other variables and functions later on in your application if you happened to name them the same or accidentally assign them a value that you did not intend to. Because of this, you often saw applications “namespace” the various methods and variables of an application to objects like so…

var app = app || {};
app.greeting = {
    sayHi: function(name) {
        return "Hi, " + name;
    }
}; 

to avoid polluting the global object.

With ES6 modules, however, all of this becomes unnecessary. You can declare functions and variables freely and the scope is always local to that file unless explicitly specified as a global object. This makes managing scope and maintaining your application a lot easier.

If you have ever done any development with Node.js, you should be somewhat familiar with modules as it follows a particular form and structure known as the CommonJS module loading syntax. Basically the idea is that you can import various functions and methods from other JavaScript files into your own JavaScript file by using the “import” keyword. You reference the path to the file you are loading as a module from the location of the file you are importing your module(s) into. So let’s say in your project you had a file called “greeting.js” that lived in a folder named “lib” with the following code in it

export function sayHi(name) {
    return "Hi, " + name;
}
export function sayHello(name) {
    return "Hello, " + name;
}
export function sayHey(name) {
    return "Hey, " + name;
}

The “exports” keyword means that these functions are exported and can be imported into other JavaScript files. For example, in the root of your project (one level up from the “lib” directory) let’s say you had a file called main.js. In that file you could do the following…

import * as greet from 'lib/greeting';

console.log(greet.sayHi('Joe')); // "Hi, Joe"
console.log(greet.sayHello('Sally')); // "Hello, Sally"

Here we are importing all of the exported functions in the greeting.js file into a variable called “greet” using the * wildcard locally to our main.js file. If there were any other exported functions or properties in our greeting.js file, this would be available in main.js as well.

There are other syntaxes that can be utilized in module importing. For example, if we only wanted the sayHi and sayHello methods imported from our greeting.js file (and we did not want or need the sayHey method for some reason) we could do the following in main.js…

import { sayHi, sayHello } from 'lib/greeting';

console.log(sayHi('Joe')); // "Hi, Joe"
console.log(sayHello('Sally')); // "Hello, Sally"

There are other variations on the importing syntax and implementation as well that we might see as we go along. The main point is that this gives you a lot of flexibility in how you structure your application. Doing all of this is not too dissimilar from the importing that you would so in Java…

import com.domain.package

or C#

using System.Net;

The difference being that in JavaScript you import from the path to the file and in Java and C# the packages are compiled into memory and imported from there. But in all cases the approach serves the same purpose.

This is just a brief look at a couple of the newer features found in ES6. There are many other parts of ES6 that will be incorporated into Angular 2 we will discuss these as we go along.

TypeScript (Optional)

As mentioned earlier, Angular 2 is intimately connected with TypeScript. Though it is not required that you write in TypeScript to build your Angular 2 application, you certainly have the option to utilize it and much of the Angular 2 core code incorporates TypeScript for a greater level of validation of a lot of the values that make up the library. TypeScript offers up the following description of itself on its homepage

TypeScript lets you write JavaScript the way you really want to. TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. Any browser. Any host. Any OS. Open Source.

That doesn’t sound too bad. The basic premise of TypeScript is that it offers an extension of syntax to regular JavaScript that offers a compile-time like checking of JavaScript types within your application. JavaScript, as you likely well know, is a weakly-typed language meaning that variables of one type can be assigned to a value another type (or sometimes automatically converted) with no error or warning being thrown. Thus in a weakly-typed language like JavaScript it is perfectly ok to do something like this…

var num = 1;  
console.log(typeof num);// number
num = "one";
console.log(typeof num); // string

where we just throw a string into our variable that *was* a number. Or, sometimes, we can even mix different types where an automatic conversion takes place…

var num = 1; 
num = num + 2 + " is the magic number"; 
console.log(num); // "3 is the magic number"
console.log(typeof num); // string

Whereas, if you were using a strongly-typed language like Java or C#, the following would not be allowed because the compiler would complain…

int num  = 1;
num = "one"; // error

In strongly-typed languages with variables being declared as certain types we cannot change the type of value that we assign to them. In JavaScript, however, we can. While this offers some flexibility within the language, it can also be the source for many bugs. TypeScript attempts to bring together the strengths of both strongly and weakly typed languages by letting you keep the flexibility of writing JavaScript as you like, but also allowing you to specify when you do not want the types of certain variables or parameters to change. Thus if we had something like this in vanilla JavaScript…

function greeter(person) {
    return "Hello, " + person;
}

var user = [0, 1, 2];
console.log( greeter(user) ); // Hello, 0,1,2

As we can see here user is an array and is of type “object.” JavaScript doesn’t care and just converts it to the string of all the values in the array “0,1,2”. If we accidentally did this and did not have good validation of data integrity, this could potentially be the source of a number of different unexpected bugs.

In TypeScript we could do the following where we always want to ensure the parameter of our function always received a string…

function greeter(person: string) {
    return "Hello, " + person;
}

var user = [0, 1, 2];
console.log( greeter(user) ); 

Notice the extra “: string” on our function parameter? If we were to run the TypeScript compiler with this code in our application we would get the following…

greeter.ts(7,26): Supplied parameters do not match any signature of call target

TypeScript does the type checking for us. Awesome! That, in a nutshell, is the 2 minute overview of what TypeScript is. We will not explicitly devote time to doing a Typescript tutorial because we will essentially learn enough of it as we go along in the context of Angular 2 — which is what is most important. If you do want a deeper background/understanding of what TypeScript is and why it is useful, you can take a look at the tutorials on the TypeScript website.

Now that you have Node.js installed, you will want to be sure that you have TypeScript installed as well (which you can install via npm). Be sure to sure to install globally with the -g flag.

$ npm install -g typescript

Now that we have briefly looked at some of the bare essentials to get up and running with Angular 2, we will now look at some of the steps involved with creating an application!

Getting Started with Angular 2

The first thing we will want to do to get up and going with creating an app in Angular 2 is to set up an http-server. If you have a local Apache or IIS setup you may wish to use this by putting your Angular 2 files in a directory created inside the www folder. Another quick easy implementation is to just pull down the Node.js http-server module…

$ npm install -g http-server

From there you can create a directory anywhere you like and run the following…

$ http-server

This will run an HTTP server at localhost:8080. If you got to https://localhost:8080 you should see something there that tells you a server is running. We have not created an index.html yet so as of right now some default content is just being served up. We will do that in what follows. It is worth looking at how to build an Angular 2 application in both native JavaScript and TypeScript just so you can get a feel for each and get a sense of which you prefer. We will start with native JavaScript.

JavaScript

We mentioned earlier that Angular 2 will use a lot of ES6 features. This will continue to be more and more prevalent as the library grows and matures, but currently, at the time of this writing in mid-2015, a lot of browsers do not support a number of the features found in ES6. Support is being added gradually but Angular 2 is still probably going to need to have a way to run in “older-ish” browsers. Thus, you can still write Angular 2 apps in JavaScript version ECMAScript 5. We will do this in what follows and it is probably easier to start here because there is less setup and tooling involved.

So create an index.html file and put the following markup…

<!DOCTYPE html>
<html>
<head>
    <script src="https://code.angularjs.org/2.0.0-alpha.26/angular2.sfx.dev.js"></script>
    <script src="main.js"></script>
</head>
<body>
    <my-app></my-app>
</body>
</html>

This will serve as the basis of our application. We are loading the dev version of Angular 2 from the repository at code.angularjs.org. In a production application we would want to pull this down and load it locally. But for now this will do fine.

Next, create a main.js file in the same directory as our index.html file…

function AppComponent() {}
AppComponent.annotations = [
    new angular.ComponentAnnotation({
        selector: 'my-app'
    }),
    new angular.ViewAnnotation({
      template: '<h1>My first Angular 2 App</h1>'
    })
];
document.addEventListener('DOMContentLoaded', function() {
    angular.bootstrap(AppComponent);
});

Now go to https://localhost:8080 and refresh. You should see the text “My first Angular 2 App” displayed. Congratulations, you have just created your first application with Angular 2!

We will get into what the pieces of our application handle in more detail in later tutorials. The basic premise of it is that Angular 2 has this concept of “components” which consist of 1) a piece of logic and 2) a view that defines the display of that logic. Components are somewhat analogous to what directives were in Angular 1.X. These components interact with services, another building block in Angular 2 that are responsible for data transfer; fetching and sending data to and from a server or other data source. Again, this “services” terminology is probably familiar to those readers who did some development in Angular 1.X — though “services” is a bit more universal in what it refers to in the web development world. At the most basic level we can see that our component defines a selector where we want to insert our data (the my-app element) and a template to render our data out. In this case it is static markup (an <h1> element), but this will become much more involved and dynamic as we build our application out and use services to move data in and out of our components.

So as we can see, we can use native ES6 JavaScript to build an Angular 2 app, but let’s see if we can use some TypeScript and some of the more novel features that we will find surrounding Angular 2.

TypeScript

Let’s create the same app using TypeScript. You should already have TypeScript installed via Node.js from earlier on but just in case you have not, you can do that now. Be sure to sure to install globally with the -g flag.

$ npm install -g typescript

There is one additional piece that we need to make our TypeScript stack complete. This is what is known as a “TypeScript definition.” What is this? Basically a TypeScript definition is the information that TypeScript needs to do the compile-time checking of types for common JavaScript libraries that are not written in TypeScript. For example, if you were using jQuery in your code and wanted to use, say, the fadeOut method somewhere in your application, how would TypeScript know that this method is expecting a string specifying the duration as the first parameter and a function as the callback as the second? The fadeOut method is defined in the core jQuery library which is not written in TypeScript. The answer is adding a reference to a TypeScript definition file which does tell TypeScript what to look for when someone uses something from the jQuery library in his/her code. There are whole repositories out there that consist of collections of TypeScript definition files for common JavaScript libraries that you can freely use if you want to write your application in TypeScript.

To implement TypeScript definitions we will need to install the TypeScript definition module tsd from npm…

$ npm install -g tsd

You then need to install the type script definitons onto your system using “tsd install.” Angular 2 you’ll want to have the d.ts files (TypeScript definition files use the format: filename.d.ts) for the following libraries. So run the following after installing the tsd module…

$ tsd install angular2 es6-promise rx rx-lite

This will pull down the .d.ts files and place them in a directory called “typings.” Angular 2 is the important one for now, but we will also definitely need the others later on so we might as well install them now.

The way that you import a TypeScript definition file in your own TypeScript file is by using the following syntax. You can add something like this at the beginning of your .ts files…

/// <reference path="../typings/angular2/angular2.d.ts" />

We are now all setup to start writing and compiling TypeScript. Sweet!

So similar to what we did before create an empty index.html file in the same directory as we have our “Typings” folder. In this index.html file add the following code…

<!DOCTYPE html>
<html>
<head>
    <script src="https://github.jspm.io/jmcriffey/bower-traceur-runtime@0.0.87/traceur-runtime.js"></script>
    <script src="https://jspm.io/system@0.16.js"></script>
    <script src="https://code.angularjs.org/2.0.0-alpha.26/angular2.dev.js"></script>
</head>
<body>
    <my-app></my-app>
    <script>
        System.import('main');
    </script>
</body>
</html>

The TypeScript setup includes SystemJS, a third-party open-source library that adds ES6 module loading functionality to browsers. Obviously this functionality is not present in the ES5 implementation. The above uses some slightly different synax to incorporate System.js. System.js will look for a file named “main.js” which we will create next by compiling a main.ts file to (remember that .ts is the extension of a TypeScript file) to main.js. So, in the same directory create a file called main.ts…

/// <reference path="../typings/angular2/angular2.d.ts" />
import {Component, View, bootstrap} from 'angular2/angular2';

@Component({
    selector: 'my-app'
})
@View({
    template: '<h1>My first Angular 2 App</h1>'
})

class AppComponent {

}
bootstrap(AppComponent);

Notice that we have the reference to the TypeScript definition for Angular 2. We then load our component and view similar to what we did before but also using a slightly different syntax.

Now we have to compile our main.ts file to a main.js file that can be used by our application. We do this by running the TypeScript compiler. In the same directory open up a terminal and type the following…

$ tsc --watch -m commonjs -t es5 --emitDecoratorMetadata main.ts

This sets up a “watch” such that whenever our main.ts file changes it will compile a main.js. Because this does not exist yet, the main.ts file will be compiled to main.js. The following main.js file is generated by the compiler…

/// <reference path="../typings/angular2/angular2.d.ts" />
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") return Reflect.decorate(decorators, target, key, desc);
    switch (arguments.length) {
        case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target);
        case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0);
        case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc);
    }
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var angular2_1 = require('angular2/angular2');
var AppComponent = (function () {
    function AppComponent() {
    }
    AppComponent = __decorate([
        angular2_1.Component({
            selector: 'my-app'
        }),
        angular2_1.View({
            template: '<h1>My first Angular 2 App</h1>'
        }), 
        __metadata('design:paramtypes', [])
    ], AppComponent);
    return AppComponent;
})();
angular2_1.bootstrap(AppComponent);

Ew, that is a little bit messy. But oh well. If we are writing in the clean simpler syntax of TypeScript, we will not need to concern ourselves with what the generated code looks like too much.

Note: I encountered some weirdness with TypeScript on Windows machines with VisualStudio 2013 or 2015 installed, both of which come with TypeScript. There seems to be some sort of conflict when typing to run the command line to the Node.js version of the TypeScript compiler. I got an error in this scenario. You might need to run the following instead with the “.cmd” file extension.

$ tsc.cmd --watch -m commonjs -t es5 --emitDecoratorMetadata main.ts

I ran the TypeScript compiler on my Ubuntu 14.X virtual machine without issue.

Now as before, if we run

$ http-server

in the directory with our index.html file and open up https://localhost:8080 we should see the exact same text as we did before. We have just completed our first Angular 2 app only this time in TypeScript! Not too shabby!

Summary

Now that we have examined some of the dependencies and implementations that will be found in the current and future releases Angular 2, we have a good basis for building upon this knowledge and adding greater layers of complexity. We have also seen that Angular 2 is flexible enough to give you the option to write your application in either native JavaScript or TypeScript and it is very possible that other JavaScript libraries will be incorporated into the ecosystem as well. In upcoming discussions, we will take a closer look at the ins and outs of the core building blocks we can use to build applications in Angular 2.

]]>
https://9bitstudios.com/2015/09/an-angular-2-primer/feed/ 0
JavaScript Routing https://9bitstudios.com/2014/12/javascript-routing/ https://9bitstudios.com/2014/12/javascript-routing/#respond Wed, 03 Dec 2014 18:42:33 +0000 https://9bitstudios.com/?p=1007 One of the more important pieces of any client-side JavaScript framework is the routing of requests when users navigate to different places. Basically a JavaScript router in a client-side application will listen for changes in the hash location and update the data and UI in the page accordingly, making all of the API calls that it needs to do so. But how is this feat accomplished? How do we match a route pattern defined in a JavaScript framework to an actual route navigated to by the user? This is something we will look at in what follows.

Normally, when you talk about matching you immediately think to look at regular expressions. However, the way that routes are defined in client side applications often have patterns that are a little bit different that the actual routes that will be navigated to by users. What do we mean by this? We talked about routing in Backbone.js here. Basically a Backbone.js router is set up like the following…

var Router = Backbone.Router.extend({
  routes: {
    "": "home", // url:event that fires
    "new": "createNew"
    "edit/:id": "editItem",
    "download/*anything": "downloadItem"
  },
 
  home: function() {
      alert('We have loaded the home view');
  },
 
  createNew: function() {
     alert('We are going to create something new');
  }
 
  editItem: function(idParam) {
      alert('We are going to edit entry number ' + idParam);
  }
 
});
var router = new Router;

In this setup, when a user navigates to a particular route, the function defined in the routes object will run. So if a user were to navigate to the route #/edit/4, the editItem function will run passing in the id 4.

Notice the parameterized definition of :id with the colon. The approach you have to take when writing a JavaScript router is to match a route like #/edit/4 to a route like edit/:id. It’s the same situation with other JavaScript libraries as well. Let’s look at an AngularJS router. This is an example router from a recipe book application written in Angular…

app.config(function ($routeProvider) {
    $routeProvider
        .when('/',
        {
            controller: 'HomeController',
            templateUrl: 'app/views/home.html'
        }) 
        .when('/login',
        {
            controller: 'LoginController',
            templateUrl: 'app/views/login.html'
        }) 
        .when('/logout',
        {
            controller: 'LogoutController',
            templateUrl: 'app/views/login.html'
        })     
        .when('/signup',
        {
            controller: 'SignUpController',
            templateUrl: 'app/views/signup.html'   
        })    
        .when('/recipes',
        {
            controller: 'RecipesAllController',
            templateUrl: 'app/views/recipes-all.html'
        })
        .when('/recipe/:id',
        {
            controller: 'RecipeDetailsController',
            templateUrl: 'app/views/recipe-details.html'
        })
        .when('/new',
        {
            controller: 'RecipeCreateController',
            templateUrl: 'app/views/recipe-create.html'
        })    
        .when('/edit/:id',
        {
            controller: 'RecipeEditController',
            templateUrl: 'app/views/recipe-edit.html'
        })
        .otherwise({ redirectTo: '/' });

}).run( function($rootScope, $location, userService) {
    
    // listener to watch all route changes
    $rootScope.$on("$routeChangeStart", function(event, next, current) {
    });
});

Notice here the same kind of parameterized matching in the routes /recipe/:id and edit/:id. How do you sort through these defined routes when a user navigates to a route to match them up with the desired code that you want to run?

Below is a function that we’ll use to match routes. If we had, say, an object literal of key value pairs of defined routes and callback functions to run for those routes, basically what it does is iterate through all the key value pairs in the collection. It checks to see if the route is a “match” and if it is it will return an object with information about the route. In this simple example, this will essentially boil down to the parameter value that was passed into the route, but we will get a bit more sophisticated with it a bit later.

So if our object literal looked like the following…

var routes = {
    "/": function() {
        console.log('This is the home route');
    },
    "/about": function() {
        console.log('This is the about route');
    },
    "/edit/:id": function(obj) {
        console.log('This is the edit route route with the id ' + obj.id);
    },
}

Our routing function for matching routes would look like the following…

function matchRoute(url, definedRoute) {

    // Current route url (getting rid of '#' in hash as well):
    var urlSegments = url.split('/');
    var routeSegments = definedRoute.split('/');
    var routeObject = {};

    if(urlSegments.length !== routeSegments.length) {
        // not a match
        return false;
    }
    else {   

        for(var i = 0; i < urlSegments.length; i++) {
            if(urlSegments[i].toLowerCase() === routeSegments[i].toLowerCase()) {
                // matched path
                continue;
            }
            else if (routeSegments[i].indexOf(':') === 0) {
                // matched a param, remove query string (which is handled below) and push id onto object
                var val = routeSegments[i].replace(':','');
                val = val.split('?')[0];
                routeObject[val] = urlSegments[i].split('?')[0];
            }
            else {
                // not a match
                return false;
            }
        }        
    }

    // after all is finished, return the route object to pass to router for use by controller
    return routeObject;

}

Now if we were going to put this to use, we could create another function to handle changes in the hash and call that function when we listen for changes in the hash …

function routerHandler () {  

    // Current route url (getting rid of '#' in hash as well):
    var url = location.hash.slice(1) || '/';	

    for(var i in routes) {

        var routeData = matchRoute(url, i);

        // Do we have a route? 
        if (typeof routeData === "object") {
            routes[i].call(this, routeData);
            // we found our route, no need to continue...
            break;
        }
    }
}

Finally, we need to make sure that we listen for changes in the hash…

// Listen on hash change...
window.addEventListener('hashchange', routerHandler);  
    
// Listen on page load...
window.addEventListener('load', routerHandler);

Looking pretty good so far. We could also handle the cases where we have a query string (e.g. /#/edit/4?food=cheese). If we update our matchRoute function to include the following, we can handle this scenario…

function matchRoute(url, definedRoute) {

    // Current route url (getting rid of '#' in hash as well):
    var urlSegments = url.split('/');
    var routeSegments = definedRoute.split('/');
    var routeObject = {};

    if(urlSegments.length !== routeSegments.length) {
        // not a match
        return false;
    }
    else {   

        for(var i = 0; i < urlSegments.length; i++) {
            if(urlSegments[i].toLowerCase() === routeSegments[i].toLowerCase()) {
                // matched path
                continue;
            }
            else if (routeSegments[i].indexOf(':') === 0) {
                // matched a param, remove query string (which is handled below) and push id onto object
                var val = routeSegments[i].replace(':','');
                val = val.split('?')[0];
                routeObject[val] = urlSegments[i].split('?')[0];
            }
            else {
                // not a match
                return false;
            }
        }        
    }

    // did we reach the end? Get querystring, if any...
    var hash = window.location.hash.split("?")[1];

    if(typeof hash !== "undefined") {

       var queryString = hash.split('&'); 
       var queryStringObject = {};

       for(var i = 0; i < queryString.length; i++) {
           var currentParameter = queryString[i].split("=");
           queryStringObject[currentParameter[0]] = currentParameter[1];
       }

       routeObject.queryString = queryStringObject;           

    }

    // after all is finished, return the route object to pass to router for use by controller
    return routeObject;

}

Our example above shows one possible way to handle hash-based routing in JavaScript. There might be other ways to do this. Utilization of RegEx in some manner might be another route (*bad pun groan*) to go, but all in all, whatever you choose, as we have seen here, it does not have to be needlessly complicated. Take a look at the demo below…

View Demo
]]>
https://9bitstudios.com/2014/12/javascript-routing/feed/ 0