Toggle Menu

Insights / Digital Service Delivery / Building Scalable and Beautiful AngularJS Apps (Part 4): Reusable Components, Templates, and Services

April 04, 2017

Building Scalable and Beautiful AngularJS Apps (Part 4): Reusable Components, Templates, and Services

5 mins read

Doguhan Uluca was a speaker at ng-conf 2017, where he talked about Do More with Less JavaScript. Check out the full talk. For more about conference-related materials visit TheJavaScriptPromise.com.

This post is the fourth in a multi-part series on building scalable and beautiful Angular apps.

First things first: Running code is far more valuable than any blog post. So, launch the demo site here, check out the source code on GitHub and pull the Docker image without delay: docker pull duluca/angular1.5-starter

Reusable Components

You can create reusable components that can be used like directives inline within html templates, but also, they can be navigated to using the router like a page. See app/components/login and how it’s referenced in app/routes/home like a directive as an example of this technique.

Reusable Template Code

The entire app uses vm as the name for every controller. vm stands for viewModel from the MVVM pattern. Read my article on the pattern here. The benefits of using vm in the controller is related to the contextual quirks of the this keyword in JavaScript, which is detailed in John Papa’s Angular Style Guide as rule Y032.

There’s another benefit to using vm, which helps tremendously when creating new screens. When you give your controllers unique names, you’ll be binding to {{about.name}} so if you wanted to move around the template code surrounding this binding over to another screen, the binding won’t work. You’ll have to hunt down the error manually and fix it because Angular 1 isn’t very helpful about pinpointing binding errors. However, if you bind to {{vm.name}} your template code will just work across your app.

You’ll notice that I never even mentioned $scope because you should never use $scope in your components and templates. There is one and only one exception to this rule and that is when you’re building isolated, well-encapsulated user controls as directives that require rich user interactivity; keywords being isolated and encapsulated.

Reusable Constants

It is a bad coding practice to use string literals in your application and this applies to Angular apps as well. You may notice that I declare a moduleName on every file, where I need to export an Angular component. In app/routes/home, declaring ‘app.home’ once to define the module name and again to export the module name on the last line with module.exports = ‘app.home’ would lead to a frustrating situation if I ever renamed app.home, so it makes sense to store this string literal in a variable. There’s another case, when creating new screens, if I copy home and renamed the files and only the module name, it would be easy to miss the fact that I needed to update the exported name as well.

We can apply the same idea to objects and arrays that contain static information that you use to do logical if comparisons on, or populate dropdowns. Using string literals or copying and pasting such objects/arrays around can lead to runtime bugs when you update one instance, but forget to update the other one.

As an example, see app/shared/roles, where I defined a roles objects that contains the string literal for the roles user and admin. I require this object, whenever I need to check if a user is an admin. See service/auth -> login() function as an example.

Angular’s own dependency injection system requires too much boilerplate to create such a simple shared service like this. So, we leverage Browserify and just export our object using module.exports. Also, by using Object.freeze, we can ensure contents of our object or array won’t be modified at runtime by mistake.

Reusable Services

Previously, we covered that every controller uses vm to define its own context. The same rule applies to Angular services as well. Following this pattern, pays off dividends when leveraging the self-revealing module pattern in building your services. Doing JavaScript variable scoping gymnastics in your head is no fun and it will almost certainly lead to bugs or runtime errors when you’re in a hurry.

You should leverage services to share behavior, business logic, persistence and server access between your components. Services help decouple your UI code from your back-end server. Enforce this architectural pattern with rigor. If you use $http or $cookies outside of a service, reconsider your architecture. Do not abuse services to share state; there’s ng-redux for that. Otherwise, leverage route parameters to communicate between components.

Using the self-revealing module pattern is important because every function you expose through a service becomes a public interface. In an application with hundreds of views, there’s no taking back of a function publicaly exposed. With this pattern, we can control exactly which function is public and which ones we can keep private. If in the future, we need to refactor or deprecate a particular function, you can do so gracefully by piping the function through another, proxy, function.

Start Today

As German poet Goethe puts it, “what is not started today is never finished tomorrow.” Here’s the code on GitHub. Fork it, create issues with it, otherwise just clone it and use it. Start today.

If you have any questions, ask them on Twitter @duluca.

You Might Also Like

Resources

Simplifying Tech Complexities and Cultivating Tech Talent with Dustin Gaspard

Technical Program Manager, Dustin Gaspard, join host Javier Guerra, of The TechHuman Experience to discuss the transformative...

Resources

How Federal Agencies Can Deliver Better Digital Experiences Using UX and Human-Centered Design

Excella UX/UI Xpert, Thelma Van, join host John Gilroy of Federal Tech Podcast to discuss...