A Story about AngularJS modularization development

1,872 views 57 slides Jan 30, 2015
Slide 1
Slide 1 of 57
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24
Slide 25
25
Slide 26
26
Slide 27
27
Slide 28
28
Slide 29
29
Slide 30
30
Slide 31
31
Slide 32
32
Slide 33
33
Slide 34
34
Slide 35
35
Slide 36
36
Slide 37
37
Slide 38
38
Slide 39
39
Slide 40
40
Slide 41
41
Slide 42
42
Slide 43
43
Slide 44
44
Slide 45
45
Slide 46
46
Slide 47
47
Slide 48
48
Slide 49
49
Slide 50
50
Slide 51
51
Slide 52
52
Slide 53
53
Slide 54
54
Slide 55
55
Slide 56
56
Slide 57
57

About This Presentation

The more your AngularJS App is growing the more important is modularization. It starts with the naming convention, file structure, AMD, goes through the build process, loading and packaging strategies and ends in the browser.
We are going to give proposed solutions in practice as a ground for discu...


Slide Content

A Story about AngularJS
modularization development
Munich Meetup #4

Johannes Weber
[email protected]
@jowe
Developer at
David Amend
[email protected]
Frontend Architect at

How to handle cross file
dependency management in my angular app ?

You could use Script Tags!
-You care manually about the ordering
-No magic - you add each file dependency by your own
-download happens synchronous

<script type="text/javascript" src="/js/app.js"></script>
<script type="text/javascript" src="/js/AppConfig.js"></script>
<script type="text/javascript" src="/js/auth/AuthService.js"></script>
<script type="text/javascript" src="/js/auth/RegisterResource.js"></script>
<script type="text/javascript" src="/js/auth/RegisterService.js"></script>

What’s about loading more these 5 scripts?
...how could I handle this?

You could use AMD!
-scoped thirdparty
-correct order of “mc” + “angular.module”
-framework independant
-async file loading of all dependencies
define([“lodash”, “angular”, “mycomponents”], function(_, angular, mc) {
mc.setModel(friends: [{name : “Peter”, age : 35}]);
return angular.module('myApp', ['mycomponents'])
.controller('MyCtrl1', [‘myService’, function (myService) {
//...
}])
});

AMD: what’s that?
“mycomponents” is duplicated! Why?

define([“lodash”, “angular”, “mycomponents”], function(_, angular, mc) {
mc.setModel(friends: [{name : “Peter”, age : 35}]);
return angular.module('myApp', ['mycomponents'])
.controller('MyCtrl1', [‘myService’, function (myService) {
//...
}])
});

AMD, cool! But what’s this?
“myservice” is duplicated! Why?
●AMD + Angular Dependency

define([“angular”, “myservice”], function(angular, mc) {
mc.setModel(friends: [{name : “Peter”, age : 35}]);
return angular.module('myApp', ['myservice'])
.controller('MyCtrl1', [‘myService’, function (myService) {
//...
}])
});

DRY!

angulars DI?


define([“angular”, “mycomponents”], function(angular, mc) {
mc.setModel(friends: [{name : “Peter”, age : 35}]);
return angular.module('myApp', ['myservice'])
.controller('MyCtrl1', [‘myService’, function (myService) {
//...
}])
});

AMD
angular
1.
2.
3.

angulars DI?


define([“angular”, “mycomponents”], function(angular, mc) {

return angular.module('myApp', ['myservice'])
.controller('MyCtrl1', [‘myService’, function (myService) {
mc.setModel(friends: [{name : “Peter”, age : 35}]);
}])
});

AMD
angular
1.
2.
3.

$script([
'bower_components/angular/angular.js',
'bower_components/lodash/lodash.js',
'dist/app.js'
'dist/mycomponents.js'
], function() {
angular.bootstrap(document, ['myApp']);
});


global libs for complete app



define([“mycomponents”], function(mc) {
angular.module('myApp', ['mycomponents'])
.controller('MyCtrl1', [‘myService’, function (myService) {
mc.setModel(friends: [{name : “Peter”, age : 35}]);
//...
}]) });

