A boilerplate for writing modular Angular 1.X in ES6 using Webpack.
npm install
npm run dev
In your browser, navigate to: http://localhost:8080/
npm run test
npm run build
Copy everything in build/
folder to the server.
Instead of using the great AngularJS Seed Project, which is using Horizontal Modules, provided by the AngularJS team, I am using Vertical Modules, which is highly inspired by MEAN.JS. This helps us divide the project logic into modules that represent individual logical units and scale well for bigger projects that are more maintainable in the long term. The following Module structure and folder structure use demo example to demonstrate how it works.
Application | |||
---|---|---|---|
HomeModule | PostModule | CommentModule | UserModule |
HomeController | PostController | CommentController | UserController |
HomeDirective | PostDirective | CommentDirective | UserDirective |
HomeService | PostService | CommentService | UserService |
HomeFilter | PostFilter | CommentFilter | UserFilter |
HomeRoutes | PostRoutes | CommentRoutes | UserRoutes |
HomeView | PostView | CommentView | UserView |
This structure allows clear separation of functionality and concerns.
|-sass
|-images
|-modules
|---home
|-----config
|-----controller
|-------tests
|-----directive
|-------tests
|-----service
|-------tests
|-----filter
|-------tests
|-----view
|-----sass
|---post
|-----config
|-----controller
|-------tests
|-----directive
|-------tests
|-----service
|-------tests
|-----filter
|-------tests
|-----view
|-----sass
|---comment
|-----config
|-----controller
|-------tests
|-----directive
|-------tests
|-----service
|-------tests
|-----filter
|-------tests
|-----view
|-----sass
|---users
|-----config
|-----controller
|-------tests
|-----directive
|-------tests
|-----service
|-------tests
|-----filter
|-------tests
|-----view
|-----sass
This boilerplate comes with a blog example. I am taking post module as an example to illustrate how the Angular work with Webpack in ES6.
Using JSONPlaceholder for fake Online REST API for Testing and Prototyping
export default class PostController {
constructor($stateParams, $location, post, user, comments) {
this.$stateParams = $stateParams;
this.$location = $location;
this.post = post;
this.user = user;
this.comments = comments;
this.author = 'Yang Zhao';
}
}
PostController.$inject = ['$stateParams', '$location', 'post', 'user', 'comments'];
/*@ngInject*/
export default class Menu {
constructor() {
this.template = require('../view/menu.html');
this.restrict = 'E';
this.scope = {};
this.controller = HomeController;
}
// optional compile function
compile(tElement) {
return this.link.bind(this);
}
// optional link function
link(scope, element, attributes, controller) {
scope.isActive = function(viewLocation) {
return viewLocation === controller.$location.path();
};
}
}
/*@ngInject*/
export default class PostService {
constructor($http) {
this.$http = $http;
}
getPosts() {
return this.$http.get('http://jsonplaceholder.typicode.com/posts');
}
getPost(postId) {
return this.$http.get('http://jsonplaceholder.typicode.com/posts/' + postId);
}
getUser(usreId) {
return this.$http.get('http://jsonplaceholder.typicode.com/users/' + usreId);
}
getComments(postId) {
return this.$http.get('http://jsonplaceholder.typicode.com/posts/' + postId + '/comments');
}
}
export default () => {
return (input) => {
return input + ' <a href="mailto:' + input + '"><i class="fa fa-reply" aria-hidden="true"></i></a>';
}
}
/*@ngInject*/
export default ($stateProvider) => {
$stateProvider
.state('listPosts', {
url: '/posts',
template: require('../view/posts.html'),
controller: 'PostsController',
controllerAs: 'posts',
resolve: {
posts: (PostService) => {
return PostService.getPosts().then((object) => {
return object.data;
});
}
}
})
.state('post', {
url: '/posts/:postId',
template: require('../view/post.html'),
controller: 'PostController',
controllerAs: 'post',
resolve: {
post: (PostService, $stateParams) => {
return PostService.getPost($stateParams.postId).then((object) => {
return object.data;
});
},
user: (PostService, post) => {
return PostService.getUser(post.userId).then((object) => {
return object.data;
});
},
comments: (PostService, post) => {
return PostService.getComments(post.id).then((object) => {
return object.data;
});
}
}
});
}
describe('PostController', () => {
let controller;
let service;
beforeEach(() => {
angular.mock.module(post);
angular.mock.inject(($injector, $controller, $stateParams, $location) => {
service = $injector.get('PostService');
controller = $controller('PostController', {
$stateParams: $stateParams,
$location: $location,
post: service,
user: service,
comments: service
});
})
});
it('should have correct author name', () => {
assert.equal(controller.author, 'Yang Zhao', 'PostController has correct author name');
});
});
import routes from './config/routes';
import PostService from './service/service';
import PostController from './controller/post';
import PostDirective from './directive/post';
import PostFilter from './filter/post';
export default angular.module('post')
.config(routes)
.service('PostService', PostService)
.controller('PostController', PostController)
.directive('PostDirective', () => new PostDirective())
.filter('PostFilter', PostFilter)
.name;
import home from './modules/home';
import post from './modules/post';
import comment from './modules/comment';
import user from './modules/user';
/*@ngInject*/
angular.module('app', [home, post, comment, user]);