An isolated AngularJS frontend container that offers an intuitive folder structure and a guided developer workflow.
- Install Docker.
- Run
docker-compose up
. - That's it.
Windows: Go to http://192.168.99.100:8000 to get started.
Linux/Mac: Go to http://localhost:8000 to get started.
This frontend project was designed to help guide developers in the right direction when making decisions for their website or application.
As a frontend developer, the app
folder should serve as your root directory. Any changes you make will "magically" bundle into the correct folder behind the scenes in your docker container. If there is ever a syntactical error in this process, it will be clearly displayed in the terminal running your docker-compose up
command.
The entrypoint of this application is index.html
. It is a simple template that does the following things:
- Imports
style.css
(bundled .scss files) - Imports
angular.min.js
(minified AngularJS framework) - Imports
bundle.js
(bundled JS files) - Renders content within
<div ng-view></div>
(where your page will render)
The entrypoint for the AngularJS application is index.js
. This file is in charge of loading the router, as well as other dependencies your application may have. It is also the entrypoint for our JS bundler.
The application routes are defined in the routes.js
file. All this file does is render the appropriate component into the ng-view
div, as mentioned before. We'll discuss components in a bit.
The index.scss
file serves as the entrypoint for our styles. Sass allows us to modularize our styles, similar to how AngularJS will enable us to modularize our logic. It will be the entrypoint for our SASS bundler.
Our application will primarily be composed of components (views for the user) and services (view-independent logic).
Whether creating a component or a service, you will be creating an isolated, testable module. This means that all of the code relevant to that module will be neatly stored in a designated folder.
- index.html (main entrypoint)
- index.js (js entrypoint)
- routes.js (route configuration)
- index.scss (css entrypoint)
- shared/ (modules shared across pages)
- example-cmpt/ (shared component)
- index.js (creates module, requires dependencies)
- tpl.html (content to display)
- ctrl.js (exposes and maintains view model)
- style.scss (specifies component-specific styles)
- spec.js (tests all public interfaces of component)
- example-service/ (shared service)
- index.js (creates module, requires dependencies)
- srvc.js (exposes and maintains data model)
- spec.js (tests all public interfaces of service)
- example-cmpt/ (shared component)
- pages/ (pages in the application)
- index.js (creates pages module, requiring page folders)
- welcome-page-cmpt/ (example page)
- (matches example-component structure)
- page-specific-cmpt/ (some component only used in welcome-page)
- (matches example-component structure)
- .../ (other pages)
index.js
angular.module(module.exports = 'exampleCmpt', [])
.component(module.exports, {
template: require('./tpl.html'),
controller: require('./ctrl')
});
tpl.html
<!-- Example Component -->
<h1 class="example-header" ng-bind="$ctrl.name"></h1>
<input type="text" class="example-input" ng-model="$ctrl.name">
ctrl.js
module.exports = [function(){
// All properties of '$ctrl' are exposed to template
var $ctrl = this;
// Available in template
$ctrl.name = 'Jack';
// Private to controller
var hiddenName = 'Jill';
}];
style.scss
example-cmpt {
.example-header {
font-family: Arial;
}
.example-input {
/* Style your input */
}
}
spec.js
describe('exampleCmpt', function() {
var ctrl;
beforeEach(function(){
// Load module
angular.mock.module(require('.'));
// Initialize component
inject(function($componentController) {
ctrl = $componentController('exampleCmpt', {});
});
});
it('has initial name set to John', function(){
expect(ctrl.name).toEqual('John');
});
});
index.js
angular.module(module.exports = 'ExampleSrvc', [])
.service( module.exports, require('./srvc') );
srvc.js
module.exports = [function(){
var srvc = this;
srvc.data = {
person: {
name: 'Jack',
age: 21,
}
};
}];
spec.js
describe('ExampleSrvc', function() {
var ExampleSrvc;
beforeEach(function(){
angular.mock.module(require('.'));
inject(function(_ExampleSrvc_) {
ExampleSrvc = _ExampleSrvc_;
});
});
it('has right name',function(){
expect(ExampleSrvc.data.person.name).toEqual('Jack');
});
});
You may have noticed that the spec.js
files require you to write tests for all public interfaces. For component controllers, this means that if a function is not private to the controller, it needs to be tested to define and validate the purpose of that function. The same rule applies to services.
This rewards privatizing functions that aren't necessary to expose. Unit tests also help keep modules lightweight and focused.