3.

angular
→ used in any file !

load order must be handled?
→ anything loaded before angular.bootstrap
→ no code outside of angular.module
$script([
'vendorLibs.min.js',
'myapp.min.js'
], function() {
angular.bootstrap(document, ['myApp']);
});

How to deal with vendors?
angular + lodash
-polluting global namespace?

lodash → not used anywhere

define([“mycomponents”], function(mc) {
angular.module('myApp', ['mycomponents'])
.controller('MyCtrl1', [‘myService’, function (myService) {
var _ = myNamespace.vendorLibs.lodash;
//...
}]) });

How to deal with vendors?
polluting → namespacing

How to deal with vendors?
Use existing modules: angular-lodash
https://github.com/rockabox/ng-lodash

var app = angular.module('yourAwesomeApp', ['ngLodash']);

define([“mycomponents”], function(mc) {
angular.module('myApp', ['mycomponents'])
.controller('MyCtrl1', [‘myService’, ‘lodash’, function (myService, _) {
_.random(10);
//...
}]) });

revise angular.module order issue
angular.module('mycomponents',[])


angular.module(myapp, ['mycomponents']).service( … )


→ How to guarantee order ?

1.module registration
2.module getter
angular.module('mycomponents', [])



angular.module('mycomponents')

angular.module('mycomponents', [])


angular.module('myapp', ['mycomponents'])


angular.module(myapp).service( … )


→ Does this work ?

...
angular.module('mycomponents', [])


