I’m always tinkering around with different ideas and toward the beginning of 2013 decided to build a sample application using AngularJS that I call Customer Manager. The goal of the application is to highlight a lot of the different features offered by AngularJS and demonstrate how they can be used together. I also wanted to make sure that the application was approachable by people new to Angular since I’ve never found overly complex applications great for learning new concepts.
The application initially started out small and was used in my AngularJS in 60-ish Minutes video on YouTube but has gradually had more and more features added to it and will continue to be enhanced over time. It’s used in a new “end-to-end” instructor-led training course my company wrote for AngularJS as well as in some video courses that will be coming out. Here’s a quick look at what the application home page looks like:
In this post I’m going to provide an overview about how the application is organized, back-end options that are available, and some of the features it demonstrates. I’ve already written about some of the features so if you’re interested check out the following posts:
- Building an AngularJS Modal Service
- Building a Custom AngularJS Unique Value Directive
- Using an AngularJS Factory to Interact with a RESTful Service
Two versions of the application are available on Github including a “standard” version that uses out-of-the-box AngularJS features and a custom version that provides custom routing and dynamic loading of controller scripts:
CustomerManager with Custom Routing: https://github.com/DanWahlin/CustomerManager
The Customer Manager application certainly doesn’t cover every feature provided by AngularJS but does provide insight into several key areas. Here are a few of the features it demonstrates with information about the files to look at if you want more details:
- Using factories and services as re-useable data services (see the app/customersApp/services folder)
- Creating custom directives (see the app/customersApp/directives and app/wc.directives/directives folder)
- Custom paging (see app/views/customers/customers.html and app/customersApp/controllers/customers/customersController.js)
- Custom filters (see app/customersApp/filters)
- Showing custom modal dialogs with a re-useable service (see app/customersApp/services/modalService.js)
- Making Ajax calls using a factory (see app/customersApp/services/customersService.js)
- Using Breeze to retrieve and work with data (see app/customersApp/services/customersBreezeService.js). Switch the application to use the Breeze factory by opening app/customersApp/config.js and changing the useBreeze property to true.
- Intercepting HTTP requests to display a custom overlay during Ajax calls (see app/wc.directives/directives/wcOverlay.js)
- Custom animations using the GreenSock library (see app/customersApp/animations/listAnimations.js)
- Creating custom AngularJS animations using CSS (see Content/customersApp/animations.css)
- Card View and List View display of data (see app/customersApp/views/customers/customers.html and app/customersApp/controllers/customers/customersController.js)
- Using AngularJS validation functionality (see app/customersApp/views/customerEdit.html, app/customersApp/controllers/customerEditController.js, and app/customersApp/directives/wcUnique.js)
- Nesting controllers
The structure of the application is shown to the right. The homepage is index.html and is located at the root of the application folder. It defines where application views will be loaded using the ng-view directive and includes script references to AngularJS, AngularJS routing and animation scripts, plus a few others located in the Scripts folder and to custom application scripts located in the app folder.
The app folder contains all of the key scripts used in the application. There are several techniques that can be used for organizing script files but after experimenting with several of them I decided that I prefer content organized by module name (customersApp and wc.directives are examples of module folder names). Within each module folder I follow a convention such as controllers, views, services, etc. Individual features are identified within a convention folder by using additional subfolders such as customers and orders. Doing that helps me find things a lot faster due to mixing the convention/feature approach.
I’m a huge believer in having some conventions in place especially when it comes to team development. Having managed several development teams over the years I learned that consistency across projects is good since people come and go on teams and taking that approach allows files to be categorized and located easily (such as controllers and services). If you’re new to an app (a new hire, production support, a contractor, etc.) and are given a pure feature-based folder structure to work with it can be challenging to find things if you don’t know the app features well since whoever created the folder structure did it based on their way of thinking about the app. If some convention is mixed in with the features it becomes much easier to find things in my opinion and it makes it easier to understand multiple projects – not just one. On the other hand, going with a pure convention-based approach causes challenges with large applications since a controllers folder could have a ton of files in it which is why I like to segregate things by module/convention/feature.
There are MANY different opinions on this so my recommendation is to go with whatever works best for you. I’m definitely not saying this is “the way”…this is my way. Anyone who says, “You’re doing it wrong!” should be ignored because in my experience these are generally the type of close-minded people you run into who aren’t willing to take time to consider alternatives to their approach. Contrary to what some people think, there is no “one right way” to organize scripts and files. As long as the scripts make it down to the client properly (you’ll likely minify and concatenate them anyway to reduce bandwidth and minimize HTTP calls so the structure is irrelevant to the browser), the way you organize them is completely up to you. Here’s what I ended up doing for this application:
- Animation code for some custom animations is located in the app/customersApp/animations folder. In addition to AngularJS animations (which are defined using CSS in Content/animations.css), it also animates the initial customer data load using a 3rd party script called GreenSock.
- Controllers are located in the app/customersApp/controllers folder. Some of the controllers are placed in subfolders based upon the their feature/functionality while others are placed at the root of the controllers folder since they’re more generic:
- The directives folder contains the custom directives created for the application. Directives that can be used across projects are placed in the wc.directives/directives folder which represents the module/convention approach.
- The filters folder (app/customersApp/filters) contains the custom filters created for the application that filter city/state and product information.
- The partials folder contains partial views. This includes things like modal dialogs used in the application.
- The services folder contains AngularJS factories and services used for various purposes in the application. Most of the scripts in this folder provide data/Ajax functionality. Two types of services exist to send and retrieve data to/from a RESTful service. The application uses $http by default but can be switched to use BreezeJS (an alternative way to work with data) by updating the config.js file.
- The views folder contains the different views used in the application. Like the controllers folder, the views are organized into subfolders based on their functionality:
The Customer Manager application (grab it from Github) provides two different options on the back-end including ASP.NET Web API and Node.js. The ASP.NET Web API back-end uses C# and Entity Framework for data access and stores data in SQL Server (LocalDb). The other option on the back-end is Node.js, Express, and MongoDB.
Using the ASP.NET Web API Back-End
To run the application using ASP.NET Web API/SQL Server back-end open the .sln file at the root of the project in Visual Studio 2012 or higher (the free Express 2013 for Web version is fine). Press F5 and a browser will automatically launch and display the application. Under the covers, Entity Framework code first is used to create the database dynamically.
Using the Node.js Back-End
To run the application using the Node.js/MongoDB back-end follow these steps:
- In the CustomerManager directory execute 'npm install' to install Express, MongoDB and Mongoose (package.json).
- Load sample data into MongoDB by performing the following steps:
- Execute 'mongod' to start the MongoDB daemon (this assumes you’ve already downloaded and extracted the MongoDB files).
- Navigate to the CustomerManager/server directory (the one that has initMongoData.js in it) then execute 'mongo' to start the MongoDB shell.
- Enter the following in the mongo shell to load the seed files that handle seeding the database with initial data:
- Alternatively you can navigate to CustomerManager/server and double-click the initMongoData.bat (Windows) or initMongoData.sh (Mac/Linux) file to initialize MongoDB with the data.
The Windows script assumes that MongoDB is installed at c:\mongodb while the Linux/Mac script relies on the fact that you have the monogo executable in the path.
- Start the Node/Express server by navigating to the CustomerManager/server directory and executing node app.js.
- View the application at http://localhost:3000 in your browser.
The Customer Manager application uses the following frameworks and libraries on the front-end:
- AngularJS (with the ngRoute and ngAnimation modules)
- Angular UI BootStrap
- GreenSock Animations
- BreezeJS (optional)
The application uses native AngularJS $http by default to make calls back to the server. However, by going to app/customersApp/services/config.js you can switch from using $http to using BreezeJS. When using BreezeJS you’ll also need to include jQuery and and helper script for $q (these are already loaded in index.html to keep things simple). For more details on what BreezeJS is all about check out my previous post.
The application has several views designed to cover different aspects of AngularJS from editing data to displaying, paging, and filtering data. Here are the main views:
This view provides multiple views of customer data (Card View and List View), supports paging, allows customers to be added or removed, and provides filtering functionality.
Card View (app/customersApp/views/customers/customers.html)
This view displays customer information and allows customers to be edited (by clicking their name), deleted (by clicking the X), or their orders to be viewed.
List View (app/customersApp/views/customers/customers.html)
This view displays customer information in a standard list type of view.
Login View (app/customersApp/views/customers/customerEdit.html)
This view allows the user to login. Security isn’t officially checked on the server for this demo as it just returns a boolean true value but the client-side does have security functionality built-in to show how that could be integrated with AngularJS, how events can be broadcast and handled, and more. Keep in mind that in a “real” application every secured resource on the server would have to be checked for the proper security credentials regardless of what data or information the client has.
Customer Edit View (app/customersApp/views/customers/customerEdit.html)
This view adds some custom AngularJS validation including a custom directive (wcUnique.js) that ensures that the email address being added is unique.
Customer Orders View (app/customersApp/views/customers/customerOrders.html)
This view shows the orders for a specific customer. Orders can be sorted by clicking on the column headings.
Orders View (app/customersApp/views/orders/orders.html)
This view shows orders for all customers and supports paging, filtering, and sorting of the orders.
About View (app/customersApp/views/about.html)
There isn’t much to this view but I listed it for the sake of completeness.
Three custom directives are currently included in the application:
- Unique value directive (app/customersApp/directives/wcUnique.js) – This directive ensures that email addresses entered on the customer edit view are unique. It makes a call back to a service and then calls ngModel.$setValidity() to handle showing or hiding an error message. A post on this directive can be read here.
- Angular Overlay directive (app/wc.directives/directives/wcOverlay.js) – This directive intercepts XmlHttpRequest calls and displays a custom overlay (tracks AngularJS calls as well as jQuery). The directive is available in the application or as a stand-alone directive on Github.
- Menu highlighter directive (app/wc.directives/menuHighlighter.js). This directive is responsible for highlighting menu items as a user clicks on them.
The application includes two custom filters used to filter data in the customers and orders views:
- Name/City/State Filter (app/customersApp/filters/nameCityStateFilter.js)
- Name/Product Filter (app/customersApp/filters/nameProductFilter.js)
I’ll be enhancing the application even more over time and welcome contributions as well. Tony Quinn contributed the initial Node.js/MongoDB code (thanks Tony!) which is very cool to have as a back-end option and several other contributions have been made for testing and the initial version of the menuHighlighter directive. Access the standard application here and a version that has custom routing in it here. Additional information about the custom routing can be found in this post.