AngularJS in practice

Preview:

DESCRIPTION

Already seen the angular basics? Ready for a deeper dive? Lets dive into how angular is being used at Dealer.com and dive deep into $compile and what those magic directive symbols really mean. If you didn't make it to VT Code Camp to see this in person, then you should read this post which is the basis for the slides. http://goo.gl/JMf0ss

Citation preview

©2014 Dealertrack, Inc. All rights reserved.

Jon HoguetSr. UI Developerjonathan.hoguet@dealer.com

AngularJS in Practice

©2014 Dealertrack, Inc. All rights reserved.

Jon Hoguet who?

©2014 Dealertrack, Inc. All rights reserved.

▪ Our Architecture▪ Overview▪ Metrics▪ File Structure▪ Lessons Learned

▪ Angular ▪ Controller▪ Directive▪ $compile▪ Directives and Isolate Scope▪ Lessons Learned

▪ Build▪ Grunt▪ Testing with Karma▪ Testing with Protractor

▪ Debugging▪ $log▪ in the browser

▪ I reading this online, you should check out the ▪ Correlating Blog Post

What to expect

©2014 Dealertrack, Inc. All rights reserved.

Our Architecture

©2014 Dealertrack, Inc. All rights reserved.

15 Angular Modules

43 Angular Directives

15 Angular Controllers

36 Angular Services

52 Angular Templates

13,669 lines of javascript

Focusing on Angular

©2014 Dealertrack, Inc. All rights reserved.

Some Humility

©2014 Dealertrack, Inc. All rights reserved.

File Structure

©2014 Dealertrack, Inc. All rights reserved.

Minimal Grails dependency

©2014 Dealertrack, Inc. All rights reserved.

Do organize around functionality

Build First

Don’t mix templating languages

Lessons Learned

©2014 Dealertrack, Inc. All rights reserved.

Don’t mix templating languages

<inventory:firedoor firedoorClass="search-firedoor lifecycle-criteria"

ngshow="lifecycles.targetLifecycle.isEditingCriteria" fluid="false"

customButtons="${[ [click:

'lifecycles.targetLifecycle.isEditingCriteria=false', icon: 'check', text: 'Done', customClass:

'btn-primary'] ]}">

<div class="form-group lifecycle-name-group">

<input type="text" class="form-control input-lg"

ng-model="lifecycles.targetLifecycle.model.name"

ng-change="lifecycles.targetLifecycle.dirty()"

placeholder="Enter a name...">

</div><div criteria-form title="Lifecycle Criteria"

criteria-model="lifecycles.targetLifecycle.model.criteria"

dirty="lifecycles.targetLifecycle.dirty">

</div>

</inventory:firedoor>

©2014 Dealertrack, Inc. All rights reserved.

Controllers vs Directives

©2014 Dealertrack, Inc. All rights reserved.

A controller is an abstraction of

a directive

©2014 Dealertrack, Inc. All rights reserved.

Consider...

©2014 Dealertrack, Inc. All rights reserved.

Controller right?

©2014 Dealertrack, Inc. All rights reserved.

Somewhere else in the dom...

©2014 Dealertrack, Inc. All rights reserved.

Controller abstraction of directive isn’t working… use a directive

©2014 Dealertrack, Inc. All rights reserved.

©2014 Dealertrack, Inc. All rights reserved.

$compile = BFF

$compile( markup / directives )( scope ) = live dom

fragment

1. Most important thing angular does2. composable3. makes testing easy4. makes experimentation easy

©2014 Dealertrack, Inc. All rights reserved.

live dom element

var ioc = angular.element('[ng-app]').injector(),

$rootScope = ioc.get('$rootScope'),

$compile = ioc.get('$compile');

var scope = $rootScope.$new(true);

scope.child = {

name : 'Lance',

age : 3

};

var markup = "<div>{{ child.name }} is {{ child.age }} years old!</div>";

var el;

