├── .editorconfig
├── .eslintrc
├── .gitignore
├── LICENSE.md
├── README.md
├── docs
├── assets
│ ├── css
│ │ ├── angular-material-boilerplate.css
│ │ └── angular-material.min.css
│ ├── images
│ │ ├── favicon.ico
│ │ └── user-profile.png
│ └── scripts
│ │ ├── angular-material-boilerplate.js
│ │ ├── bundle.js
│ │ └── templates.js
└── index.html
├── gulp_config.js
├── gulpfile.js
├── karma.conf.js
├── package.json
├── protractor.conf.js
├── protractor.config.js
├── src
├── app
│ ├── about
│ │ ├── about.contoller.spec.js
│ │ ├── about.controller.e2e.js
│ │ ├── about.controller.js
│ │ ├── about.module.js
│ │ ├── about.style.scss
│ │ └── about.view.html
│ ├── app.js
│ ├── layout
│ │ ├── layout.controller.js
│ │ ├── layout.module.js
│ │ ├── layout.style.scss
│ │ └── layout.view.html
│ ├── shared
│ │ ├── directives
│ │ │ └── backButton.js
│ │ ├── services
│ │ │ └── Utils.js
│ │ ├── shared.module.js
│ │ └── styles
│ │ │ ├── _color-palette.scss
│ │ │ ├── _mixins.scss
│ │ │ ├── overrides.scss
│ │ │ └── shared.scss
│ ├── sidenav
│ │ ├── sidenav.controller.js
│ │ ├── sidenav.module.js
│ │ ├── sidenav.service.js
│ │ ├── sidenav.style.scss
│ │ └── sidenav.view.html
│ ├── todo
│ │ ├── todo.controller.e2e.js
│ │ ├── todo.controller.js
│ │ ├── todo.controller.spec.js
│ │ ├── todo.module.js
│ │ ├── todo.service.js
│ │ ├── todo.style.scss
│ │ └── todo.view.html
│ └── toolbar
│ │ ├── toolbar.controller.js
│ │ ├── toolbar.module.js
│ │ ├── toolbar.style.scss
│ │ └── toolbar.view.html
├── assets
│ └── images
│ │ ├── favicon.ico
│ │ └── user-profile.png
└── index.html
├── vendor.config.js
└── vendor_files.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs.
2 | # More information at http://EditorConfig.org
3 |
4 | # No .editorconfig files above the root directory
5 | root = true
6 |
7 | [*]
8 | indent_style = space
9 | end_of_line = lf
10 | charset = utf-8
11 | trim_trailing_whitespace = true
12 | insert_final_newline = true
13 | indent_size = 2
14 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["angular"],
3 |
4 | "env": {
5 | "browser": true,
6 | "node": true,
7 | "jasmine" : true,
8 | "phantomjs" : true
9 | },
10 |
11 | "rules": {
12 | "strict": 0,
13 | "no-alert": 2,
14 | "no-caller": 2,
15 | "no-bitwise": 0,
16 | "no-console": 1,
17 | "no-debugger": 1,
18 | "no-empty": 1,
19 | "no-eval": 2,
20 | "no-ex-assign": 1,
21 | "no-floating-decimal": 0,
22 | "no-implied-eval": 1,
23 | "no-with": 1,
24 | "no-fallthrough": 1,
25 | "no-unreachable": 2,
26 | "no-underscore-dangle": 0,
27 | "no-undef-init": 1,
28 | "no-octal": 1,
29 | "no-obj-calls": 1,
30 | "no-new-wrappers": 1,
31 | "no-new": 1,
32 | "no-new-func": 1,
33 | "no-native-reassign": 1,
34 | "no-plusplus": 0,
35 | "no-delete-var": 1,
36 | "no-return-assign": 1,
37 | "no-new-object": 1,
38 | "no-label-var": 1,
39 | "no-ternary": 0,
40 | "no-self-compare": 0,
41 | "smarter-eqeqeq": 0,
42 | "brace-style": 0,
43 | "camelcase": 1,
44 | "curly": 1,
45 | "dot-notation": 1,
46 | "eqeqeq": 2,
47 | "new-parens": 1,
48 | "guard-for-in": 0,
49 | "radix": 0,
50 | "new-cap": 0,
51 | "quote-props": 0,
52 | "semi": 2,
53 | "use-isnan": 1,
54 | "quotes": [1, "single"],
55 | "max-params": [0, 3],
56 | "max-statements": [0, 10],
57 | "complexity": [0, 11],
58 | "wrap-iife": 1,
59 | "no-multi-str": 1,
60 | "no-multi-spaces": 1,
61 | "key-spacing": 0,
62 | "no-unused-vars": 2,
63 | "no-shadow": 0,
64 | "no-undef": 1,
65 | "comma-spacing": 1,
66 | "no-use-before-define": 0,
67 |
68 | // ANGULAR RULES
69 | "angular/controller-as": 0,
70 | "angular/typecheck-object": 0,
71 | "angular/timeout-service": 0,
72 | "angular/controller-as-vm": 1,
73 | "angular/controller-as-route": 0,
74 | "angular/controller-name": [1, "/[A-Z].*Controller|Ctrl$/"], //e.g HomeController or HomeCtrl
75 | "angular/on-watch": 0,
76 | "angular/di": 0,
77 | "angular/log": 0
78 | },
79 |
80 | "globals": {
81 | "document": true,
82 | "window": true,
83 | "before": true,
84 | "beforeEach": true,
85 | "inject": true,
86 | "$log": false,
87 | "_": true,
88 | "after": true,
89 | "afterEach": true,
90 | "angular": true,
91 | "require": true,
92 | "module": true,
93 | "define": true,
94 | "brackets": true,
95 | "jQuery": true,
96 | "$": true,
97 | "Mustache": true,
98 | "CallManager": true,
99 | "getEncodedServerAddr": true,
100 | "log4javascript": true,
101 | "console": true,
102 | "atmosphere": true,
103 | "jasmine": true,
104 | "exports": true,
105 | "describe": true,
106 | "browser": true,
107 | "it": true,
108 | "by": true,
109 | "waits": true,
110 | "expect": true,
111 | "runs": true,
112 | "element": true,
113 | "waitsFor": true,
114 | "xit": false,
115 | "xdescribe": false,
116 | "spyOn": false
117 | }
118 |
119 | }
120 |
121 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .DS_Store
3 | Thumbs.db
4 | *.log
5 | *.sass-cache
6 | *.orig
7 | *.diff
8 | junit-report.xml
9 | build/
10 | public
11 | production/
12 | node_modules/
13 | reports/
14 | coverage/
15 | vendor
16 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Cathal Mac Donnacha
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # angular-material-boilerplate
2 |
3 | A straightforward and well structured boilerplate based on Google's Angular Material project.
4 |
5 | ## Features
6 | * Live reload
7 | * Organised folder structure
8 | * Minified CSS, HTML and JS build files
9 | * Minified images.
10 | * SASS support
11 | * Responsive layout
12 | * Mobile ready
13 | * [Material Design Icons](https://material.io/icons/) icons
14 | * Support for Unit & E2E Testing
15 | * Unit Test reporting
16 | * [ESLint](http://eslint.org/) code analysis tool.
17 | * [Jasmine](http://jasmine.github.io/2.3/introduction.html) testing framework
18 | * [Karma](http://karma-runner.github.io/0.13/index.html) test runner
19 | * [Protractor](https://angular.github.io/protractor/#/) end-to-end test framework
20 |
21 | ## Live Demo
22 | [Check out the live demo](http://cmacdonnacha.github.io/angular-material-boilerplate/)
23 |
24 | ## Setup
25 | 1. Install [Git](https://git-scm.com/downloads) and [NodeJS](http://nodejs.org/)
26 | 2. `git clone https://github.com/cmacdonnacha/angular-material-boilerplate.git myApp`
27 | 3. `cd myApp`
28 | 4. `npm install`
29 |
30 | ## Quick Usage
31 | * `npm start` : Creates a development build folder called `public` and serves it on: [`http://localhost:3000/`](http://localhost:3000/)
32 | * `npm start --production` : Creates a production ready folder called `production` and serves it on: [`http://localhost:4000/`](http://localhost:4000/)
33 | * `npm test` : Runs code checks, unit tests and E2E tests.
34 | * `npm run unit` : Runs unit tests only.
35 | * `npm run e2e` : Runs E2E tests only.
36 | * `npm run eslint` : Runs an ESLint code check only.
37 | * `npm run coverage` : Creates and serves the unit test coverage reports on: [`http://localhost:5000/`](http://localhost:5000/)
38 | * `npm run build` : Creates the `public` build folder but doesn't serve it.
39 | * `npm run build --production` : Creates the `production` build folder but doesn't serve it.
40 |
41 | ## Project Structure
42 | This project follows a **"Folders-by-Feature"** structure very similar to [John Papa's Styleguide](https://github.com/johnpapa/angular-styleguide#application-structure). From the screenshot below you can see that there are 6 separate features, a folder for each one.
43 | Each feature is treated as a mini Angular app. This structure allows us developers to easily locate code and identify what each file represents at a glance.
44 | By retaining this structure the project is much more manageable as it grows.
45 |
46 | 
47 |
48 | * The `app` folder contains the following individual features:
49 | * `about`: Contains the about page related files.
50 | * `layout`: The high level layout container which stitches it all together.
51 | * `shared`: Contains all shared services, directives, styles etc. used across the entire app.
52 | * `sidenav`: Contains the sidenav related files.
53 | * `todo`: Contains the todo page related files.
54 | * `toolbar`: Contains the toolbar related files.
55 |
56 | * The `assets` folder contains all globally used images.
57 |
58 | #### Adding a new feature folder
59 | In the future I hope to automate the ability to create a new feature folder but for now, check out the [wiki](https://github.com/cmacdonnacha/angular-material-boilerplate/wiki/How-to-add-your-own-feature-folder) to see how you can go about adding one manually.
60 |
61 | ## Troubleshooting
62 | Even crème de menthe projects have their issues. Here are some problems you may face along with some suggestions on how to resolve them:
63 |
64 | #### 1. Issue: I'm getting the following error: ***"Error: EPERM or operation not permitted or permission denied"***
65 | This can happen when trying to delete a folder that's already in use. For example when running `npm test` while the `npm start` task is already running.
66 |
67 | **Suggestion:**
68 | 1. Stop any tasks that are already running and try again.
69 |
70 |
71 |
72 | #### 2. Issue: I'm getting the following error when running the `npm test` task: ***"No selenium server jar found at the specified location"***
73 |
74 | **Suggestion:**
75 | 1. Run the following command and try again: `npm run webdriver-update`
76 |
77 |
78 |
79 | #### 3. Issue: I'm getting the following error while running the `npm start` task: ***"Error: ENOENT: no such file or directory, scandir 'C:....node_modeules\node-sass\vendor"***
80 | This can happen if you have changed your environment since first installing node-sass or if you are running an old version of node-sass.
81 |
82 | **Suggestion:**
83 |
84 | 1. Run this command: `npm rebuild node-sass`
85 |
86 |
87 |
88 | #### 4. Issue: None of the above have helped
89 | **Suggestion:**
90 |
91 | This project has been tested with the following tools:
92 | * **NodeJs:** 6.9.2
93 | * **Npm:** 3.10.9
94 |
95 | If you are running into issues while installing node packages then ensure you have the versions above installed.
96 |
97 |
98 |
99 | ## Contribute
100 | Believe it or not, **angular-material-boilerplate** is not perfect. If you want to improve it somehow then by all means go ahead and create a pull request :-)
101 |
102 | ## TODO
103 | - Add source maps
104 | - Automate ability to add new feature folder
105 | - Switch to Material SVG icons
106 |
--------------------------------------------------------------------------------
/docs/assets/css/angular-material-boilerplate.css:
--------------------------------------------------------------------------------
1 | /**
2 | * angular-material-boilerplate v1.0.0
3 | * Copyright (c) 2017 Cathal Mac Donnacha
4 | * Licensed under MIT
5 | */
6 | body{background-color:#d3d3d3}.todo-page md-checkbox{max-width:20px}.sidenav md-sidenav{max-width:250px}.sidenav .profile-details{margin:10px 0 10px 0}.sidenav .profile-img{width:40%;height:40%;border-radius:50%}@media (min-width:600px){.sidenav .sidenav-profile{margin-top:10px}}.sidenav .menu-items .selected{color:#03a9f4}.img-circle{width:50%;height:50%;border-radius:50%}
--------------------------------------------------------------------------------
/docs/assets/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacdonnacha/angular-material-boilerplate/3d769c517df271389b5e4362c935ab73e03f9331/docs/assets/images/favicon.ico
--------------------------------------------------------------------------------
/docs/assets/images/user-profile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacdonnacha/angular-material-boilerplate/3d769c517df271389b5e4362c935ab73e03f9331/docs/assets/images/user-profile.png
--------------------------------------------------------------------------------
/docs/assets/scripts/angular-material-boilerplate.js:
--------------------------------------------------------------------------------
1 | /**
2 | * angular-material-boilerplate v1.0.0
3 | * Copyright (c) 2017 Cathal Mac Donnacha
4 | * Licensed under MIT
5 | */
6 | angular.module("angular-material-boilerplate",["ngAnimate","ngAria","ngMaterial","ui.router","ngResource","templates","app.shared","app.layout","app.toolbar","app.sidenav","app.todo","app.about"]).config(["$mdThemingProvider",function(t){t.theme("default").primaryPalette("indigo").accentPalette("purple",{default:"200"})}]).run(["$state",function(t){t.go("todo")}]),angular.module("about.controllers.AboutCtrl",[]).controller("AboutCtrl",function(){var t=this;t.message="And here is where I tell you all about myself."}),angular.module("app.about",["ui.router","about.controllers.AboutCtrl"]).config(["$stateProvider",function(t){t.state("about",{url:"/about",templateUrl:"about/about.view.html"})}]),angular.module("app.shared",["shared.directives.backButton","shared.services.Utils"]),angular.module("layout.controllers.LayoutCtrl",[]).controller("LayoutCtrl",function(){}),angular.module("app.layout",["ui.router","layout.controllers.LayoutCtrl"]).config(["$stateProvider",function(t){t.state("layout",{url:"/layout",templateUrl:"layout/layout.view.html"})}]),angular.module("todo.controllers.ToDoCtrl",[]).controller("ToDoCtrl",["ToDo",function(t){var e=this;e.toDoItems=t.getItems}]),angular.module("app.todo",["ui.router","todo.controllers.ToDoCtrl","todo.services.ToDo"]).config(["$stateProvider",function(t){t.state("todo",{url:"/todo",templateUrl:"todo/todo.view.html"})}]),angular.module("todo.services.ToDo",[]).factory("ToDo",function(){var t=[{title:"Make a todo list",completed:!0},{title:"Check off first item on todo list",completed:!0},{title:"Realise you've have already accomplished two things on the list",completed:!0},{title:"Reward yourself with a nice long nap",completed:!1}],e={getItems:t};return e}),angular.module("sidenav.controllers.SideNavCtrl",[]).controller("SideNavCtrl",["$mdSidenav","$state","SideNav",function(t,e,o){function l(t){a.selected=t,r(),e.go(t.route)}function r(){t("left").close()}var a=this;a.selectMenuItem=l,a.closeSideNav=r,a.menuItems=o.getMenuItems,a.selected=a.menuItems[0]}]),angular.module("app.sidenav",["sidenav.controllers.SideNavCtrl","sidenav.services.SideNav"]),angular.module("sidenav.services.SideNav",[]).factory("SideNav",function(){var t=[{name:"To-Do",icon:"",route:"todo"},{name:"About",icon:"",route:"about"}],e={getMenuItems:t};return e}),angular.module("toolbar.controllers.ToolbarCtrl",[]).controller("ToolbarCtrl",["$state","$scope","$mdSidenav",function(t,e,o){e.openSideNav=function(){o("left").open()}}]),angular.module("app.toolbar",["toolbar.controllers.ToolbarCtrl"]),angular.module("shared.services.Utils",[]).factory("Utils",function(){function t(t){return!(null!==t&&!angular.isUndefined(t))}function e(t){return!!(angular.isUndefined(t)||t.trim().length<=0)}function o(t){return null===t||!!(angular.isUndefined(t)||t.trim().length<=0)}function l(t){return null===t||t.trim().length<=0}var r={isNullOrUndefined:t,isUndefinedOrWhitespace:e,isNullOrWhitespace:l,isNullOrUndefinedOrWhitespace:o};return r}),angular.module("shared.directives.backButton",[]).directive("backButton",["$window",function(t){return{restrict:"A",link:function(e,o){o.bind("click",function(){t.history.back()})}}}]);
--------------------------------------------------------------------------------
/docs/assets/scripts/templates.js:
--------------------------------------------------------------------------------
1 | /**
2 | * angular-material-boilerplate v1.0.0
3 | * Copyright (c) 2017 Cathal Mac Donnacha
4 | * Licensed under MIT
5 | */
6 | angular.module('templates', []).run(['$templateCache', function($templateCache) {$templateCache.put('about/about.view.html','
\n
About
\n
{{ about.message }}
\n
\n\n');
7 | $templateCache.put('layout/layout.view.html','\n\n\n\n\n\n');
8 | $templateCache.put('todo/todo.view.html','\n
To-Do
\n \n \n \n {{ item.title }}\n \n \n\n');
9 | $templateCache.put('sidenav/sidenav.view.html','\n
\n\n \n \n \n \n \n close\n \n
\n\n \n \n

\n
\n Joe Bloggs\n Director\n
\n
\n \n\n \n \n\n \n
\n');
10 | $templateCache.put('toolbar/toolbar.view.html','\n \n Angular Material Boilerplate
\n\n\n');}]);
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Angular Material Boilerplate
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/gulp_config.js:
--------------------------------------------------------------------------------
1 | var pkg = require('./package.json');
2 | var isProduction = 'production' === process.env.NODE_ENV;
3 |
4 | module.exports = {
5 | scripts: {
6 | sources: [
7 | 'src/app/**/*.js',
8 | '!src/app/**/*.spec.js',
9 | '!src/app/**/*.e2e.js'
10 | ],
11 | destinationFolder: isProduction ? 'production/assets/scripts' : 'public/assets/scripts',
12 | destinationName: pkg.name + '.js'
13 | },
14 | sass: {
15 | sources : ['src/app/**/*.scss'],
16 | destinationFolder: isProduction ? 'production/assets/css' : 'public/assets/css',
17 | destinationName: pkg.name + '.css'
18 | },
19 | browserify: {
20 | sources : ['./vendor_files.js'],
21 | destinationFolder: isProduction ? 'production/assets/scripts' : 'public/assets/scripts',
22 | destinationName: 'bundle.js'
23 | },
24 | templates: {
25 | sources : ['src/app/**/*.html'],
26 | destinationFolder: isProduction ? 'production/assets/scripts' : 'public/assets/scripts',
27 | },
28 | images: {
29 | sources : ['src/assets/images/*'],
30 | destinationFolder : isProduction ? 'production/assets/images' : 'public/assets/images'
31 | },
32 | vendor: {
33 | sources : ['node_modules/angular-material/angular-material.min.css'],
34 | destinationFolder: isProduction ? 'production/assets/css' : 'public/assets/css',
35 | },
36 | assets: {
37 | sources : ['src/assets/images/favicon.ico'],
38 | destinationFolder : isProduction ? 'production/assets/images' : 'public/assets/images'
39 | },
40 | index: {
41 | sources : ['src/index.html'],
42 | destinationFolder : isProduction ? 'production' : 'public'
43 | },
44 | serve: {
45 | baseDir : isProduction ? 'production' : 'public',
46 | port : isProduction ? 4000 : 3000
47 | },
48 | coverage: {
49 | destinationFolder: 'reports/coverage/lcov-report',
50 | port: 5000
51 | },
52 | protractor: {
53 | sources : ['src/app/**/*.e2e.js']
54 | },
55 | banner: ['/**',
56 | ' * <%= pkg.name %> v<%= pkg.version %>',
57 | ' * Copyright (c) <%= new Date().getFullYear() %> <%= pkg.author %>',
58 | ' * Licensed under <%= pkg.license %>',
59 | ' */',
60 | ''].join('\n')
61 | ,
62 | isProduction : isProduction
63 | };
64 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp'),
2 | browserify = require('browserify'),
3 | source = require('vinyl-source-stream'),
4 | buffer = require('vinyl-buffer'),
5 | browserSync = require('browser-sync'),
6 | del = require('del'),
7 | karma = require('karma').Server;
8 |
9 | var $ = require('gulp-load-plugins')();
10 | var config = require('./gulp_config.js');
11 | var pkg = require('./package.json');
12 |
13 | /**
14 | * Default tasks
15 | */
16 | gulp.task('default', ['serve']);
17 | gulp.task('build', ['clean', 'scripts', 'sass', 'templates', 'minify:images', 'browserify', 'copy:vendor:css', 'copy:index', 'copy:assets']);
18 | gulp.task('test', ['eslint', 'unit', 'e2e']);
19 |
20 | /**
21 | * Tasks needed to update/run protractor.
22 | */
23 | gulp.task('webdriver-update', $.protractor.webdriver_update);
24 | gulp.task('webdriver-start', $.protractor.webdriver_start);
25 |
26 |
27 | /**
28 | * Watches for file changes. Reloads the browser if a change is detected.
29 | */
30 | gulp.task('watch', function() {
31 | gulp.watch('src/**/*.scss', ['sass']);
32 | gulp.watch('src/**/*.html', ['templates', browserSync.reload]);
33 | gulp.watch('src/**/*.js', ['scripts', 'unit', 'eslint', browserSync.reload]);
34 | gulp.watch('src/index.html', ['copy:index', browserSync.reload]);
35 | });
36 |
37 | /**
38 | * Serves and watches the build folder.
39 | */
40 | gulp.task('serve', ['build', 'watch'], function() {
41 | browserSync({
42 | server: {
43 | baseDir: config.serve.baseDir // Serve the chosen folder.
44 | },
45 | open: false, // Stop the browser from opening a new window automatically.
46 | port: config.serve.port // Decide which port to serve on. Default is 3000 for development and 4000 for production.
47 | })
48 | });
49 |
50 | /**
51 | * Builds, minifies and concatenates javascript files into one single file.
52 | */
53 | gulp.task('scripts', function() {
54 | return gulp.src(config.scripts.sources) // Get all js files
55 | .pipe($.concat(config.scripts.destinationName)) // Concatenate them into one file
56 | .pipe($.ngAnnotate()) // Inject angular dependencies and prevent minfier from mangling them.
57 | .pipe(config.isProduction ? $.uglify() : $.util.noop()) // Minify the file if it's for production
58 | .pipe($.header(config.banner, { pkg : pkg } )) // Add a comment to the top of the file to include our package info (copyright etc.)
59 | .pipe(gulp.dest(config.scripts.destinationFolder)); // Output it to the destination folder
60 | });
61 |
62 | /**
63 | * Builds, minifies and concatenates sass files into one single file.
64 | */
65 | gulp.task('sass', function() {
66 | return gulp.src(config.sass.sources) // Get all sass files
67 | .pipe($.sass()) // Pass them through gulp-sass to convert it to css
68 | .pipe($.concat(config.sass.destinationName)) // Concatenate them into one file
69 | .pipe(config.isProduction ? $.cleanCss():$.util.noop()) // Minify the file if this is for production
70 | .pipe($.header(config.banner, { pkg : pkg } )) // Add a comment to the top of the file to include our package info (copyright etc.)
71 | .pipe(gulp.dest(config.sass.destinationFolder)) // Output it to the destination folder
72 | .pipe(browserSync.reload({ // Reload browser sync
73 | stream: true
74 | }));
75 | });
76 |
77 | /**
78 | * Concatenates and registers AngularJS templates in the $templateCache.
79 | */
80 | gulp.task('templates', function () {
81 | return gulp.src(config.templates.sources) // Get all html files
82 | .pipe($.angularTemplatecache({ // Pass them through angularTemplatecache
83 | standalone: true // Create a new AngularJS module dynamically so we don't have to
84 | }))
85 | .pipe($.header(config.banner, { pkg : pkg } )) // Add a comment to the top of the file to include our package info (copyright etc.)
86 | .pipe(gulp.dest(config.templates.destinationFolder)); // Output it to the destination folder. Default file name is 'templates.js'.
87 | });
88 |
89 | /**
90 | * Minifies supported images found in the assets folder.
91 | */
92 | gulp.task('minify:images', function() {
93 | return gulp.src(config.images.sources) // Get all images
94 | .pipe(config.isProduction ? $.imagemin():$.util.noop()) // Pass them imageMin in order to reduce their file sizes
95 | .pipe(gulp.dest(config.images.destinationFolder)); // Output them to the destination folder.
96 | });
97 |
98 | /**
99 | * Performs an eslint code check analysis.
100 | */
101 | gulp.task('eslint', function () {
102 | return gulp.src(config.scripts.sources) // Get all js files
103 | .pipe($.eslint()) // Run them through eslint to try and find error in the code
104 | .pipe($.eslint.format()) // Output the results to the console.
105 | .pipe($.eslint.failAfterError()); // Stop a stream if an ESLint error has been reported, but wait for all files to be processed first.
106 | });
107 |
108 | /**
109 | * Runs unit tests using Karma
110 | */
111 | gulp.task('unit', function(done) {
112 | karma.start({ // Start the Karma server
113 | configFile: __dirname + '/karma.conf.js' // Load the karma config file
114 | }, function() {
115 | done();
116 | });
117 | });
118 |
119 | /**
120 | * Bundles node dependencies (Angular, ui-router, Angular Material etc.) into one single file.
121 | */
122 | gulp.task('browserify', function() {
123 | return browserify(config.browserify.sources) // Get all node module files
124 | .bundle() // Bundle them all into one file
125 | .pipe(source(config.browserify.destinationName)) // Pass is through vinyl-source-stream to rename it
126 | .pipe(config.isProduction ? buffer() : $.util.noop()) // Pass is through vinyl-buffer so that 'uglify' can use it
127 | .pipe(config.isProduction ? $.uglify() : $.util.noop()) // Minify the file if it's for production
128 | .pipe(gulp.dest(config.browserify.destinationFolder)); // Output it to the destination folder
129 | });
130 |
131 | /**
132 | * Copies the index.html file into the build folder
133 | */
134 | gulp.task('copy:index', function() {
135 | return gulp.src(config.index.sources) // Get index.html file
136 | .pipe(gulp.dest(config.index.destinationFolder)); // Copy it to the destination folder
137 | });
138 |
139 | /**
140 | * Copies the contents of assets folder into the build folder.
141 | */
142 | gulp.task('copy:assets', function() {
143 | return gulp.src(config.assets.sources) // Get assets (e.g favicon, logo, shared images etc.)
144 | .pipe(gulp.dest(config.assets.destinationFolder)); // Copy them to the destination folder (e.g assets)
145 | });
146 |
147 | /**
148 | * Copies the vendor css files (i.e angular-material.css) into the build folder
149 | */
150 | gulp.task('copy:vendor:css', function() {
151 | return gulp.src(config.vendor.sources) // Get all vendor css files (e.g angular-material.css)
152 | .pipe(gulp.dest(config.vendor.destinationFolder)); // Copy it to the destination folder
153 | });
154 |
155 | /**
156 | * Deletes the build folder.
157 | */
158 | gulp.task('clean', function() {
159 | return del.sync([
160 | config.serve.baseDir, // Delete the destination folder (may be 'public' or 'production')
161 | config.coverage.destinationFolder // Delete the code coverage 'reports' folder
162 | ]);
163 | });
164 |
165 | /**
166 | * Runs unit test code coverage.
167 | */
168 | gulp.task('coverage', ['unit'], function() {
169 | browserSync({
170 | server: {
171 | baseDir: config.coverage.destinationFolder // Serve code coverage reports folder
172 | },
173 | port: config.coverage.port, // On the selected port.
174 | open: false // Stop the browser from opening a new window automatically.
175 | })
176 | });
177 |
178 | /**
179 | * Runs end-to-end tests using protractor.
180 | */
181 | gulp.task('e2e', ['serve:e2e'], function(done) {
182 | gulp.src(config.protractor.sources) // Get all e2e tests
183 | .pipe($.protractor.protractor({
184 | configFile: 'protractor.conf.js' // Load the protractor config file
185 | }))
186 | .on('error', function (err) { // Stop the task if an error occurs
187 | // Stop the task
188 | process.exit(0);
189 | done();
190 | })
191 | .on('end', function () { // Stop the task if complete
192 | // Stop the task
193 | process.exit(0);
194 | done();
195 | });
196 | });
197 |
198 | /**
199 | * Serves the app using browserSync so that end-to-end tests can run.
200 | */
201 | gulp.task('serve:e2e', ['build', 'webdriver-start'], function() {
202 | browserSync({
203 | server: {
204 | baseDir: config.serve.baseDir // Serve the chosen folder. Default port is 3000.
205 | },
206 | open: false // Stop the browser from opening a new window automatically.
207 | })
208 | });
209 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 |
4 | // Base path that will be used to resolve all patterns (eg. files, exclude)
5 | basePath: '',
6 |
7 |
8 | // Frameworks to use
9 | frameworks: ['jasmine'],
10 |
11 |
12 | // Plugins to use
13 | plugins : ['karma-jasmine', 'karma-phantomjs-launcher', 'karma-chrome-launcher', 'karma-coverage'],
14 |
15 |
16 | // List of files / patterns to load into the browser during testing.
17 | files: [
18 | 'node_modules/angular/angular.js',
19 | 'node_modules/angular-mocks/angular-mocks.js',
20 | 'node_modules/angular-ui-router/release/angular-ui-router.js',
21 | 'src/app/**/*.js',
22 | 'src/app/**/*.spec.js'
23 | ],
24 |
25 |
26 | // List of files to exclude
27 | exclude: [
28 | 'src/app/**/*.e2e.js'
29 | ],
30 |
31 |
32 | // Preprocessors matching files before serving them to the browser.
33 | // Available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
34 | preprocessors: {
35 | // Source files that you want to generate coverage for.
36 | // Do not include tests or libraries (these files will be instrumented by Istanbul)
37 | 'src/**/*.js': ['coverage']
38 | },
39 |
40 |
41 | // Configure the reporter
42 | coverageReporter: {
43 | type : 'lcov',
44 | dir : 'reports',
45 | subdir: 'coverage'
46 | },
47 |
48 |
49 | // Test results reporter to use.
50 | // Possible values: 'dots', 'progress', 'coverage.
51 | // Available reporters: https://npmjs.org/browse/keyword/karma-reporter
52 | reporters: ['progress', 'coverage'],
53 |
54 |
55 | // Enable / disable colors in the output (reporters and logs)
56 | colors: true,
57 |
58 |
59 | // Level of logging
60 | // Possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
61 | logLevel: config.LOG_DISABLE,
62 |
63 |
64 | // Enable / disable watching file and executing tests whenever any file changes
65 | autoWatch: true,
66 |
67 |
68 | // The list of browsers to launch to test on:
69 | // Chrome, ChromeCanary, Firefox, Opera, Safari, PhantomJS
70 | browsers: ['PhantomJS'],
71 |
72 | // Continuous Integration mode
73 | // if true, Karma captures browsers, runs the tests and exits
74 | singleRun: true
75 | });
76 | };
77 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-material-boilerplate",
3 | "version": "1.0.0",
4 | "author": "Cathal Mac Donnacha",
5 | "description": "A straightforward and well structured boilerplate based on Google's Angular Material project.",
6 | "license": "MIT",
7 | "homepage": "https://github.com/cmacdonnacha/angular-material-boilerplate",
8 | "scripts": {
9 | "start": "gulp",
10 | "test": "gulp test",
11 | "unit": "gulp unit",
12 | "e2e": "gulp e2e",
13 | "eslint": "gulp eslint",
14 | "coverage": "gulp coverage",
15 | "build": "gulp build",
16 | "postinstall": "gulp webdriver-update"
17 | },
18 | "dependencies": {
19 | "angular": "~1.8.0",
20 | "angular-animate": "~1.5.0",
21 | "angular-aria": "~1.5.0",
22 | "angular-local-storage": "^0.5.2",
23 | "angular-material": "^1.1.3",
24 | "angular-resource": "^1.6.2",
25 | "angular-ui-router": "^0.4.2",
26 | "material-design-icons": "^3.0.1",
27 | "underscore": "^1.8.3"
28 | },
29 | "devDependencies": {
30 | "angular-mocks": "^1.6.2",
31 | "async": "^2.1.2",
32 | "browser-sync": "^2.18.8",
33 | "browserify": "^14.1.0",
34 | "del": "^2.2.2",
35 | "eslint-plugin-angular": "^1.5.0",
36 | "gulp": "^3.9.1",
37 | "gulp-angular-templatecache": "^2.0.0",
38 | "gulp-clean-css": "^3.0.2",
39 | "gulp-concat": "^2.6.1",
40 | "gulp-eslint": "^3.0.1",
41 | "gulp-header": "^1.8.8",
42 | "gulp-imagemin": "^3.1.1",
43 | "gulp-jshint": "^2.0.4",
44 | "gulp-load-plugins": "^1.5.0",
45 | "gulp-ng-annotate": "^2.0.0",
46 | "gulp-protractor": "^3.0.0",
47 | "gulp-rename": "^1.2.2",
48 | "gulp-sass": "^3.1.0",
49 | "gulp-sourcemaps": "^2.4.1",
50 | "gulp-uglify": "^2.0.0",
51 | "gulp-util": "^3.0.7",
52 | "jasmine-core": "^2.5.2",
53 | "jshint": "^2.9.4",
54 | "jshint-stylish": "^2.2.1",
55 | "karma": "^1.4.1",
56 | "karma-chrome-launcher": "^2.0.0",
57 | "karma-coverage": "^1.1.1",
58 | "karma-jasmine": "^1.1.0",
59 | "karma-phantomjs-launcher": "^1.0.2",
60 | "vinyl-buffer": "^1.0.0",
61 | "vinyl-source-stream": "^1.1.0"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/protractor.conf.js:
--------------------------------------------------------------------------------
1 | var config = require('./gulp_config.js');
2 |
3 | exports.config = {
4 | // The address of a running selenium server. Comment this out if you want the selenium server tp start automatically.
5 | //'seleniumAddress': 'http://localhost:4444/wd/hub',
6 |
7 | // The base url your app is served on.
8 | baseUrl: 'http://localhost:3000/',
9 |
10 | // Jasmine is fully supported as a test and assertion framework.
11 | framework: 'jasmine',
12 |
13 | // Capabilities to be passed to the webdriver instance.
14 | 'capabilities': {
15 | 'browserName': 'chrome'
16 | },
17 |
18 | // Spec patterns are relative to the location of this config.
19 | specs: [
20 | config.protractor.sources
21 | ],
22 |
23 | stackTrace: false,
24 |
25 | // Options to be passed to minijasminenode.
26 | // See the full list at https://github.com/juliemr/minijasminenode/tree/jasmine1
27 | jasmineNodeOpts: {
28 | // If true, display spec names.
29 | isVerbose: false,
30 | // If true, print colors to the terminal.
31 | showColors: true,
32 | // If true, include stack traces in failures.
33 | includeStackTrace: false,
34 | // Default time to wait in ms before a test fails.
35 | defaultTimeoutInterval: 30000,
36 | }
37 | };
38 |
--------------------------------------------------------------------------------
/protractor.config.js:
--------------------------------------------------------------------------------
1 | exports.config = {
2 | // commenting this line auto-starts the selenium server
3 | //seleniumAddress: 'http://localhost:4444/wd/hub',
4 |
5 | // A base URL for your application under test. Calls to protractor.get()
6 | // with relative paths will be prepended with this.
7 | baseUrl: 'http://localhost:9001/',
8 |
9 | // Jasmine is fully supported as a test and assertion framework.
10 | framework: 'jasmine',
11 |
12 | // Options to be passed to minijasminenode.
13 | // See the full list at https://github.com/juliemr/minijasminenode/tree/jasmine1
14 | jasmineNodeOpts: {
15 | // If true, display spec names.
16 | isVerbose: true,
17 | // If true, print colors to the terminal.
18 | showColors: true,
19 | // If true, include stack traces in failures.
20 | includeStackTrace: false,
21 | // Default time to wait in ms before a test fails.
22 | defaultTimeoutInterval: 30000
23 | },
24 |
25 | // Spec patterns are relative to the location of this config.
26 | specs: [
27 | 'src/app/**/*.e2e.js'
28 | ]
29 | };
30 |
--------------------------------------------------------------------------------
/src/app/about/about.contoller.spec.js:
--------------------------------------------------------------------------------
1 | describe('AboutCtrl ', function () {
2 |
3 | var controller;
4 |
5 | beforeEach(module('app.about'));
6 | beforeEach(module('ui.router'));
7 |
8 | beforeEach(inject(function ($controller) {
9 | controller = $controller('AboutCtrl', {});
10 | }));
11 |
12 | it('should set the correct about page message', function () {
13 | expect(controller.message).toBe('And here is where I tell you all about myself.');
14 | });
15 |
16 | });
17 |
--------------------------------------------------------------------------------
/src/app/about/about.controller.e2e.js:
--------------------------------------------------------------------------------
1 | describe('test about page', function () {
2 |
3 | beforeEach(function() {
4 | browser.get('#/about');
5 | });
6 |
7 | it('should ensure the page title is correct', function () {
8 | expect(browser.getTitle()).toEqual('Angular Material Boilerplate');
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/src/app/about/about.controller.js:
--------------------------------------------------------------------------------
1 | angular.module('about.controllers.AboutCtrl', [])
2 | .controller('AboutCtrl', function() {
3 | var vm = this;
4 | vm.message = 'And here is where I tell you all about myself.';
5 | });
6 |
--------------------------------------------------------------------------------
/src/app/about/about.module.js:
--------------------------------------------------------------------------------
1 | angular.module('app.about', [
2 | 'ui.router',
3 | 'about.controllers.AboutCtrl'
4 | ])
5 |
6 | .config(function config($stateProvider) {
7 | $stateProvider.state('about', {
8 | url: '/about',
9 | templateUrl: 'about/about.view.html'
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/src/app/about/about.style.scss:
--------------------------------------------------------------------------------
1 | .about-page {
2 | // Add all about page related styles in here
3 | }
4 |
--------------------------------------------------------------------------------
/src/app/about/about.view.html:
--------------------------------------------------------------------------------
1 |
2 |
About
3 |
{{ about.message }}
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/app/app.js:
--------------------------------------------------------------------------------
1 | angular.module('angular-material-boilerplate', [
2 | /**
3 | * Dependencies must be injected in specific order:
4 | * 1) AngularJS dependencies
5 | * 2) Compiled HTML templates
6 | * 3) Common Services, Directives, Filters and Utilities
7 | * 4) Main App Layout
8 | * 5) Other App components (e.g. Toolbar, About, etc)
9 | */
10 |
11 | // AngularJS dependencies
12 | 'ngAnimate',
13 | 'ngAria',
14 | 'ngMaterial',
15 | 'ui.router',
16 | 'ngResource',
17 |
18 | // Include compiled HTML templates
19 | 'templates',
20 |
21 | // Common/shared code
22 | 'app.shared',
23 |
24 | // Layout
25 | 'app.layout',
26 |
27 | // Components
28 | 'app.toolbar',
29 | 'app.sidenav',
30 | 'app.todo',
31 | 'app.about'
32 | ])
33 |
34 | .config(function($mdThemingProvider) {
35 | $mdThemingProvider.theme('default')
36 | .primaryPalette('indigo')
37 | .accentPalette('purple', {
38 | 'default': '200'
39 | });
40 | })
41 |
42 | .run(['$state', function ($state) {
43 | $state.go('todo');
44 | }]);
45 |
--------------------------------------------------------------------------------
/src/app/layout/layout.controller.js:
--------------------------------------------------------------------------------
1 | angular.module('layout.controllers.LayoutCtrl', [])
2 | .controller('LayoutCtrl', function() {
3 |
4 | });
5 |
--------------------------------------------------------------------------------
/src/app/layout/layout.module.js:
--------------------------------------------------------------------------------
1 | angular.module('app.layout', [
2 | 'ui.router',
3 | 'layout.controllers.LayoutCtrl'
4 | ])
5 |
6 | .config(function config($stateProvider) {
7 | $stateProvider.state('layout', {
8 | url: '/layout',
9 | templateUrl: 'layout/layout.view.html'
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/src/app/layout/layout.style.scss:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: lightgrey;
3 | }
4 |
--------------------------------------------------------------------------------
/src/app/layout/layout.view.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/app/shared/directives/backButton.js:
--------------------------------------------------------------------------------
1 | angular.module('shared.directives.backButton', [])
2 | .directive('backButton', ['$window', function($window) {
3 | return {
4 | restrict: 'A',
5 | link: function (scope, elem) {
6 | elem.bind('click', function () {
7 | $window.history.back();
8 | });
9 | }
10 | };
11 | }]);
12 |
--------------------------------------------------------------------------------
/src/app/shared/services/Utils.js:
--------------------------------------------------------------------------------
1 | angular.module('shared.services.Utils', [])
2 | .factory('Utils', function Utils() {
3 |
4 | var service = {
5 | isNullOrUndefined: isNullOrUndefined,
6 | isUndefinedOrWhitespace: isUndefinedOrWhitespace,
7 | isNullOrWhitespace: isNullOrWhitespace,
8 | isNullOrUndefinedOrWhitespace: isNullorUndefinedOrWhitespace
9 | };
10 | return service;
11 |
12 | function isNullOrUndefined(object) {
13 | return object === null || angular.isUndefined(object) ? true : false;
14 | }
15 |
16 | function isUndefinedOrWhitespace(stringText) {
17 | return angular.isUndefined(stringText) || stringText.trim().length <= 0 ? true : false;
18 | }
19 |
20 | function isNullorUndefinedOrWhitespace(stringText) {
21 | if(stringText !== null) {
22 | return angular.isUndefined(stringText) || stringText.trim().length <= 0 ? true : false;
23 | } else {
24 | return true;
25 | }
26 | }
27 |
28 | function isNullOrWhitespace(stringText) {
29 | return stringText === null || stringText.trim().length <= 0 ? true : false;
30 | }
31 | });
32 |
--------------------------------------------------------------------------------
/src/app/shared/shared.module.js:
--------------------------------------------------------------------------------
1 | angular.module('app.shared', [
2 | 'shared.directives.backButton',
3 | 'shared.services.Utils'
4 | ]);
5 |
--------------------------------------------------------------------------------
/src/app/shared/styles/_color-palette.scss:
--------------------------------------------------------------------------------
1 | $white: #ffffff;
2 | $black: #000000;
3 | $red50: #ffebee;
4 | $red100: #ffcdd2;
5 | $red200: #ef9a9a;
6 | $red300: #e57373;
7 | $red400: #ef5350;
8 | $red500: #f44336;
9 | $red600: #e53935;
10 | $red700: #d32f2f;
11 | $red800: #c62828;
12 | $red900: #b71c1c;
13 | $redA100: #ff8a80;
14 | $redA200: #ff5252;
15 | $redA400: #ff1744;
16 | $redA700: #d50000;
17 | $pink50: #fce4ec;
18 | $pink100: #f8bbd0;
19 | $pink200: #f48fb1;
20 | $pink300: #f06292;
21 | $pink400: #ec407a;
22 | $pink500: #e91e63;
23 | $pink600: #d81b60;
24 | $pink700: #c2185b;
25 | $pink800: #ad1457;
26 | $pink900: #880e4f;
27 | $pinkA100: #ff80ab;
28 | $pinkA200: #ff4081;
29 | $pinkA400: #f50057;
30 | $pinkA700: #c51162;
31 | $purple50: #f3e5f5;
32 | $purple100: #e1bee7;
33 | $purple200: #ce93d8;
34 | $purple300: #ba68c8;
35 | $purple400: #ab47bc;
36 | $purple500: #9c27b0;
37 | $purple600: #8e24aa;
38 | $purple700: #7b1fa2;
39 | $purple800: #6a1b9a;
40 | $purple900: #4a148c;
41 | $purpleA100: #ea80fc;
42 | $purpleA200: #e040fb;
43 | $purpleA400: #d500f9;
44 | $purpleA700: #aa00ff;
45 | $deep-purple50: #ede7f6;
46 | $deep-purple100: #d1c4e9;
47 | $deep-purple200: #b39ddb;
48 | $deep-purple300: #9575cd;
49 | $deep-purple400: #7e57c2;
50 | $deep-purple500: #673ab7;
51 | $deep-purple600: #5e35b1;
52 | $deep-purple700: #512da8;
53 | $deep-purple800: #4527a0;
54 | $deep-purple900: #311b92;
55 | $deep-purpleA100: #b388ff;
56 | $deep-purpleA200: #7c4dff;
57 | $deep-purpleA400: #651fff;
58 | $deep-purpleA700: #6200ea;
59 | $indigo50: #e8eaf6;
60 | $indigo100: #c5cae9;
61 | $indigo200: #9fa8da;
62 | $indigo300: #7986cb;
63 | $indigo400: #5c6bc0;
64 | $indigo500: #3f51b5;
65 | $indigo600: #3949ab;
66 | $indigo700: #303f9f;
67 | $indigo800: #283593;
68 | $indigo900: #1a237e;
69 | $indigoA100: #8c9eff;
70 | $indigoA200: #536dfe;
71 | $indigoA400: #3d5afe;
72 | $indigoA700: #304ffe;
73 | $blue50: #e3f2fd;
74 | $blue100: #bbdefb;
75 | $blue200: #90caf9;
76 | $blue300: #64b5f6;
77 | $blue400: #42a5f5;
78 | $blue500: #2196f3;
79 | $blue600: #1e88e5;
80 | $blue700: #1976d2;
81 | $blue800: #1565c0;
82 | $blue900: #0d47a1;
83 | $blueA100: #82b1ff;
84 | $blueA200: #448aff;
85 | $blueA400: #2979ff;
86 | $blueA700: #2962ff;
87 | $light-blue50: #e1f5fe;
88 | $light-blue100: #b3e5fc;
89 | $light-blue200: #81d4fa;
90 | $light-blue300: #4fc3f7;
91 | $light-blue400: #29b6f6;
92 | $light-blue500: #03a9f4;
93 | $light-blue600: #039be5;
94 | $light-blue700: #0288d1;
95 | $light-blue800: #0277bd;
96 | $light-blue900: #01579b;
97 | $light-blueA100: #80d8ff;
98 | $light-blueA200: #40c4ff;
99 | $light-blueA400: #00b0ff;
100 | $light-blueA700: #0091ea;
101 | $cyan50: #e0f7fa;
102 | $cyan100: #b2ebf2;
103 | $cyan200: #80deea;
104 | $cyan300: #4dd0e1;
105 | $cyan400: #26c6da;
106 | $cyan500: #00bcd4;
107 | $cyan600: #00acc1;
108 | $cyan700: #0097a7;
109 | $cyan800: #00838f;
110 | $cyan900: #006064;
111 | $cyanA100: #84ffff;
112 | $cyanA200: #18ffff;
113 | $cyanA400: #00e5ff;
114 | $cyanA700: #00b8d4;
115 | $teal50: #e0f2f1;
116 | $teal100: #b2dfdb;
117 | $teal200: #80cbc4;
118 | $teal300: #4db6ac;
119 | $teal400: #26a69a;
120 | $teal500: #009688;
121 | $teal600: #00897b;
122 | $teal700: #00796b;
123 | $teal800: #00695c;
124 | $teal900: #004d40;
125 | $tealA100: #a7ffeb;
126 | $tealA200: #64ffda;
127 | $tealA400: #1de9b6;
128 | $tealA700: #00bfa5;
129 | $green50: #e8f5e9;
130 | $green100: #c8e6c9;
131 | $green200: #a5d6a7;
132 | $green300: #81c784;
133 | $green400: #66bb6a;
134 | $green500: #4caf50;
135 | $green600: #43a047;
136 | $green700: #388e3c;
137 | $green800: #2e7d32;
138 | $green900: #1b5e20;
139 | $greenA100: #b9f6ca;
140 | $greenA200: #69f0ae;
141 | $greenA400: #00e676;
142 | $greenA700: #00c853;
143 | $light-green50: #f1f8e9;
144 | $light-green100: #dcedc8;
145 | $light-green200: #c5e1a5;
146 | $light-green300: #aed581;
147 | $light-green400: #9ccc65;
148 | $light-green500: #8bc34a;
149 | $light-green600: #7cb342;
150 | $light-green700: #689f38;
151 | $light-green800: #558b2f;
152 | $light-green900: #33691e;
153 | $light-greenA100: #ccff90;
154 | $light-greenA200: #b2ff59;
155 | $light-greenA400: #76ff03;
156 | $light-greenA700: #64dd17;
157 | $lime50: #f9fbe7;
158 | $lime100: #f0f4c3;
159 | $lime200: #e6ee9c;
160 | $lime300: #dce775;
161 | $lime400: #d4e157;
162 | $lime500: #cddc39;
163 | $lime600: #c0ca33;
164 | $lime700: #afb42b;
165 | $lime800: #9e9d24;
166 | $lime900: #827717;
167 | $limeA100: #f4ff81;
168 | $limeA200: #eeff41;
169 | $limeA400: #c6ff00;
170 | $limeA700: #aeea00;
171 | $yellow50: #fffde7;
172 | $yellow100: #fff9c4;
173 | $yellow200: #fff59d;
174 | $yellow300: #fff176;
175 | $yellow400: #ffee58;
176 | $yellow500: #ffeb3b;
177 | $yellow600: #fdd835;
178 | $yellow700: #fbc02d;
179 | $yellow800: #f9a825;
180 | $yellow900: #f57f17;
181 | $yellowA100: #ffff8d;
182 | $yellowA200: #ffff00;
183 | $yellowA400: #ffea00;
184 | $yellowA700: #ffd600;
185 | $amber50: #fff8e1;
186 | $amber100: #ffecb3;
187 | $amber200: #ffe082;
188 | $amber300: #ffd54f;
189 | $amber400: #ffca28;
190 | $amber500: #ffc107;
191 | $amber600: #ffb300;
192 | $amber700: #ffa000;
193 | $amber800: #ff8f00;
194 | $amber900: #ff6f00;
195 | $amberA100: #ffe57f;
196 | $amberA200: #ffd740;
197 | $amberA400: #ffc400;
198 | $amberA700: #ffab00;
199 | $orange50: #fff3e0;
200 | $orange100: #ffe0b2;
201 | $orange200: #ffcc80;
202 | $orange300: #ffb74d;
203 | $orange400: #ffa726;
204 | $orange500: #ff9800;
205 | $orange600: #fb8c00;
206 | $orange700: #f57c00;
207 | $orange800: #ef6c00;
208 | $orange900: #e65100;
209 | $orangeA100: #ffd180;
210 | $orangeA200: #ffab40;
211 | $orangeA400: #ff9100;
212 | $orangeA700: #ff6d00;
213 | $deep-orange50: #fbe9e7;
214 | $deep-orange100: #ffccbc;
215 | $deep-orange200: #ffab91;
216 | $deep-orange300: #ff8a65;
217 | $deep-orange400: #ff7043;
218 | $deep-orange500: #ff5722;
219 | $deep-orange600: #f4511e;
220 | $deep-orange700: #e64a19;
221 | $deep-orange800: #d84315;
222 | $deep-orange900: #bf360c;
223 | $deep-orangeA100: #ff9e80;
224 | $deep-orangeA200: #ff6e40;
225 | $deep-orangeA400: #ff3d00;
226 | $deep-orangeA700: #dd2c00;
227 | $brown50: #efebe9;
228 | $brown100: #d7ccc8;
229 | $brown200: #bcaaa4;
230 | $brown300: #a1887f;
231 | $brown400: #8d6e63;
232 | $brown500: #795548;
233 | $brown600: #6d4c41;
234 | $brown700: #5d4037;
235 | $brown800: #4e342e;
236 | $brown900: #3e2723;
237 | $brownA100: #d7ccc8;
238 | $brownA200: #bcaaa4;
239 | $brownA400: #8d6e63;
240 | $brownA700: #5d4037;
241 | $grey50: #fafafa;
242 | $grey100: #f5f5f5;
243 | $grey200: #eeeeee;
244 | $grey300: #e0e0e0;
245 | $grey400: #bdbdbd;
246 | $grey500: #9e9e9e;
247 | $grey600: #757575;
248 | $grey700: #616161;
249 | $grey800: #424242;
250 | $grey900: #212121;
251 | $grey1000: #000000;
252 | $greyA100: #ffffff;
253 | $greyA200: #eeeeee;
254 | $greyA400: #bdbdbd;
255 | $greyA700: #616161;
256 | $blue-grey50: #eceff1;
257 | $blue-grey100: #cfd8dc;
258 | $blue-grey200: #b0bec5;
259 | $blue-grey300: #90a4ae;
260 | $blue-grey400: #78909c;
261 | $blue-grey500: #607d8b;
262 | $blue-grey600: #546e7a;
263 | $blue-grey700: #455a64;
264 | $blue-grey800: #37474f;
265 | $blue-grey900: #263238;
266 | $blue-greyA100: #cfd8dc;
267 | $blue-greyA200: #b0bec5;
268 | $blue-greyA400: #78909c;
269 | $blue-greyA700: #455a64;
270 |
--------------------------------------------------------------------------------
/src/app/shared/styles/_mixins.scss:
--------------------------------------------------------------------------------
1 | // ** This is where you can put your very own put Sass Mixins that can be used globally. **
2 |
3 | // Mixin: circle ($width:50%, $height:50%)
4 | // Description: Used to make a circular element. (e.g Change a square
to a circle)
5 | // @params $width: [ number ]: Specify the width of the element. Set to 50% if an argument is not passed.
6 | // @params $height: [ number ]: Specify the height of the element. Set to 50% if an argument is not passed.
7 | @mixin circle ($width:50%, $height:50%) {
8 | width: $width;
9 | height: $height;
10 | border-radius: 50%;
11 | }
12 |
--------------------------------------------------------------------------------
/src/app/shared/styles/overrides.scss:
--------------------------------------------------------------------------------
1 | // ** This is where you can override the default Angular Material styles **
2 |
--------------------------------------------------------------------------------
/src/app/shared/styles/shared.scss:
--------------------------------------------------------------------------------
1 | // ** This is where you define styles that are used globally across the entire app ** //
2 | @import "mixins";
3 |
4 | // Class: img-circle
5 | // Description: Used to make a circular element. (e.g Change a square
to a circle)
6 | .img-circle {
7 | @include circle;
8 | }
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/app/sidenav/sidenav.controller.js:
--------------------------------------------------------------------------------
1 | angular.module('sidenav.controllers.SideNavCtrl', [])
2 | .controller('SideNavCtrl', function($mdSidenav, $state, SideNav) {
3 | var vm = this;
4 | vm.selectMenuItem = selectMenuItem;
5 | vm.closeSideNav = closeSideNav;
6 | vm.menuItems = SideNav.getMenuItems;
7 | vm.selected = vm.menuItems[0];
8 |
9 | function selectMenuItem(menuItem) {
10 | vm.selected = menuItem;
11 | closeSideNav();
12 | $state.go(menuItem.route);
13 | }
14 |
15 | function closeSideNav() {
16 | $mdSidenav('left').close();
17 | }
18 | });
19 |
--------------------------------------------------------------------------------
/src/app/sidenav/sidenav.module.js:
--------------------------------------------------------------------------------
1 | angular.module('app.sidenav', [
2 | 'sidenav.controllers.SideNavCtrl',
3 | 'sidenav.services.SideNav'
4 | ]);
5 |
--------------------------------------------------------------------------------
/src/app/sidenav/sidenav.service.js:
--------------------------------------------------------------------------------
1 |
2 | angular.module('sidenav.services.SideNav', [])
3 | .factory('SideNav', function() {
4 |
5 | var menuItems = [
6 | {
7 | name: 'To-Do',
8 | icon: '',
9 | route: 'todo'
10 | },
11 | {
12 | name: 'About',
13 | icon: '',
14 | route: 'about'
15 | }
16 | ];
17 |
18 | var service = {
19 | getMenuItems: menuItems
20 | };
21 | return service;
22 |
23 | });
24 |
--------------------------------------------------------------------------------
/src/app/sidenav/sidenav.style.scss:
--------------------------------------------------------------------------------
1 | @import "../shared/styles/_mixins";
2 |
3 | .sidenav {
4 | md-sidenav {
5 | max-width: 250px;
6 | }
7 |
8 | .profile-details {
9 | margin: 10px 0 10px 0;
10 | }
11 |
12 | .profile-img {
13 | @include circle(40%, 40%);
14 | }
15 |
16 | // Add a margin above the profile when the width is greater than 600px
17 | @media (min-width: 600px) {
18 | .sidenav-profile {
19 | margin-top: 10px;
20 | }
21 | }
22 |
23 | .menu-items .selected {
24 | color: #03a9f4;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/sidenav/sidenav.view.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | close
10 |
11 |
12 |
13 |
14 |
15 |

16 |
17 | Joe Bloggs
18 | Director
19 |
20 |
21 |
22 |
23 |
24 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/app/todo/todo.controller.e2e.js:
--------------------------------------------------------------------------------
1 | describe('test todo page', function () {
2 |
3 | beforeEach(function() {
4 | browser.get('#/todo');
5 | });
6 |
7 | it('should ensure the page title is correct', function () {
8 | expect(browser.getTitle()).toEqual('Angular Material Boilerplate');
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/src/app/todo/todo.controller.js:
--------------------------------------------------------------------------------
1 | angular.module('todo.controllers.ToDoCtrl', [])
2 | .controller('ToDoCtrl', function(ToDo) {
3 | var vm = this;
4 | vm.toDoItems = ToDo.getItems;
5 | });
6 |
--------------------------------------------------------------------------------
/src/app/todo/todo.controller.spec.js:
--------------------------------------------------------------------------------
1 | describe('ToDoCtrl ', function () {
2 |
3 | var controller;
4 |
5 | beforeEach(module('app.todo'));
6 | beforeEach(module('ui.router'));
7 |
8 | beforeEach(inject(function ($controller) {
9 | controller = $controller('ToDoCtrl', {});
10 | }));
11 |
12 | it('should set the correct about page message', function () {
13 | expect(controller.toDoItems.length).toBeGreaterThan(0);
14 | });
15 |
16 | });
17 |
--------------------------------------------------------------------------------
/src/app/todo/todo.module.js:
--------------------------------------------------------------------------------
1 | angular.module('app.todo', [
2 | 'ui.router',
3 | 'todo.controllers.ToDoCtrl',
4 | 'todo.services.ToDo'
5 | ])
6 |
7 | .config(function config($stateProvider) {
8 | $stateProvider.state('todo', {
9 | url: '/todo',
10 | templateUrl: 'todo/todo.view.html'
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/src/app/todo/todo.service.js:
--------------------------------------------------------------------------------
1 | angular.module('todo.services.ToDo', [])
2 | .factory('ToDo', function() {
3 |
4 | var toDoItems = [
5 | {
6 | title: 'Make a todo list',
7 | completed: true
8 | },
9 | {
10 | title: 'Check off first item on todo list',
11 | completed: true
12 | },
13 | {
14 | title: 'Realise you\'ve have already accomplished two things on the list',
15 | completed: true
16 | },
17 | {
18 | title: 'Reward yourself with a nice long nap',
19 | completed: false
20 | }
21 | ];
22 |
23 | var service = {
24 | getItems: toDoItems
25 | };
26 | return service;
27 |
28 | });
29 |
--------------------------------------------------------------------------------
/src/app/todo/todo.style.scss:
--------------------------------------------------------------------------------
1 | .todo-page {
2 |
3 | // Fixes issue with to-do items not aligned correctly on small screens
4 | md-checkbox {
5 | max-width: 20px;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/app/todo/todo.view.html:
--------------------------------------------------------------------------------
1 |
2 |
To-Do
3 |
4 |
5 |
6 | {{ item.title }}
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/app/toolbar/toolbar.controller.js:
--------------------------------------------------------------------------------
1 | angular.module('toolbar.controllers.ToolbarCtrl', [])
2 | .controller('ToolbarCtrl', function($state, $scope, $mdSidenav) {
3 | $scope.openSideNav = function () {
4 | $mdSidenav('left').open();
5 | };
6 | });
7 |
--------------------------------------------------------------------------------
/src/app/toolbar/toolbar.module.js:
--------------------------------------------------------------------------------
1 | angular.module('app.toolbar', [
2 | 'toolbar.controllers.ToolbarCtrl'
3 | ]);
4 |
--------------------------------------------------------------------------------
/src/app/toolbar/toolbar.style.scss:
--------------------------------------------------------------------------------
1 | .toolbar {
2 | // Add all toolbar related styles in here
3 | }
4 |
--------------------------------------------------------------------------------
/src/app/toolbar/toolbar.view.html:
--------------------------------------------------------------------------------
1 |
2 |
6 | Angular Material Boilerplate
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/assets/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacdonnacha/angular-material-boilerplate/3d769c517df271389b5e4362c935ab73e03f9331/src/assets/images/favicon.ico
--------------------------------------------------------------------------------
/src/assets/images/user-profile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmacdonnacha/angular-material-boilerplate/3d769c517df271389b5e4362c935ab73e03f9331/src/assets/images/user-profile.png
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Angular Material Boilerplate
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/vendor.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The `vendor_files` property in Gruntfile.js holds files to be automatically
3 | * concatenated and minified with our project source files.
4 | *
5 | * NOTE: Use the *.min.js version when compiling for production.
6 | * Otherwise, use the normal *.js version for development
7 | *
8 | */
9 |
10 | module.exports = {
11 | js: [
12 | // utility libraries
13 | 'vendor/jquery/dist/jquery.min.js',
14 |
15 | // Angular components
16 | 'vendor/angular/angular.js',
17 | 'vendor/angular-ui-router/release/angular-ui-router.min.js',
18 | 'vendor/angular-resource/angular-resource.min.js',
19 | 'vendor/angular-mocks/angular-mocks.js',
20 |
21 | // Angular Material
22 | 'vendor/angular-animate/angular-animate.min.js',
23 | 'vendor/angular-aria/angular-aria.min.js',
24 | 'vendor/angular-material/angular-material.min.js',
25 |
26 | // Local storage
27 | 'vendor/angular-local-storage/dist/angular-local-storage.min.js',
28 |
29 | // Modernizer
30 | 'vendor/modernizr/modernizr.js'
31 | ],
32 | css: [ ],
33 | assets: [ ]
34 | };
35 |
--------------------------------------------------------------------------------
/vendor_files.js:
--------------------------------------------------------------------------------
1 | require('angular');
2 | require('angular-animate');
3 | require('angular-aria');
4 | require('angular-material');
5 | require('angular-ui-router');
6 | require('angular-resource');
7 | require('angular-local-storage');
8 |
--------------------------------------------------------------------------------