Well, I finally found the time to start looking into Angular 2, so naturally I went to the website to do the quick start demo. I got it working but I did have a some hangups getting my IDEs (yes that plural) to work with Angular 2. This is understandable considering at this writing Angular 2 is in "Alpha".
So where I work, we have a large legacy app that we would like to modernize without have to rewrite the entire application from scratch. We have been looking at the React framework because as there site states, "you can try it out on a small feature" and it won't impact the rest of your application. It also helps that it was developed and supported by the team at Facebook.
If you are not familiar with React, it is another client side framework, developed by Facebook, but unlike Angular, Ember, etc, React only focuses on the "View" portion of your MVC application.
What's a Virtual DOM
React uses what they call a "virtual DOM" to interact with DOM elements, so when you as developer are working with React, you actually never get a direct reference to a DOM element, but instead you write JSX code which is very similar to HTML and then React manages the interactions with the DOM for you.
This is where React really shines, because they optimized the interaction to be really fast. So whatever changes you want to make to the DOM, you make the changes in the JSX code, and then React figures the fastest possible way to make the changes to the DOM behind the scenes.
The Stream with Node, Twitter, and Socket.IO
I have discussed building a realttime stream before using Node, so I won't go into too much details there, but here I will say that this time I am using Socket.IO and since Twitter has real time streaming APIs, I decided to use that for streaming purposes.
On the client side I am then listening for the stream of tweets via Socket.IO and when a tweet comes in I am firing off a callback function that will update the list of tweets.
The React Code
I have broken my React code into three different files for demo
The fist file is parent file and it contains the starting point where React is going to take over the DOM and inject my code.
Since in React, you are building components, the basic way do that is to call the React.createClass function. Inside that function you can set several properties as well as add your own properties to create the component.
Ultimately, once a component is built, I then call the React.render function which takes two parameters (the parent component, and in this case, the DOM element I am attaching the component to.
The properties in my TwitterFeed class are as follows:
getInitialState: this property takes a function that allows you set the initial state of properties I am going to use. In my case I have a tweets array. It is recommended that this property only be called in your parent component.
addTweet: this is a function I created to add a tweet to the tweet list and then setting the state. The state is what React uses to pass data from one component to the other. It is recommended that only the parent component can change the state. All child conponents are immutable and only listen for changes made by the parent.
componentWillMount: this is a React event that fired right before the component is rendered. There are various event similar to this one like componentDidMount which fire immediately after the component is rendered.
render: this is the function the renders the final HTML to the DOM. Inside this render function I am calling the child component TwitterList and passing that component the tweets array from the state.
This component takes the tweets from the parent Twitter-Feed component and create the DOM container which is a DIV and then makes a call to its child to create a single tweet component.
This components gets the tweets from the parent via the "props" object.
Also note that am calling a propTypes property. This is useful as it sets the expectation of what type of data it expects. When this is set and you don't pass in the correct data to the component, React will send a nice error explaining what went wrong in the console.
The final child component builds the single tweet row. It is passed the tweet from the parent Twitter-List component
The Final Result
Obviously I need to work on the presentation a little bit but when the tweet list is automatically being updating realtime as the tweets are being made.
In my last post I talked about how AngularJs Factories need to be instantiated first before they are returned to the calling code.
Consider the following TypeScript code:
The problem here is the code that is generated builds a function by creating a prototype and the way this code is structured, "this" is referring to the global lexical scope and not scope of this class. So this.$http is undefined.
Funny thing is I did not originally write the code this way but using Visual Studio and Resharper, the code suggestion asked me if I wanted to change it. Hey! Why not?? Looks cleaner.
Here is the slightly different TypeScript example, this time using arrow function to implement the needed function:
Now the generated getUser function is in my constructor as a closure function and the "this" has been preserved by defining it with the "_this" variable. Plus, I think this approach is easier to read.
I have been refactoring an Angular project of mine to use TypeScript. I have been doing this for a few reasons:
To be able to start using the EcmaScript 6 and beyond coding features.
To make it easier to upgrade to Angular 2 in the future.
To take advantage of TypeScrips typing and other features.
One of the issues I have come accross while doing this excercise is that all my services are actually Angular Factories.
Angular has three types of services you can have to reuse logic accross your application and they are services, factories, and providers.
Factories are typically the most common way to create these components. A factory is basically a function call that returns an object and that object contains properties and functions.
Services are similar to factories except that they are instaniated when you call them. So the functionality you want to reuse you would put the logic on the "this" keyword which is essentially attaching the functionality to the service itself.
When you use a service Angular will autimatically call it using the "new" keyword.
Providers are a little more involved, but they are useful when you want to setup some configuration before befure you actually call it. It is the only type of service that is available in the "config" section of a module whcih is executed first, so you can use it at that point to set it up and then call it later in a controller for example.
The problem in TypeScript
Because in TypeScript you create interfaces and then classes that impliment those interfaces, factories will not new up your class. So the following will not work in TypeScript.
The code that is generated looks like this:
To get around this issue, I had to "new up" the classes myself before I returned them as a factory.
So since this is the case, you might as well start using services rather than factories if you are going to use TypeScirpt, but if you are going to use factories, remember to instantiate them first before you inject them into your code.
Well, I just went through the slide deck of a presentation that Igor Minar gave at the ng-europe conference.
There wasn't much details, but in the deck it says that controller, $scope, and the angular.module command are going away. That's like the most commonly used pieces of the framework.
At first, I was a little taken back by this revelation, but the more I think about it, the more it make sense. Lately, I have tended to just have one line of code in a controller that basically attached a service to the $scope, and the latest craze I've seen is the "Controller As syntax".
On the down side, this seems like totally different paradigm for Angular and a totally new approach to take in and get accustom too. Then again, with ECMAScript 6 coming out soon as well, I'll be learning a lot of new syntax already, so might as well throw Angular 2.0 in too. Two birds one stone.
Creating a Single Page Application will inevitably require making an API call to a service at some point, and AngularJs provides two mechanisms to do accomplish this in its framework. The one instance is using the ngResource module, and the other approach is using the $http service. Personally, I am sort of torn as to what approach to use but I am finding my self gravitating more towards using the $http service just because it is more customizable. I think ngResource shines if you have a predictable set of RESTFUL API calls and you just want to make calls and get responses back, but if you want so more control of things like exception management, or provide a chain of promises, I find the $http service to be more straight forward in setting up.
Another nice thing the $http service is that is comes with the corresponding $httpBackend service which can be used for mocking calls so your unit test have no external dependencies. After all, that is whole point of a unit test is that it is self contained and not reliant on any outside factors.
So in this example I have a service that makes an API call to get the friends for a current user:
This service has a GET call and a POST call and I want to write a test that will test a mock of the POST call.
The Test Setup
So lets look at the unit test and talk about what is going in this code.
So the first thing that is done is that we need to define our service and also define the $httpBackend service that will be mocking our calls.
I am also defining a mocked response that will be returned one the service is called.
In the beforeEach section, the service and $httpBackend are injected into our environment before each test.
And then after each call we want to ensure that the calls were expected to be made were actually made and that there were no exceptions thrown on those calls.
This tells the $httpBackend service to expect a POST call to be made to a service and that it will return the refreshResponse object that was defined at the top of the page.
The next lines of code are the actual execution of the service.
var deferredResponse = friendsApiService.refreshUsers();
users = response.data;
Since we want to preserve the asynchronous nature of the call while at the same time be able to test the response of the call we will then need to flush the response. This will explicitly flush any pending request so that when we can evaluate the response it returns within the unit test. If we did not do this, since our call is asynchronous, our test would finish before the call was actually completed.
Once the flush is complete, we now can evaluate our response to see of our test is working.
In this post, I will show an example of unit testing a simple controller.
In this case I have a started building a controller, and one of the first things I want to test is a collapsible panel inside my view. The way the panel works is when at first the user initially goes to view that has a list of items, at the top of the view there is a button that opens and closes a panel that contains a search text box so the user can filter the items on the page.
The Controller Code
So to start out, my new controller looks like this:
The $scope initially sets the isSearchCollapsed property to true, in which the view uses to indicate the panel is closed.
Here is the Jade view that shows or hides the panel.
Using the Angular UI directive, the panel slides open and closed based on the value of the isSearchCollapsed property. The value of that property is change every time the toggleSearchCollapsed is executed.
The Test Code
To test the controller code, I need to be able to replicate the controller and its scope in the test environment and Angular gives us a way to do that with two built in functions. Lets look at the code
First thing I need to do is to replicate the controller's scope so I can then attach it to the replicated controller. The $rootScope object has a function called $new which will do that for me. This function basically is used to create child scopes from the base scope.
The next thing I need to do is replicate the controller assigning our newly created scope to that controller. Angular uses the $controller service internally to instantiate controllers, and we can use it to instantiate our controller in our test environment.
Once that is set up we are ready to start testing, and in this case, I have three tests.
Starting from scratch my first test is there just to make sure I have successfully replicated the controller and it's scope.
The second test, makes sure the isSearchCollapsed property is initially set to false.
The third test, validates that when the toggleSearchCollapsed is triggered that the value of the isSearchCollapsed is changed. Since my second test passed and I know the initial value will be true, when I call the function and test the value again it should be false.
When testing in AngularJs, I have found that once you have everything setup correctly, unit testing with Jasmine is not all that complicated. However, when I first started testing in AngularJs, getting things setup right was a bit hit or miss for me. At the time, the documentation for testing wasn't all that definitive so it took me a little longer to grasp the concepts than probably should have been needed.
So in this post, I am going to show how I test a simple service that takes an array of directory paths as a string and parses it out into an array of arrays. Its a simple test for a service that has no dependencies and only one function.
I am basically building on my previous post where I showed how to setup a unit testing environment using NodeJs, GruntJs, KarmaJs, and Jasmine.
So in this example, I am taking an AngularJs factory that returns one function. This function should take an array of paths and convert them into an array of array of directories.
The whole point of encapsulating this code is that not only is it testable, but is also mockable, and it is reusable.
The Unit Test
So here is the test:
The way I setup a unit test for the module, is I divide the test module into three different sections.
The first section I include any variables that will be used by all the tests. This includes things like the scope, mocks, services, controllers etc. On a side note, if my mock data becomes too big, I separate them out into a different file and place the file in a folder called mocks and then reference them from there. My Karma.conf.js file then will need a reference to this folder.
The next section is the most critical section and that is the section that sets everything up before each test runs.
The last section is the section that handles all the tests.
Let me focus on the second section as that for me has always been the trickiest to get right.
Jasmine comes with two global variables (beforeEach and afterEach), and as the names describe, the beforeEach runs before each test, while the afterEach runs after each test.
Here I am only using the beforeEach to set up the tests.
The module command is used to specify what AngularJs module you are going to test. You need not worry about any other dependent modules, at this point, the module routine is only specific to the module you currently want to test, even if you have other dependent modules.
The inject function wraps objects into an injectable function and is where I am initializing the service. In this case I am only referencing my service that I want to test since I don't have any other dependencies.
Notice the underscores in the parameters.These underscores are actually ignored by the injector but allow us differentiate between what is being injected and the module level variable that we want to use for all out tests so they can be assigned accordingly.
The Actual Tests
Finally, the last part of the file contains the actual tests themselves.
If I am using a TDD approach, the first thing I do is make sure the service exists. This is represented by the test, "should be defined as a service". Once this test passes, I am then ready to move on to my next test.
I then test to make sure the method I am actually testing in the service exists. This test is represented by the test, "should have a function called getReportList". Again, once this test passes, I can then move to me next tests.
Then my last two tests, test that the function is actually returning what I am expecting it to return.
Hope that helps. In later posts I will show some different unit tests, such as testing a controller, testing a service that has a $HttpBackend dependency, and testing a controller that has a service dependency. In those posts I hope to cover things like mocks, spies, and the $HttpBackend service as well.
As a developer coming from an ASP.Net, C# background, I had come to appreciate the ability to be able to unit test my code. I find that I am not all that great a programmer, and often times the unit tests I write have brought to surface a bug that I would have introduced had I not had a unit test in place to discover the bug in the first place.
I find the feedback loop is much quicker with the unit tests I write, because I can see write away that the code is working or not without having to do much of anything.
So with being said, lets look and see how to setup the unit testing framework.
For my unit tests, I use Jasmine, Karma, Grunt which all sit on top of the NodeJs runtime, so for that you will first need to install NodeJS.
NodeJs comes with package installer called NPM. Similar to Gems in Ruby, or Nuget in Visual Studio, NPM will install all the packages needed to start unit testing.
Note, if you are starting a project from scratch, I would recommend installing Yoeman. Yoeman is a scaffolding package that will install a starter project with everything already configured. For example, when I have a new AngularJs project, I use the Yoeman Angular Fullstack generator, and then go back in and finegle the generated code to my own preferences.
For this post I am assuming that the project and tests already exists. So I am going to only go though all the steps to to get up and running with unit testing on an existing project.
Also, I am running Windows 8.1, so if you are on a different OS like Linux or Apple OS, or even a different version of windows, these steps might differ slightly. I'm not really sure, but I had noticed that other posts were slightly different than what I had to do to get started.
Creating the package.json File
The first thing to do is create a package.json file. NPM uses this file to keep track of what you have installed and what versions of the packages you are using. The easiest way to create this file is to initialize NPM from the command line.
Doing this generates a file that will look like this.
You could also just manually create the file yourself. Either way, this file is required.
Installing the packages
You can install each package separately from the command line but an easier way to get everything installed is to get a copy of the packages.json file with all the packages you need and just type the following from the command line:
This command will take what ever you have in the package.json file and install it in your project if it isn't already installed.
My package.json file looks like this:
The packages under the "devDependencies" node tells NPM that these are packages are only for use in the development environment and are not to be copied to production.
You might also want to install Grunt, Karma and maybe even Yoeman globally so you don't have to keep installing it for every project. You can do that with the following commands.
npm install -g grunt-cli
npm install -g karma-cli
npm install -g yoeman
The "cli" suffix is the package that allows grunt and karma to called directly from the command line in Windows. The -g parameter tells NPM you want to install the package globally.
Initializing the karma.config.js File
You can generate a karma.config.js file by running the following command:
When you run this you will be asked a bunch of questions:
Which unit testing framework do you use?
Do you want to use Require.js?
Do you want to capture any browsers automatically?
What is the location of your source and test files?
Should any of the files included by the previous patterns be excluded?
Do you want karma to watch all the files and run the tests on change?
I typically accept all the defaults, and, if needed, go back and change the configuration later. My current file looks like this:
My grunt file is pretty simple since I am only running one task. It looks like this:
Now I'm ready
Running "karma:unit" (karma) task
[2014-06-24 11:14:00.421] [INFO] karma - Karma v0.12.16 server started at http://localhost:9876/
So for example:
If/Then statements and Firefox
The problem is in Firefox if you define a function inside an if/then statement the function no longer gets hoisted. This only happens in Firefox.
So in the following example, the following code works in IE and Chrome but is broken in Firefox.
In Firefox I get an error because it did not hoist the callHoistedFunction therefore it was not defined at the point it was being used.
ReferenceError: callHoistedFunction is not defined
var hello = callHoistedFunction();
So the moral of the story is never define a function inside a “if/then” or “try/catch” statement. As a matter of practice, you should probably just define all variables and functions at the top of the outer function as that is where it gets hoisted to anyways.
Here is his video on his book in case you were also interested.