scope.$apply(function(){

el = $compile(markup)(scope);

});

// el.html() === 'Lance is 3 years old!'

©2014 Dealertrack, Inc. All rights reserved.

live dom element

var ioc = angular.element('[ng-app]').injector(),

$rootScope = ioc.get('$rootScope'),

$compile = ioc.get('$compile');

var scope = $rootScope.$new(true);

scope.child = {

name : 'Lance',

age : 3

};

var markup = "<div>{{ child.name }} is {{ child.age }} years old!</div>";

var el;

scope.$apply(function(){

el = $compile(markup)(scope);

});

// el.html() === 'Lance is 3 years old!'

©2014 Dealertrack, Inc. All rights reserved.

live dom element

var ioc = angular.element('[ng-app]').injector(),

$rootScope = ioc.get('$rootScope'),

$compile = ioc.get('$compile');

var scope = $rootScope.$new(true);

scope.child = {

name : 'Lance',

age : 3

};

var markup = "<div>{{ child.name }} is {{ child.age }} years old!</div>";

var el;

scope.$apply(function(){

el = $compile(markup)(scope);

});

// el.html() === 'Lance is 3 years old!'

©2014 Dealertrack, Inc. All rights reserved.

live dom element

var ioc = angular.element('[ng-app]').injector(),

$rootScope = ioc.get('$rootScope'),

$compile = ioc.get('$compile');

var scope = $rootScope.$new(true);

scope.child = {

name : 'Lance',

age : 3

};

var markup = "<div>{{ child.name }} is {{ child.age }} years old!</div>";

var el;

scope.$apply(function(){

el = $compile(markup)(scope);

});

// el.html() === 'Lance is 3 years old!'

©2014 Dealertrack, Inc. All rights reserved.

live dom element

var ioc = angular.element('[ng-app]').injector(),

$rootScope = ioc.get('$rootScope'),

$compile = ioc.get('$compile');

var scope = $rootScope.$new(true);

scope.child = {

name : 'Lance',

age : 3

};

var markup = "<div>{{ child.name }} is {{ child.age }} years old!</div>";

var el;

scope.$apply(function(){

el = $compile(markup)(scope);

});

// el.html() === 'Lance is 3 years old!'

©2014 Dealertrack, Inc. All rights reserved.

live dom element

var ioc = angular.element('[ng-app]').injector(),

$rootScope = ioc.get('$rootScope'),

$compile = ioc.get('$compile');

var scope = $rootScope.$new(true);

scope.child = {

name : 'Lance',

age : 3

};

var markup = "<div>{{ child.name }} is {{ child.age }} years old!</div>";

var el;

scope.$apply(function(){

el = $compile(markup)(scope);

});

// el.html() === 'Lance is 3 years old!'

©2014 Dealertrack, Inc. All rights reserved.

What do you mean live?

scope.$apply(function(){

scope.child = {

name : 'Lana',

age : 2

};

});

//el.html() === 'Lana is 2 years old!'

©2014 Dealertrack, Inc. All rights reserved.

Live meaning 2 way

markup = "<input type=\"text\" ng-model=\"child.name\" />";

var input;

// note we are applying the same scope to this markup

scope.$apply(function(){

input = $compile(markup)(scope);

});

input.val('Lana Hoguet');

input.change();

// scope.name === 'Lana Hoguet'

// el.html() === 'Lana Hoguet is 2 years old!'

©2014 Dealertrack, Inc. All rights reserved.

Live meaning 2 way

markup = "<input type=\"text\" ng-model=\"child.name\" />";

var input;

// note we are applying the same scope to this markup

scope.$apply(function(){

input = $compile(markup)(scope);

});

input.val('Lana Hoguet');

input.change();

// scope.name === 'Lana Hoguet'

// el.html() === 'Lana Hoguet is 2 years old!'

©2014 Dealertrack, Inc. All rights reserved.

Live meaning 2 way