angular.module(myapp).controller(function(dataService){ … )


angular.module('myapp', ['mycomponents'])


angular.module(mycomponent).service(“dataService” )

angular.module('mycomponents', [])


angular.module('myapp', ['mycomponents'])


angular.module(myapp).controller( … )

angular.module(mycomponents).service( … )

angular.module(myapp).service( … )


applicationContext.js → module registration
[src/**/*.js, !applicationContext.js] → module getters
...

You can use a gulp task for that
gulp.concat([
"applicationContext.js",
"src/**/*.js"
]);

…but AMD
-handle correct loading order
-anonymouse function
-additional effort
-async: no support for all frameworks

→ solving dependency management ?
→ solving namespacing ?
→ solving async ?

Are you using A of AMD, only for DEV ?
Loading 100 single files in the browser ?

Do you distinguish between DEV and PROD?

Do you distinguish between
dev and prod with AMD ?
app.js
JS Dependency Levels
app.js
JS Dependency Levels
Development Production
build, minified, concatinated

vendorLibs.min.js
JS Dependency Levels
Production Production
Splitting your source into junks would be nicer!
app.js
JS Dependency Levels
appTemplates.min.js
myapp.min.js

$script([
'vendorLibs.min.js',
'vendorLibs2.min.js',
'myapp.min.js'
'mysubapp.min.js'
'mycommonComponents.min.js'
], function() {
angular.bootstrap(document, ['myApp']);
});


Actually you want to dev/deliver junks
How to ?

angular.module('mycomponents', [])


angular.module('myapp', ['mycomponents'])



$script([
'myapp.min.js',
'mysubapp.min.js',
'mycommonComponents.min.js'
], function() {
angular.bootstrap(document, ['myApp']);
});


applicationContext inside
angular.module(myapp).controller( … )


Async
Loader

angular-loader module
<script type=”text/javascript”>/*angular-module-loader code here*/<script>
$script([
'myapp.min.js',
'mysubapp.min.js',
'mycommonComponents.min.js'
], function() {
angular.bootstrap(document, ['myApp']);
});


angular.module('mycomponents', [])


angular.module('myapp', ['mycomponents'])



angular.module(myapp).controller( … )

How about commonjs and others ?

How about CSS/HTML/Images
(webpack) ?

How to lazy load junks of code?
-just less/no SPA ?
$script([
'mysubapp.min.js',
'mycommonComponents.min.js'
], function() {
angular.bootstrap(document, ['myApp']);
});
$script([
'myapp.min.js',
'mycommonComponents.min.js'
], function() {
angular.bootstrap(document, ['myApp']);
});
app/index.html
app/subapp/index.html
How to handle
dependencies from
app to app ?

somehow with ui router … AMD?
myApp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/state1");
$stateProvider
.state('state1', {
url: "/state1",
templateUrl: "partials/state1.html"
})

AngularAMD
https://github.com/marcoslin/angularAMD


app.config(function ($routeProvider) {
$routeProvider.when(
"/home",
angularAMD.route({
templateUrl: 'views/home.html',
controller: 'HomeController',
controllerUrl: 'scripts/controller'
})
);
});

On Demand Loading

Angular.overmind
“Easy module isolation and lazy loading for Angular apps”
var overmind = angular.module('overmind');

overmind.apps = {
'/profile': {
ngModule: 'profile',
file: 'apps/profile/profile'
},
// ...

Angular.js 2.0

What are Modules in AngularJS 2.0
import {Inject} from 'di/annotations';
import {Grinder} from './grinder';
import {Pump} from './pump';
import {Heater} from './heater';

@Inject(Grinder, Pump, Heater)
export class CoffeeMaker {
constructor(grinder, pump, heater) {
}
brew() {
console.log('Brewing a coffee...');
}}

Angular 2.0
→ combining angulars DI with ES6 loaders
→ combining ES6 loader with angular

???

→ which is preferred long time strategy ?

The end

Outlook

Outlook: AngularJS 2.0
-removal of angular.module
-Loading through ES6 modules
-Dependency Injection, Inversion of Control,
annotations

What are Modules in AngularJS 2.0

More Info in @vojtajina annotated Slides

https://dl.dropboxusercontent.com/u/36607830/talks/ng-conf-di-v2-annotated.pdf

namespacing pattern
var mynamespace = mynamespace || {};
mynamespace.subspace = mynamespace.subspace || {};
mynamespace.subspace.app = mynamespace.subspace.app || {};

module pattern
mynamespace.myObject = (function(lodash){
var myprivateVar = 123;
var publicFunction = function(){return 'Peter';});
return {
publicFunction : publicFunction
}
})(vendorLibs.lodash);

Upcoming

What is a module ?

Integrate external dependencies
Package Manager
-maven
-rpm
-bower
-npm
...
My Project
angular 1.3
angular-router
angular-ui
bootstrap
yamm3

http://www.globaldots.com/big-page-size-going-2014/

But: how to know about magic dependencies of
'vendorLibs.min.js'
→ bower.json

Using Bower for YOUR modules !
/someRepo
-mymodule/bower.json
-mysubmodule/bower.json
-vendor-libs/bower.json
-global-css/bower.json
/anotherRepo
-anothermodule/bower.json

Angular.module Graph
http://kyu-project.co/kyu.html
https://github.com/lucalanca/grunt-angular-architecture-
graph

Inner Modules = namespacing



TypeScript
import SayingsType = some.prefix.module.Sayings;
...
var myValue : SayingsType = {a: “red”, b: 2, c: true}

TypeScript to support traceability of usages
module myapp.body {
import MyserviceType = myapp.header.myService; → compiled + removed at build time
angular.module('myApp')
.controller('MyCtrl1', [‘myService’, function (myService : MyServiceType) {
var result = myService.fetchData('123');
//...
}]) }) };

declare module myapp.header {

interface responseData {
name : string,
age : number
}

interface myService {
fetchData(id : string) : responseData;
}
}


Myservice.d.ts

Dependencies @build+compile time
myApp/bower.json
devDependencies:
commonComponents : 0.0.2

TypeScript Compiler
-/src/**.*/*.ts
-bower_components/commonComponents/src/ **/*.d.ts
→ myApp.js
Read dependency
information by build tool
var bowerProps = fs.readFileSync(filePath, 'utf8').JSON.parse(bowerFile);
var deps = bowerProps.devDependencies.startsWith(“dab-*”);

TypeScript
import angular = require(“angular”)

JavaScript
CommonJS
import angular = require(“angular”)
AMD
define([“angular”], function(angular){ …}

ECMAScript6 Harmony
import * as angular from “angular”


TypeScript Outer Modules