voorhoede - front-end architecture
DESCRIPTION
Presented during Javascript MVC Amsterdam meetup, 29 Jan 2014: http://www.meetup.com/JavaScript-MVC-Meetup-Amsterdam/events/156767102/ At De Voorhoede (http://voorhoede.nl) I'm responsible for setting up new front-end projects in such a way that it's easy for teams to work with. This presentation explains how we structure these projects. The presentation includes some tips on structuring larger AngularJS projects.TRANSCRIPT
Jasper Moelker @jbmoelker
(ng) Architecturebest practices for setting up front-end / AngularJS projects
! warning: opinions ahead
de voorhoede
(NG) Architecture
Why care about front-end architecture? !
How to approach setting it up? !
modular structures !
Use tools that help you
image source: http://static.onemansblog.com/wp-content/uploads/2011/05/impossible-lego-03.jpg
Why care?aim: easy to use for (new) team members
de voorhoede
Why care?
• easy to understand
• easy to maintain
• easy to extend
• easy to re-use
• easy to deploy
• easy to distribute
Aim: easy to use for (New) team members
image source: http://www.registrycleaner.us.com
Ok, so how?approach: clear rules and standards
de voorhoede
Ok, so how?
• clear naming conventions
• clear directory structure
• clear dependency declarations
• clear code styles
• clear documentation (for what’s still unclear)
• clear tests & tasks for everything else
Approach: clear rules & standards
image source: http://www.carlosdinares.com
Think Modular-
de voorhoede
think modular
Atomic design by Brad Frost
source: http://bradfrostweb.com/blog/post/atomic-web-design/ see also: http://demo.pattern-lab.info/
de voorhoede
think modular
BEM blocks on smashing
source: http://coding.smashingmagazine.com/2012/04/16/a-new-front-end-methodology-bem/ see also: http://bem.info/
de voorhoede
think modular
source: https://github.com/angular/angular-seed/tree/master/app/js
used in ng tutorial
de voorhoede
think modular
source: http://briantford.com/blog/huuuuuge-angular-apps.html see also: https://github.com/angular/angularjs-batarang/
Brian ford on huuuuuge (5x) angular apps
used in batarang
de voorhoede
think modular
source: https://github.com/angular-app/angular-app/tree/master/client/src/app see also: https://github.com/ngbp/ngbp/tree/v0.3.1-release/src/app/home
read: http://cliffmeyers.com/blog/2013/4/21/code-organization-angularjs-javascript
ng app & ng boilerplate
de voorhoede
• Symfony 1: plugins
• Symfony 2: bundles (example on right)
• Web components
• Component.io
• Bower?
think modular
source: http://blog.solutionset.com/2012/09/14/easy-way-to-transform-a-twitter-oauth-library-into-a-symfony-2-bundle/
other frameworks / concepts
de voorhoede
think modular
Atomics
original image: http://bradfrostweb.com/blog/post/atomic-web-design/
Views
Components
de voorhoede
Think modular
App source structure
./source/ common/ <— atomics modules/ components/ <— re-usable components views/ <— unique views vendor/ <— third party modules app.js <— app core (config), include views bootstrap.js (+json) <— bootstrap app if supported index.html
de voorhoede
Atomics
typically no dependencies or only dependent on other atomics, examples:
• global css rules and variables (style guide)
• global assets like logo, fonts, icons
• high level (AngularJS) services: pub sub, rest service, transformers
• common (AngularJS) directives
• common (AngularJS) filters
elementary app rules & assets
source: http://www.heringinternational.com/de/beton/betoglass-4505.htm
de voorhoede
Atomics
structure atomics
common/ assets/ fonts/ images/ scss/ atomics/ _icons.scss mixins/ _mixin-name.scss directives/ view-box-directive.js filters/ services/ <— providers, services, factories rest-service.js
de voorhoede
Atomics in AngularJS
/** * @ngdoc directive * @name directives.viewBox.directive:viewBox * @description Supports using expression for SVG viewBox, by using `data-view-box` which sets * `viewBox` attribute. Code adapted from http://stackoverflow.com/a/14596319 * @example <doc:example> <doc:source> <svg data-view-box="{{ APP_VIEWPORT.viewBox }}"></svg> </doc:source> </doc:example> */ angular.module('directives.viewBox', []) // no dependencies .directive('viewBox', [ function () { 'use strict'; return { // no template, or other dependencies link: function (scope, element, attributes) { attributes.$observe('viewBox', function(value) { element.attr('viewBox', value); }); } }; } ]);
de voorhoede
Components
source: http://www.heringinternational.com/en/news&cmd=details&newsid=1179.htm
Re-usable encapsulated building blocks
• include structure (markup), presentation (style), behaviour (scripts), assets, docs, tests.
• important: declare dependencies
• in AngularJS use isolate scope to encapsulate component
de voorhoede
componentS
Structure component
modules/ components/ my-component/ media/ _my-component.scss / .less my-component-template.html my-component.js <— dependencies, config, ngdocs my-component-directive.js my-component-controller.js my-component-controller.test.js my-component-service.js my-component-service.test.js README.md
de voorhoede
Component in AngularJS
/** * @ngdoc overview * @name components.pager * @requires common/services.isPositiveInteger */ angular.module('components.pager', ['services.isPositiveInteger']); !/** * @ngdoc directive * @name components.pager.directive:pager */ angular.module('components.pager') .directive('pager', ['isPositiveInteger', function (isPositiveInteger) { 'use strict'; return { templateUrl: 'modules/components/pager/pager-template.html', replace: true, scope: { page: '=', itemsPerPage: '@', itemsTotal: '@' }, link: function (scope, element, attributes) {} } }]);
de voorhoede
Views
source: http://demavo.nl/literatuur/mondriaan.html
Unique compilation of components
in AngularJS
• uses ngView
• $routeProvider
• $stateProvider
de voorhoede
Views
Structure view
modules/ views/ my-view/ media/ _my-view.scss / .less my-view-view.html my-view.js <— dependencies, config, ngdocs my-view-directive.js my-view-controller.js my-view-controller.test.js README.md
de voorhoede
View in AngularJS
/** * @ngdoc overview * @name views.myView * @description My view module. * [detailed description of 'my view' module, which doesn't fit in this presentation] */ angular .module('views.myView', [ 'ngRoute', 'components.modal', 'components.pager', 'services.restService' ]) ! .config([ '$routeProvider', function ($routeProvider) { 'use strict'; $routeProvider .when('/view/my-view/:action', { templateUrl: 'modules/views/my-view/my-view-view.html', controller: 'MyViewController' }); } ]);
Code stylehelp keep your code clean & coherent
de voorhoede
Code quality tools
Configuration files in root
./ .csslintrc .jshintrc .jscs.json dubfind.cfg README.md
resources: csslint: http://csslint.net/ jshint: http://www.jshint.com/ jscs: https://github.com/mdevils/node-jscs dufind: https://github.com/sfrancisx/dupfind
Document your app-
de voorhoede
App documentation
…/ module.js index.ngdoc README.md !!!!!and use a style and / or code guide? for rules like use ‘ngMin proof syntax’.
README’s & NGDOCS
use ngdocs: https://github.com/m7r/grunt-ngdocs
Test your code-
de voorhoede
test your code
tests structure
./tests/ end-to-end/ helpers/ report/ csslint.xml jshint.xml karma.xml vendor/ <— eg. mocks end-to-end.js <— e2e and karma.js <— unit test config !test configs use bootstrap.json tests run locally and on Jenkins CI
Automatewrite tasks for repetitive actions
de voorhoede
Automate
Tasks structure
./Gruntfile.js —————>
./tasks/ grunt/ configuration/ task-name.js tasks/ task-name.js templates/ utilities/ phing/ java/ !we run tasks both locally and on server using Jenkins CI
module.exports = function (grunt) { 'use strict'; ! // Use `tasks/grunt/configuration/index.js`, // which reads package.json and loads all task configs: var config = require('./tasks/grunt/configuration')(grunt); grunt.config.init(config); ! // Load all npm installed grunt tasks. require(‘matchdep').filterDev('grunt-*') .forEach(grunt.loadNpmTasks); ! // load all project grunt tasks. grunt.task.loadTasks('tasks/grunt/tasks'); grunt.registerTask('default', [‘task-wizard']); };
source: http://gruntjs.com/img/grunt-logo.png
de voorhoede
Grunt Task wizard
source: https://gist.github.com/jbmoelker/8384456#file-task-wizard-js
1) Select task category
2) Select project task
3) Enter arguments
Result: New component created * new directory created * html, js, scss, readme files created * registers module in index files
de voorhoede
Automate
Develop task Deploy / distribute task// tasks/grunt/tasks/develop.js (non-ng project) !module.exports = function (grunt) { 'use strict'; grunt.registerTask( 'develop', 'Setup web dir for development and watch source', function (mode) { var tasks = [ 'compile-index:development', 'compile-html:development', 'copy:development', 'sass:development', 'concat:development' ]; if(mode !== 'no-watch'){ tasks.push('watch'); } grunt.task.run(tasks); } ); };
// tasks/grunt/tasks/deploy.js (ng-project) !module.exports = function (grunt) { 'use strict'; grunt.registerTask( 'deploy', 'Concatenates and minifies source files', function () { grunt.task.run([ 'clean:distribution', 'copy', 'ngtemplates', 'concat', 'uglify', 'clean:templates' ]); } ); };
de voorhoede
Think modular
App structure
./ distribution/ <— auto generated via ‘grunt deploy’ docs/ <— auto generated via ‘grunt docs’ source/ common/ <— atomics modules/ components/ <— re-usable components views/ <— unique views vendor/ app.js bootstrap.js index.html tasks/ <— task config, templates, utilities tests/ <— test config, e2e, helpers, reports web/ <— auto generated via ‘grunt develop’