Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Keep your sanity while developing AngularJS app...

Keep your sanity while developing AngularJS apps on an enterprise scale

Building larger applications is tricky in AngularJS because there are very few conventions. Angular leaves this up to you. In this talk we’ll share all the lessons we learned from building a huge AngularJS application.

We’ll go over the importance of creating those conventions and some of the conventions we made. We’ll also highlight how to correctly use angular’s concepts in order to keep your codebase clean and maintainable.

Lastly we’ll show the benefits of using UI-Router over ngRouter and the ways in which you can automate your workflow for improved maintainability.

Resources:
* http://www.kunstmaan.be
* http://www.playframework.com/
* http://vertx.io/
* https://github.com/sockjs
* http://gruntjs.com/
* http://sass-lang.com/
* http://savanne.be/articles/testing-with-angular-js/
* https://github.com/angular/angular-seed
* http://yeoman.io/
* https://github.com/yeoman/generator-angular
* http://briantford.com/blog/huuuuuge-angular-apps.html
* https://github.com/rwaldron/idiomatic.js/
* https://github.com/airbnb/javascript
* https://github.com/
* https://bitbucket.org
* https://www.gitlab.com/
* https://www.npmjs.org/package/grunt-file-blocks
* https://www.npmjs.org/package/grunt-usemin
* https://www.npmjs.org/package/grunt-contrib-connect
* https://github.com/angular-ui/ui-router
* http://ify.io/lazy-loading-in-angularjs/

On April 9th 2014 we've shared our experiences in AngularJS with the Belgian AngularJS meetup group. 72 people joined us to see what we had to say. In case you missed it, don't worry you'll find everything you need here: http://bit.ly/angularmeetup

daanporon

April 09, 2014
Tweet

More Decks by daanporon

Other Decks in Programming

