├── .gitignore ├── README.md ├── karma.conf.js ├── package.json ├── src ├── app.config.js ├── app.css ├── app.js ├── asset │ └── image │ │ └── components.jpg ├── common │ ├── common.module.js │ └── component │ │ └── user-info-component.js ├── feature-a │ ├── feature-a.config.js │ ├── feature-a.ctrl.js │ ├── feature-a.module.js │ └── feature-a.tpl.html ├── feature-b │ ├── feature-b.config.js │ ├── feature-b.module.js │ └── some-component │ │ ├── some-component.js │ │ └── some-component.tpl.html ├── index.html └── tests.webpack.js ├── webpack.build.js ├── webpack.config.js ├── webpack.make.js └── webpack.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | public 3 | build 4 | .idea -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### ATTENTION 2 | This is one of the first Angular JS repositories I created, if you like component pattern 3 | and want a seed project use [Angular JS 1.X ES6 seed](https://github.com/tomastrajan/angular-js-es6-testing-example) 4 | which implements `component pattern`, contains better `webpack` build and advanced 5 | testing support out of the box, happy hacking! 6 | 7 | # Component Pattern for Angular JS 1.X 8 | 9 | * original blog post describing [Component Pattern](https://medium.com/@tomastrajan/component-paradigm-cf32e94ba78b) 10 | * presentation describing history of Angular JS concepts and [Component Pattern](https://slides.com/tomastrajan/component-pattern-for-angular-js-1-x) 11 | 12 |  13 | 14 | ## Features 15 | 16 | * standard implementation of a state using `ui-router` 17 | * component implementation used as inline template in `ui-router` state definition 18 | * ES6, Webpack 19 | 20 | ## Installation 21 | 22 | To use it, just clone this repo and install the npm dependencies: 23 | 24 | ```shell 25 | $ git clone https://github.com/tomastrajan/component-pattern-for-angular-js-1-x component_pattern_example 26 | $ cd component_pattern_example 27 | $ npm install 28 | ``` 29 | 30 | ## Scripts 31 | 32 | All scripts are run with `npm run [script]`, for example: `npm run test`. 33 | 34 | * `build` - generate a minified build to dist folder 35 | * `dev` - start development server, try it by opening `http://localhost:8080/` 36 | * `test` - run all tests 37 | * `test:live` - continuously run unit tests watching for changes 38 | 39 | ## Credits 40 | 41 | This example uses build process from [angular-webpack-workflow](https://github.com/Foxandxss/angular-webpack-workflow), 42 | so check it out for more information if needed. -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | var webpackConfig = require('./webpack.test'); 2 | 3 | // Reference: http://karma-runner.github.io/0.12/config/configuration-file.html 4 | module.exports = function karmaConfig (config) { 5 | config.set({ 6 | frameworks: [ 7 | // Reference: https://github.com/karma-runner/karma-jasmine 8 | // Set framework to jasmine 9 | 'jasmine' 10 | ], 11 | 12 | reporters: [ 13 | // Reference: https://github.com/mlex/karma-spec-reporter 14 | // Set reporter to print detailed results to console 15 | 'spec', 16 | 17 | // Reference: https://github.com/karma-runner/karma-coverage 18 | // Output code coverage files 19 | 'coverage' 20 | ], 21 | 22 | files: [ 23 | // Grab all files in the app folder that contain .test. 24 | 'src/tests.webpack.js' 25 | ], 26 | 27 | preprocessors: { 28 | // Reference: http://webpack.github.io/docs/testing.html 29 | // Reference: https://github.com/webpack/karma-webpack 30 | // Convert files with webpack and load sourcemaps 31 | 'src/tests.webpack.js': ['webpack', 'sourcemap'] 32 | }, 33 | 34 | browsers: [ 35 | // Run tests using PhantomJS 36 | 'PhantomJS' 37 | ], 38 | 39 | singleRun: true, 40 | 41 | // Configure code coverage reporter 42 | coverageReporter: { 43 | dir: 'build/coverage/', 44 | type: 'html' 45 | }, 46 | 47 | webpack: webpackConfig 48 | }); 49 | }; 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "component-pattern-for-angular-js-1-x", 3 | "version": "1.0.0", 4 | "description": "Example of how to implement Component Pattern in Angular JS 1.X", 5 | "scripts": { 6 | "build": "webpack --config webpack.build.js --bail -p", 7 | "dev": "webpack-dev-server --history-api-fallback --hot --inline --progress", 8 | "test": "karma start", 9 | "test:live": "karma start --auto-watch --no-single-run" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/tomastrajan/component-pattern-for-angular-js-1-x" 14 | }, 15 | "author": "Tomas Trajan", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/tomastrajan/component-pattern-for-angular-js-1-x/issues" 19 | }, 20 | "homepage": "https://github.com/tomastrajan/component-pattern-for-angular-js-1-x", 21 | "dependencies": { 22 | "angular": "1.3.16", 23 | "angular-ui-router": "0.2.15", 24 | "autoprefixer-core": "5.2.0", 25 | "babel-core": "5.5.8", 26 | "babel-loader": "5.1.4", 27 | "babel-runtime": "5.5.8", 28 | "bootstrap": "3.3.5", 29 | "css-loader": "0.15.5", 30 | "extract-text-webpack-plugin": "0.8.2", 31 | "file-loader": "0.8.4", 32 | "html-webpack-plugin": "1.5.2", 33 | "jquery": "2.1.4", 34 | "node-libs-browser": "0.5.2", 35 | "postcss-loader": "0.5.1", 36 | "raw-loader": "0.5.1", 37 | "style-loader": "0.12.3", 38 | "webpack": "1.9.11", 39 | "webpack-dev-server": "1.9.0" 40 | }, 41 | "devDependencies": { 42 | "angular-mocks": "1.4.1", 43 | "isparta-instrumenter-loader": "0.2.1", 44 | "jasmine-core": "2.3.4", 45 | "karma": "0.13.2", 46 | "karma-coverage": "0.4.2", 47 | "karma-jasmine": "0.3.5", 48 | "karma-phantomjs-launcher": "0.2.0", 49 | "karma-sourcemap-loader": "0.3.5", 50 | "karma-spec-reporter": "0.0.20", 51 | "karma-webpack": "1.5.1", 52 | "null-loader": "0.1.1", 53 | "phantomjs": "1.9.17" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/app.config.js: -------------------------------------------------------------------------------- 1 | export function routing($urlRouterProvider, $stateProvider) { 2 | 3 | $urlRouterProvider.otherwise('/feature-a'); 4 | 5 | $stateProvider 6 | .state('app', { 7 | abstract: true, 8 | template: '
' 9 | }) 10 | 11 | } 12 | 13 | export function routingEventsLogger($rootScope) { 14 | 15 | const ROUTING_EVENTS = [ 16 | '$stateChangeStart', 17 | '$stateChangeSuccess', 18 | '$stateChangeError' 19 | ]; 20 | 21 | const VIEW_EVENTS = [ 22 | '$viewContentLoading', 23 | '$viewContentLoaded' 24 | ]; 25 | 26 | ROUTING_EVENTS.forEach(function(routingEvent) { 27 | $rootScope.$on(routingEvent, function(event, toState, toParams, fromState, fromParams, error){ 28 | console.log(routingEvent, event, toState, toParams, fromState, fromParams); 29 | }); 30 | }); 31 | 32 | VIEW_EVENTS.forEach(function(viewEvent) { 33 | $rootScope.$on(viewEvent, function(event, viewConfig){ 34 | console.log(viewEvent, event, viewConfig); 35 | }); 36 | }); 37 | 38 | } -------------------------------------------------------------------------------- /src/app.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 80px; 3 | } -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | import 'bootstrap/dist/css/bootstrap.min.css'; 2 | import './app.css'; 3 | 4 | import bootstrap from 'bootstrap'; 5 | 6 | import angular from 'angular'; 7 | import uirouter from 'angular-ui-router'; 8 | 9 | import { routing, routingEventsLogger } from './app.config'; 10 | 11 | import common from './common/common.module'; 12 | 13 | import featureA from './feature-a/feature-a.module'; 14 | import featureB from './feature-b/feature-b.module'; 15 | 16 | const DEBUG = false; 17 | 18 | const app = angular 19 | .module('app', [uirouter, common, featureA, featureB]) 20 | .config(routing); 21 | 22 | if (DEBUG) { 23 | app 24 | .run(routingEventsLogger) 25 | ; 26 | } 27 | -------------------------------------------------------------------------------- /src/asset/image/components.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomastrajan/component-pattern-for-angular-js-1-x/7f58f40400a5faef0f838e8bdcc82bc9194f5f02/src/asset/image/components.jpg -------------------------------------------------------------------------------- /src/common/common.module.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | 3 | import userInfoComponent from './component/user-info-component'; 4 | 5 | export default angular 6 | .module('app.common', []) 7 | .directive('userInfoComponent', userInfoComponent) 8 | .name; -------------------------------------------------------------------------------- /src/common/component/user-info-component.js: -------------------------------------------------------------------------------- 1 | export default function() { 2 | 3 | return { 4 | scope: {}, 5 | controller: UserInfoComponent, 6 | controllerAs: 'ctrl', 7 | bindToController: true, 8 | template: 'Hi {{ctrl.name}}!' 9 | }; 10 | 11 | } 12 | 13 | class UserInfoComponent { 14 | 15 | constructor() { 16 | this.name = 'Tomas'; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/feature-a/feature-a.config.js: -------------------------------------------------------------------------------- 1 | import template from './feature-a.tpl.html' 2 | 3 | export function routing($stateProvider) { 4 | 5 | $stateProvider 6 | .state('app.feature-a', { 7 | url: '/feature-a', 8 | controller: 'FeatureACtrl', 9 | controllerAs: 'ctrl', 10 | template: template 11 | }); 12 | } -------------------------------------------------------------------------------- /src/feature-a/feature-a.ctrl.js: -------------------------------------------------------------------------------- 1 | export default function FeatureACtrl() { 2 | 3 | const vm = this; 4 | 5 | vm.property = 'My Controller Property'; 6 | 7 | } -------------------------------------------------------------------------------- /src/feature-a/feature-a.module.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | 3 | import { routing } from './feature-a.config.js'; 4 | import FeatureACtrl from './feature-a.ctrl.js'; 5 | 6 | export default angular.module('app.feature-a', []) 7 | .config(routing) 8 | .controller('FeatureACtrl', FeatureACtrl) 9 | .name; -------------------------------------------------------------------------------- /src/feature-a/feature-a.tpl.html: -------------------------------------------------------------------------------- 1 |Feature is so great that you won't believe! It also contains: {{ctrl.property}}
6 | 7 | 8 |Feature is so great that it even contains counter functionality! It also contains: {{ctrl.property}}
6 |Counter: {{ctrl.count}}
7 |Oh, and don't forget about user-info-component
which we just reused here... Sick!