An awesome setup for your AngularJS project (3/3)

This is post #3 of 3 to explain about fsstatic2. Beware: it may change forever how you approach web development. 🙂

Let’s finish this.

docs.html – A component catalog AND a playground environment.

When working with AngularJS, it’s very useful to have a “playground area” where you can create components without necessarily changing the behaviour of the main application. The DOCS environment serves this purpose. It’s a collection of “test pages” which are able to showcase its own code. It’s very easy to add a new test page, let me show you:

This environment actually can serve multiple purposes:

Playground area: using this environment on their local machines, developers on your team can develop and test components individually. It’s very advisable that every component have its own test page.

Component documentation catalog: If you publish this application on the internet, people can check it out whenever they need to see which components are available, and how to use them.

Prototype showcase: Developers can show their work in progress to designers, clients, and stakeholders in general, in order to collect feedback and refine the direction the product is supposed to go.

So, this is another good practice:

Good practice #7: Have a component catalog.

Tests: running, debugging and coverage reports

Tests should go in the docs folders and be called “test_*.js”. Now there are a few things you want to be able to do with them, like:

  • Run them all [runtests –singleRun=true]
  • Watch for changes, and keep running [runtests]
  • Run a single test [runtests –grep=blabla]
  • Debugging [runtests, open localhost:9876/debug.html]
  • Generate test coverage report (sorry, forgot about this in the video) [runtests –coverage=true]

The video below shows you how

 

test_todo.js: how to write good tests

If you follow Good practice #5 you’ll end up with a codebase where most of your code will be inside AngularJS services. Well, this is *very* good for testing – among all the types of objects you can create with Angular, services are the easiest to test.

Take a look at our test (test_todo.js). I could have made that test use TODOModel directly, but instead I created another service TODOTester. When testing viewmodels like TODOModel, I found out that using a “tester service” like that allows me to write more expressive tests. I mean look at this code:

TodoTester.assert_count_todos(0);
TodoTester.type_and_add('one todo');
TodoTester.type_and_add('to twodos');
TodoTester.type_and_add('tdee throdos');
TodoTester.assert_count_todos(3);
TodoTester.remove(1);
TodoTester.assert_count_todos(2);
TodoTester.assert_todo_text(1, 'tdee throdos');

It really looks like we’re simulating a user “driving” the application. You can even write the test first, give empty implementations to TodoTester methods – your tests will pass, then add the method bodies later. This gives you an incredible “TDD feeling” when you’re doing it.

Another detail worth mentioning is that the tests include api_mock.js, not api.js. This means you can reuse the same mock implementations you already have in your tests (and this is much much better than mocking ajax calls with $httpBackend). For example, we do that in TodoTester.type_and_add(). We know TODOModel.add() will make a (fake) ajax call, and all we need to do is to force our fake backend to respond by calling $timeout.flush().

expect(count).equal(TODOModel.todos.length); 
//still waiting for the "server" to respond
$timeout.flush(); //yay, the server responded!
expect(count + 1).equal(TODOModel.todos.length);

See?

Here’s another tip that may help you with your tests. You could create additional methods on the fake FSApi service (say, FSApi._set_todos(array)), which are only supposed to be used inside tests or DOCS sample pages, which would allow you to preload data on the “fake database”. Speaking more generally, you can add methods to your fake apis that change your fake backend’s behaviour. You can use those methods to load different scenarios both inside unit tests and test pages.

We talked a lot about tests and the good practice here is not so obvious this time:

Good practice #8: ViewModel tests should be talking in a user’s vocabulary.

You should think “I’m gonna write a test for requirement/scenario X” much more often than “I’m gonna write a test for method X”. High test coverage should be only a consequence of that approach. Most of the time this means having to create a “tester” service with method names that are very close to user actions. Sometimes you may also feel the need to create other types of test-helping services, like a “ScenarioLoader”. In any case, the code inside the test itself, should be “easy” enough for even non-programmers to understand.

Wrapping up

What we’ve seen here is more than a project’s setup. I believe that the techniques and ideas presented here can be applied to any web application project to improve architecture/code quality, maintainability, testability, development speed, and as a consequence, happinnes, for you and your team 🙂

Perhaps this is a lot to remember, and if so, the list of good practices below will help, hopefully.

  • #1: Have a project help on the command line (help your team and your future self)
  • #2: Everything is a component (which uses other components)
  • #3: Have a mock API (with promises created out of $timeout)
  • #4: Plan for some flexibility in your templateURLs (with some global variable as a prefix)
  • #5: NEVER use the $scope as a view model. Put your models into services (this is very important!)
  • #6: Let me see the API first (helps answer the “What does this do?” question faster)
  • #7: Have a component catalog
  • #8: ViewModel tests should be talking in a user’s vocabulary (and you’ll need to create extra services for this)

OK, that’s all I had to say folks. Hope it helps.

Cheers! 🙂

Anúncios

An awesome setup for your AngularJS project (2/3)

This is post #2 of 3 to explain about fsstatic2. Beware: it may change forever how you approach web development. 🙂

So, let us continue.

index.html – How the main application works

Routing

There’s not much to say here, it just applies basic ui-router configuration to make a Single Page Application.

  • fs_main.js is responsible for setting up the routing rules
  • Each route has a template that is as simple as <fsissue></fsissue>
  • The toolbar has some <a ui-sref=”statename”> links on it that activate different routes

More details in the video below

Now, notice that even if we had chosen not to build an SPA, we could still use a similiar structure. For example, instead of having an SPA in the index.html file, we could have more pages, like home.html, issue.html, and so on. The code on such a page, say: home.html, could look like:

<body ng-app="fs_main" layout="column" ng-controller="FSMainCtrl">
  <md-toolbar layout="row">
    <fstoolbar></fstoolbar>
  </md-toolbar>
  <div layout="row" flex>
    <div layout="column" flex id="content">
      <md-content layout="column" flex class="md-padding">
        <fshome></fshome>
      </md-content>
    </div>
  </div>
</body>

 

Can you smell another good practice here? Right…

Good practice #2: Everything is a component.

Our application should be structured as a composition of components, not very different from each other in terms of architecture. In our case I’ve decided that each component is a restrict=”E” directive. The result is a very standardized way of doing things. People on your team will be able to understand each other’s code faster because everything has more or less the same structure.

Mock api

Don’t talk to URL’s directly. Instead, hide your backend URLs behind a stateless Angular service with a bunch of methods that return promises.

Then, make an alternative implementation that pretends to talk to the backend, but actually generate hard-coded data in javascript and resolves promises with $timeout. This takes us to the next good practice:

Good practice #3: Have a mock API.

This will allow you to have an environment where you can switch between a full stack environment and front-end-only environment. This ability will most likely boost the speed on your development process.

Authentication

Does it even make sense talking about authentication if we don’t have a real backend yet? The answer is YES. The front-end plays a big part in the authentication process and we can go and implement the client part beforehand, just like we can do it with anything else, really. We don’t even need a video this time (but you will have to take a look at the code as you keep reading!)

FSAuth’s (fsauth.js) responsibility is to provide information about the current user to the rest of the application. Whoever needs to know if we are authenticated can just call FSAuth.authenticated(). If they need the current username, then FSAuth.user.username. The <fslogin> directive (fslogin.js/fslogin.html) has a viewmodel (FSLoginModel) that is activated in the login screen. When the user fills the login form and hits OK, FSLoginModel.login() will ask our “backend” to authenticate us – FSApi.login(username, password) – and then give FSAuth the currently logged user by invoking FSAuth.set_user().

Now there’s an important detail here. We want the real backend implementation of FSApi.login(u, p) not only to return a json with information about the current user. This particular response also needs to come with a set-cookie header that will tell the browser to store a session variable in a cookie, which will be sent in all subsequent requests. That way, the backend can know who is the user making those requests. This is what is supposed to happen when we call FSApi.whoami() – if the request comes from an authenticated user, this should return that user data, otherwise the response must say something like “Oh, you’re just nobody :-)”.

Now FSAuth, during its initialization (see _check_for_authentication) will call FSApi.whoami() and store the current user in itself if it is authenticated. The current mocked implementation of FSApi.whoami() will always return an authenticated user – this is arbitrary. So if the user refreshes the page, FSAuth will still know who the current user is.

Our main <fstoolbar> (fstoolbar.js/fstoolbar.html) is also dependent on FSAuth. You can notice that the toolbar’s right side will show different content depending on the result of FSAuth.authenticated().

The logout feature is left as an exercise for you to understand alone 🙂

So, the only piece missing in our authentication process is the backend. If we have correct, real implementations for FSApi.login(u, p) and FSApi.whoami(), it should work for real.

Template URLs

Anywhere we include a template we have a path prefix before the template URL. For example, the templateURL for the <fslogin> directive is FS.BASE_URL+’login/fslogin.html’.

This will give you more flexibility to move included .js files relative to the main html file. Let me show you.

And don’t forget this also goes for templates included with ng-include.

This is important, so let’s add it as another good practice to our list:

Good practice #4: Plan for some flexibility in your templateURLs.

This can help you have with the dev vs. prod build differences, and it will also save you some trouble when you have to move stuff around.

todo.js – A detailed example of how to make a component

The DOCS page has a <todo> component that illustrates how to make a simple component. This doesn’t relate to any feature in FS, it’s just that I didn’t have a better idea for a sample component.

The main recommendation that I give to everyone using angular is this: do not use the $scope as a model. It’s better to put the model implementation inside a service. Let me show you.

Of course, this is another good practice.

Good practice #5: NEVER use the $scope as a view model. Put your models into services.

I can’t stress enough how important this is. Having your view models inside services allows you to have a much cleaner code as a result of a clear separation of concerns. As a bonus, it also makes your code very easy to right unit tests for.

Your screen needs some AJAX action? No problem, inject an API service into its viewmodel and let the model handle the AJAX for you. Need a loading-please-wait animation? Sure, have a boolean attribute in your model that tells the template when to display it. But keep your distance from the $scope object. Trust me, the less you rely on it, the better off you are.

If you look at the rest of the directives in the src/ folder you can see that I intend to apply this same pattern to all components.

Some other detail that you might have noticed is the way that the code inside the TODOModel is laid out:

var m = {
    newtodo: '',
    adding: false,
    todos: [],
};

angular.extend(m, {
    add: add,
    remove: remove,
});

There’s actually another good practice behind this:

Good practice #6: Let me see the API first.

You can see what TODOModel is supposed… er… to do (:P) after a quick glance on just the snippet above, right? It’s a plain object with those 3 attributes that make up its state, and those two operations that change it. Seeing the attributes and operations at the top allows me to realize faster what are this object’s responsibilities.

All right, this is enough for post #2. In the next and last post we’ll cover.

  • docs.html – A component catalog AND a playground environment.
  • Tests: running and debugging
  • test_todo.js: how to write good tests

One last thing: please repeat with me one last time (out loud is better):

I will never use the $scope as a viewmodel. 

Good.

Just to be sure 🙂