Transcript

  1. 60 Communication Agency @ Leuven Kunstmaan 60 employees (21 developers)

    Campaign, corporate websites and web applications
  2. ‘$scope’ of the frontend APP 13105 CONTR. 61 DIRECTIVES 91

    SERVICES 96 MARKUP 5253 TEST 24812 OTHER
  3. The power of AngularJS! • Two-way data binding • Directives

    • Dependency Injection • Powerful services • Testability
  4. Conventions to the rescue! • Code becomes easier to read

    • Simpler to test • Easier to implement changes or features • Team collaboration • Lower total cost of ownership
  5. angular-seed partials/ |- home.html |- login.html js/ |- controllers.js |-

    directives.js |- filters.js |- services.js |- app.js https://github.com/angular/angular-seed One file per type for all things
  6. yeoman’s generator-angular js/ |- controllers/ |- homeController.js |- loginController.js |-

    directives/ |- kmAuthentication.js |- filters/ |- services/ |- usersService.js partials/ |- home.html |- login.html https://github.com/yeoman/generator-angular Improvement: each thing = one file, grouped by type
  7. js/ |- app.js |- api/ |- home/ |- login/ |-

    login.js |- config/ |- states.json |- config.json |- controllers/ |- loginCtrl.js |- directives/ |- kmAuthentication.js |- filters/ |- services/ |- usersService.js |- translations/ |- en.json |- views/ |- login.html
  8. • Group in features by type js/ |- login/ |-

    config/ |- controllers/ |- directives/ |- filters/ |- services/ |- translations/ |- views/
  9. • Each file = one thing • Name it like

    you $provide it - Unless it’s related
  10. • Use suffixes or prefixes to identify the type -

    Controllers: LoginCtrl.js - Specs: LoginCtrlSpec.js - Factory services: usersFactory.js - Directives: kmTabBar.js - …
  11. Will this work? angular.module('app.presenters', []) .constant('presenters', ['Daan', 'Vincent']); angular.module('app.resources', [])

    .service('presentersFetcher', function(presenters) { presenters.push('Ibe'); return function() { return presenters; }; }); angular.module('app', ['app.presenters', 'app.resources']) .controller('AppCtrl', function($scope, presentersFetcher) { $scope.presenters = presentersFetcher(); }); http://plnkr.co/edit/2pw2XqJHax9oPyG2BbM9?p=preview
  12. Benefits • Get an overview of your dependencies • Reusability

    between projects • Faster tests • Lazy loading?
  13. Bootstrapping your app angular.module('app.common', []); angular.module('app.login', ['app.common']); angular.module('app', ['app.login']); angular.module('app.login')

    .config(['fooProvider', function(fooProvider) { // config phase }]).run([function() { // run phase }); http://plnkr.co/edit/VTVBieWqd9gq0oXJFIqS?p=preview /app.js /<module_name>/<module_name>.js
  14. Services • Should be responsible for only one thing •

    Should have a clean stable API • Should be reusable
  15. Example 1 A angular.module('app.api') .factory('socket', [function() { return { 'send':

    function(method, action, data, callback) { // return uuid }, 'unregisterCallback': function(uuid) { // unregister callback }, 'setDefaultParam': function(key, value) { // set default param }, 'deleteDefaultParam': function(key) { // delete default param } }; }]);
  16. Example 1 B angular.module('app.api') .factory('pull', ['socket', function(socket) { return {

    'get': function(action, data) { /* get */ }, 'post': function(action, body, data) { /* post */ }, 'put': function(action, body, data) { /* put */ }, 'delete': function(action, data) { /* delete */ } }; }]); angular.module('app.api') .factory('push', ['socket', function(socket) { return { 'subscribe': function(action, data) { // return subscribtion object with unsubscribe function } }; }]);
  17. Directives • Should not try to do too much •

    Should be reusable via configurable attributes
  18. Parent: I can manage. Child: Manage me! Closely related parent

    / child directives. Use parent controller to store state. http://plnkr.co/edit/T47y04WxoDK098UAdc3B?p=preview PROBLEM SOLUTION
  19. Directive A: This changed. Directive B: I know what to

    do! Directives that sort of rely on each other. Introduce shared state and $watch! Mediator Pattern http://plnkr.co/edit/gT1Olo0FwUQgXVLlH4TR?p=preview PROBLEM SOLUTION
  20. What about events? • No source of truth -> bad

    on init • When using ‘hooks’ - e.preventDefault() - e.stopPropagation()
  21. Controllers • Should be lean / small / simple -

    Controller are ‘just’ glue between models and views • Should never touch the DOM
  22. JavaScript coding style • Community driven standards - idiomatic.js, airbnb,

    ... • You should enforce these - via jshint, code reviews, ...
  23. 1. Code reviews • Why? - Improved code quality -

    Help improve each other - Fewer defects in code - Improved communication about code content • How? - Github/Bitbuckets/Gitlab Pull Requests system
  24. 2. Configuration • Easier to test • Environment specific configuration

    - dev, staging, production, … • Module based
  25. <body ng-app="app"> <div ng-view></div> <!-- build:js js/app.js --> <!-- fileblocks:js

    app --> <!-- endfileblock --> <!-- endbuild --> </body> config['fileblocks'] = { options: { removeFiles: true, cwd: paths['app'] }, app: { src: paths['tmp'] + 'index.html', blocks: { app: { src: [ 'js/app.js', 'js/*/*/**/*.js', 'js/**/*.js' ] } } } } Injecting our js files in our index.html (grunt-file-blocks)
  26. <body ng-app="app"> <div ng-view></div> <!-- build:js js/app.js --> <!-- fileblocks:js

    app --> <script src="js/app.js"></script> <script src="js/common/controllers/fooCtrl.js"></script> <script src="js/common/directives/kmTabBar.js"></script> <script src="js/login/controllers/loginCtrl.js"></script> <script src="js/login/services/login.js"></script> <script src="js/common/common.js"></script> <script src="js/login/login.js"></script> <!-- endfileblock --> <!-- endbuild --> </body> Injecting our js files in our index.html (grunt-file-blocks)
  27. ngRouter UI Router • $route / $routeProvider • Application mapped

    to urls • Flat structure • One view per route • $state / $stateProvider • Application mapped to states! • Nested states • Many views per state
  28. ngRouter overview index.html /inbox ng-view / inboxCtrl + inbox.html 1.

    email 1 2. email 2 3. email 3 4. email 4 Where your routes are rendered ng-if / ng-show / ng-switch to modify your template to show email details
  29. ngRouter, with nice urls /inbox index.html 1. email 1 2.

    email 2 3. email 3 4. email 4 ng-view InboxCtrl / inbox.html
  30. ngRouter, with nice urls /inbox/<emailId> index.html 1. email 1 2.

    email 2 3. email 3 4. email 4 ng-view InboxDetailsCtrl / inbox_details.html always show email details
  31. Pain of ngRouter • duplicate logic • content flashes -

    reload all emails & lose context • link to hardcoded urls
  32. UI Router overview index.html 1. email 1 2. email 2

    3. email 3 4. email 4 ui-view / inbox InboxCtrl / inbox.html ui-view / inbox.detail (emailId) EmailDetailsCtrl / email_details.html
  33. Benefits of UI Router • States - less brittle to

    maintain • Faster - less work to do • No content flashes - appy feel • Nested states - maps better to domain • Named views - go crazy <a ui-sref="inbox.details({'idenfifier': email.id})">{{ email.name }}</a>
  34. Routing = Configuration { "inbox": { "path": "/inbox", "controller": "InboxCtrl.js",

    "templateUrl": "inbox.html" }, "inbox.details": { "path": "/:emailId", "controller": "EmailDetailsCtrl.js", "templateUrl": "email_details.html" } } file: /inbox/config/routes.json
  35. Resolvers { "inbox": { "path": "/inbox", "controller": "InboxCtrl.js", "resolve": {

    "emails": "emailsResolver", "markAsSpamText": { "value": ["Foo!"] }, }, "templateUrl": "inbox.html" } } factory('emailsResolver', ['$http', function ($http) { return function ($stateParams) { return $http.get('/inbox', { userId: $stateParams.userId } ); }; }]);
  36. Mixins angular.module('app').factory('sortMixin', [function() { return function($scope) { $scope.sort = function(values,

    field) { /* sort logic */ }; }]); sortMixin.js { "inbox": { "controller": "MyCtrl", "mixins": ["sort"] } } routes.json
  37. Our own seed • Configuration • Routing by configuration •

    Mixins • Slimmed down version of our Gruntfile Github.COM/KUNSTMAAN/angular-SUPER-SEED
  38. Q&A