markup = "<input type=\"text\" ng-model=\"child.name\" />";

var input;

// note we are applying the same scope to this markup

scope.$apply(function(){

input = $compile(markup)(scope);

});

input.val('Lana Hoguet');

input.change();

// scope.child.name === 'Lana Hoguet'

// el.html() === 'Lana Hoguet is 2 years old!'

©2014 Dealertrack, Inc. All rights reserved.

Live meaning 2 way

markup = "<input type=\"text\" ng-model=\"child.name\" />";

var input;

// note we are applying the same scope to this markup

scope.$apply(function(){

input = $compile(markup)(scope);

});

input.val('Lana Hoguet');

input.change();

// scope.child.name === 'Lana Hoguet'

// el.html() === 'Lana Hoguet is 2 years old!'

©2014 Dealertrack, Inc. All rights reserved.

Hurt yet?

©2014 Dealertrack, Inc. All rights reserved.

Directives Refresher

©2014 Dealertrack, Inc. All rights reserved.

Directives Refresher

= for two way

@ for one way

& for functions

©2014 Dealertrack, Inc. All rights reserved.

one way / two way misnomer

©2014 Dealertrack, Inc. All rights reserved.

one way / two way misnomer$compile source code

©2014 Dealertrack, Inc. All rights reserved.

one way / two way misnomer$compile source code

©2014 Dealertrack, Inc. All rights reserved.

one way / two way misnomer

Note: $interpolate and $parse are not coupled to $scope

©2014 Dealertrack, Inc. All rights reserved.

= for two way uses $parse

@ for one way uses $interpolate

& for functions

©2014 Dealertrack, Inc. All rights reserved.

& demystified

©2014 Dealertrack, Inc. All rights reserved.

& demystified

$compile source code

©2014 Dealertrack, Inc. All rights reserved.

= for two way uses $parse

@ for one way uses $interpolate

& for functions wraps $parse in fn

©2014 Dealertrack, Inc. All rights reserved.

= for two way uses $parse and watches

@ for one way uses $interpolate and watches

& for functions wraps $parse in fn

©2014 Dealertrack, Inc. All rights reserved.

©2014 Dealertrack, Inc. All rights reserved.

Beware the ng-model monster

fiddle

©2014 Dealertrack, Inc. All rights reserved.

©2014 Dealertrack, Inc. All rights reserved.

©2014 Dealertrack, Inc. All rights reserved.

angular-templates

Not proposing you use inline scripts - just simple example

©2014 Dealertrack, Inc. All rights reserved.

angular-templates

$templateCache

$http

Not proposing you use inline scripts - just simple example

©2014 Dealertrack, Inc. All rights reserved.

angular-templates

ng-include, ng-view, ui-view, or templateUrl

$http $templateCache

©2014 Dealertrack, Inc. All rights reserved.

angular-templates

npm install grunt-angular-templates --save-dev

©2014 Dealertrack, Inc. All rights reserved.

angular-templates

©2014 Dealertrack, Inc. All rights reserved.

angular-modules

©2014 Dealertrack, Inc. All rights reserved.

angular-modules

©2014 Dealertrack, Inc. All rights reserved.

©2014 Dealertrack, Inc. All rights reserved.

Unit testing directives is easy!

©2014 Dealertrack, Inc. All rights reserved.

Protractor

Not Just for Angular

But has angular specific hooks built in

Promises!!

©2014 Dealertrack, Inc. All rights reserved.

Debugging - Log More

©2014 Dealertrack, Inc. All rights reserved.

Debugging - In the Browser

You can get anything out of IOC

©2014 Dealertrack, Inc. All rights reserved.

Debugging - In the Browser

You can inspect any scope

©2014 Dealertrack, Inc. All rights reserved.

Debugging - In the Browser

You can mutate the scope and verify how the view reacts

©2014 Dealertrack, Inc. All rights reserved.

Conclusion