SlideShare a Scribd company logo
1 of 88
Download to read offline
Design Strategies with 
AngularJS 
1
Design Strategies with 
AngularJS 
1 
Somik Raha
Design Strategies with 
AngularJS 
Somik Raha Kai Wu 
1
Design Strategies with 
AngularJS 
Somik Raha Kai Wu 
1 
#smartorgdev
Rip Van Winkle 
Slept through 
the American 
Revolution 
Slept off in 2004 
and woke up in 
2014 2
3
2004… 
3
2004… 
3
2004… 
C++ 
3
2004… 
C++ Java 
3
2004… 
C++ Java 
Microsoft 
3
2004… 
C++ Java 
Microsoft Sun 
3
2004… 
C++ Java 
Microsoft Sun 
#smartorgdev 3
2014. 
C++ Java 
Microsoft Sun 
#smartorgdev 4
2014. 
Javascript 
#smartorgdev 4
Backend 
#smartorgdev 5
Backend Middleware 
#smartorgdev 5
Backend Middleware Front-end 
#smartorgdev 5
Backend Middleware Front-end 
#smartorgdev 5
Backend Middleware Front-end 
#smartorgdev 6
Super-heroic Javascript framework 
#smartorgdev 7
Super-heroic Javascript framework 
#smartorgdev 7
First reaction 
I don’t get it. Backbone is great for us. 
#smartorgdev 8
Second reaction 
Whoa!! 
#smartorgdev 9
Backbone learning curve AngularJS learning curve 
Flattens quickly 
Minimal concepts 
10 
Service 
Directives 
Controllers 
and views 
Staircase 
… 
!
Backbone learning curve AngularJS learning curve 
Flattens quickly 
Minimal concepts 
10 
Ben Nadel’s blog
Stats comparing Backbone 
with AngularJS 
AppStructure Wizard 
#smartorgdev 11
Stats comparing Backbone 
with AngularJS 
AppStructure Wizard 
#smartorgdev 11
Stats comparing Backbone 
with AngularJS 
AppStructure Wizard 
#smartorgdev 11
Three Kinds of Scenarios 
Single Page 
Application 
Multi-Page 
Application 
Legacy 
Application 
Classic Sophisticated Convoluted 
#smartorgdev 12
Multi-Page Application 
Browser loads entire page 
e.g. Login 
e.g. Show 
Projects 
Page 1 Page 2 Page 3 
View 1 View 2 View 3 
Controller 1 Controller 2 Controller 3 
Route 1 Route 2 Route 3 
/login /showProjects /… 
Map routes to controllers and views 
#smartorgdev 13
angular.module('myApp', [ 
'ngRoute', 
… 
]).config(function ($routeProvider) { 
$routeProvider.when('/route1', { 
controller: 'Controller1 as c1', 
templateUrl: 'views/view1.html', 
}).when('/route2', { 
controller: 'Controller2 as c2', 
templateUrl: 'views/view2.html', 
}).otherwise({ 
redirectTo: '/route1' 
}); 
}); 
Route1 
Controller1 
View1 
Route2 
Controller2 
View2 
Map routes to controllers and views 
app.js 
14
angular.module('myApp', [ 
'ngRoute', 
… 
]).config(function ($routeProvider) { 
$routeProvider.when('/showProjects', { 
controller: 'ShowProjectsController as show', 
templateUrl: ‘views/showProjects.html’, 
}).when('/login', { 
controller: 'LoginController as login', 
templateUrl: 'views/login.html', 
}).otherwise({ 
redirectTo: '/login' 
}); 
}); 
Routes! 
http://../login 
http://../showProjects 
app.js 
15
angular.module('myApp', [ 
'ngRoute', 
… 
]).config(function ($routeProvider) { 
$routeProvider.when('/showProjects', { 
controller: 'ShowProjectsController as show', 
templateUrl: ‘views/showProjects.html’, 
}).when('/login', { 
controller: 'LoginController as login', 
templateUrl: 'views/login.html', 
}).otherwise({ 
redirectTo: '/login' 
}); 
}); 
Routes! 
http://../login 
http://../showProjects 
app.js 
15
when('/login', { 
controller: 'LoginController as login', 
templateUrl: 'views/login.html', 
}) 
Routes! 
http://../login 
http://../showProjects 
app.js 
16
! 
class LoginController { 
$location: ng.ILocationService; 
password: string; 
userName: string; 
constructor($location:ng.ILocationService) { 
this.$location = $location; 
this.userName = ""; 
this.password = ""; 
} 
doLogin() { 
// Call your login code with a call back to loginSuccessFn 
callSomeAuthCode(this.userName, this.password) 
.then((response) => this.onLoginSuccess(response)) 
} 
onLoginSuccess(response){ 
if (response.status) { 
this.$location.path("/showProjects"); 
} else { 
this.$location.path("/login"); 
console.log("Login failed. You cannot proceed.") 
} 
} 
} 
! 
! 
<form name="loginForm" class="navbar-form navbar-left form-signin"> 
<input ng-model="login.userName" class="form-control" placeholder="User Name" required autofocus> 
<input ng-model="login.password" type="password" class="form-control" placeholder="Password" 
required> 
<button ng-click="login.doLogin()" class="btn btn-primary" type="submit">Sign in</button> 
</form> 
login.html 
TypeScript 
Simple OO Code 
AngularJS 
Creates an object in app.js 
app.js 
when('/login', { 
controller: 'LoginController as login', 
templateUrl: 'views/login.html', 
}) 
17
! 
class LoginController { 
$location: ng.ILocationService; 
password: string; 
userName: string; 
constructor($location:ng.ILocationService) { 
this.$location = $location; 
this.userName = ""; 
this.password = ""; 
} 
doLogin() { 
// Call your login code with a call back to loginSuccessFn 
callSomeAuthCode(this.userName, this.password) 
.then((response) => this.onLoginSuccess(response)) 
} 
onLoginSuccess(response){ 
if (response.status) { 
this.$location.path("/showProjects"); 
} else { 
this.$location.path("/login"); 
console.log("Login failed. You cannot proceed.") 
} 
} 
} 
! 
! 
<form name="loginForm" class="navbar-form navbar-left form-signin"> 
<input ng-model="login.userName" class="form-control" placeholder="User Name" required autofocus> 
<input ng-model="login.password" type="password" class="form-control" placeholder="Password" 
required> 
<button ng-click="login.doLogin()" class="btn btn-primary" type="submit">Sign in</button> 
</form> 
login.html 
TypeScript 
Simple OO Code 
AngularJS 
Creates an object in app.js 
app.js 
Pattern 
! 
Controller in Typescript Classes! 
! 
Packages the controller logic and makes 
it much easier to read and test. 
when('/login', { 
controller: 'LoginController as login', 
templateUrl: 'views/login.html', 
}) 
17
! 
class LoginController { 
$location: ng.ILocationService; 
password: string; 
userName: string; 
constructor($location:ng.ILocationService) { 
this.$location = $location; 
this.userName = ""; 
this.password = ""; 
} 
doLogin() { 
// Call your login code with a call back to loginSuccessFn 
callSomeAuthCode(this.userName, this.password) 
.then((response) => this.onLoginSuccess(response)) 
} 
onLoginSuccess(response){ 
if (response.status) { 
this.$location.path("/showProjects"); 
} else { 
this.$location.path("/login"); 
console.log("Login failed. You cannot proceed.") 
} 
} 
} 
! 
! 
<form name="loginForm" class="navbar-form navbar-left form-signin"> 
<input ng-model="login.userName" class="form-control" placeholder="User Name" required autofocus> 
<input ng-model="login.password" type="password" class="form-control" placeholder="Password" 
required> 
<button ng-click="login.doLogin()" class="btn btn-primary" type="submit">Sign in</button> 
</form> 
login.html 
TypeScript 
Simple OO Code 
AngularJS 
Creates an object in app.js 
app.js 
when('/login', { 
controller: 'LoginController as login', 
templateUrl: 'views/login.html', 
}) 
17
! 
class LoginController { 
$location: ng.ILocationService; 
password: string; 
userName: string; 
constructor($location:ng.ILocationService) { 
this.$location = $location; 
this.userName = ""; 
this.password = ""; 
} 
doLogin() { 
// Call your login code with a call back to loginSuccessFn 
callSomeAuthCode(this.userName, this.password) 
.then((response) => this.onLoginSuccess(response)) 
} 
onLoginSuccess(response){ 
if (response.status) { 
this.$location.path("/showProjects"); 
} else { 
this.$location.path("/login"); 
console.log("Login failed. You cannot proceed.") 
} 
} 
} 
! 
! 
<form name="loginForm" class="navbar-form navbar-left form-signin"> 
<input ng-model="login.userName" class="form-control" placeholder="User Name" required autofocus> 
<input ng-model="login.password" type="password" class="form-control" placeholder="Password" 
required> 
<button ng-click="login.doLogin()" class="btn btn-primary" type="submit">Sign in</button> 
</form> 
login.html 
TypeScript 
Simple OO Code 
AngularJS 
Creates an object in app.js 
app.js 
when('/login', { 
controller: 'LoginController as login', 
templateUrl: 'views/login.html', 
}) 
17
! 
class LoginController { 
$location: ng.ILocationService; 
password: string; 
userName: string; 
constructor($location:ng.ILocationService) { 
this.$location = $location; 
this.userName = ""; 
this.password = ""; 
} 
doLogin() { 
// Call your login code with a call back to loginSuccessFn 
callSomeAuthCode(this.userName, this.password) 
.then((response) => this.onLoginSuccess(response)) 
} 
onLoginSuccess(response){ 
if (response.status) { 
this.$location.path("/showProjects"); 
} else { 
this.$location.path("/login"); 
console.log("Login failed. You cannot proceed.") 
} 
} 
} 
! 
! 
<form name="loginForm" class="navbar-form navbar-left form-signin"> 
<input ng-model="login.userName" class="form-control" placeholder="User Name" required autofocus> 
<input ng-model="login.password" type="password" class="form-control" placeholder="Password" 
required> 
<button ng-click="login.doLogin()" class="btn btn-primary" type="submit">Sign in</button> 
</form> 
login.html 
TypeScript 
Simple OO Code 
AngularJS 
Creates an object in app.js 
app.js 
when('/login', { 
controller: 'LoginController as login', 
templateUrl: 'views/login.html', 
}) 
17
! 
class LoginController { 
$location: ng.ILocationService; 
password: string; 
userName: string; 
constructor($location:ng.ILocationService) { 
this.$location = $location; 
this.userName = ""; 
this.password = ""; 
} 
doLogin() { 
// Call your login code with a call back to loginSuccessFn 
callSomeAuthCode(this.userName, this.password) 
.then((response) => this.onLoginSuccess(response)) 
} 
onLoginSuccess(response){ 
if (response.status) { 
this.$location.path("/showProjects"); 
} else { 
this.$location.path("/login"); 
console.log("Login failed. You cannot proceed.") 
} 
} 
} 
! 
! 
<form name="loginForm" class="navbar-form navbar-left form-signin"> 
<input ng-model="login.userName" class="form-control" placeholder="User Name" required autofocus> 
<input ng-model="login.password" type="password" class="form-control" placeholder="Password" 
required> 
<button ng-click="login.doLogin()" class="btn btn-primary" type="submit">Sign in</button> 
</form> 
login.html 
TypeScript 
Simple OO Code 
AngularJS 
Creates an object in app.js 
app.js 
when('/login', { 
controller: 'LoginController as login', 
templateUrl: 'views/login.html', 
}) 
17
! 
class LoginController { 
$location: ng.ILocationService; 
password: string; 
userName: string; 
constructor($location:ng.ILocationService) { 
this.$location = $location; 
this.userName = ""; 
this.password = ""; 
} 
doLogin() { 
// Call your login code with a call back to loginSuccessFn 
callSomeAuthCode(this.userName, this.password) 
.then((response) => this.onLoginSuccess(response)) 
} 
onLoginSuccess(response){ 
if (response.status) { 
this.$location.path("/showProjects"); 
} else { 
this.$location.path("/login"); 
console.log("Login failed. You cannot proceed.") 
} 
} 
} 
! 
! 
<form name="loginForm" class="navbar-form navbar-left form-signin"> 
<input ng-model="login.userName" class="form-control" placeholder="User Name" required autofocus> 
<input ng-model="login.password" type="password" class="form-control" placeholder="Password" 
required> 
<button ng-click="login.doLogin()" class="btn btn-primary" type="submit">Sign in</button> 
</form> 
login.html 
TypeScript 
Simple OO Code 
AngularJS 
Creates an object in app.js 
app.js 
Optional Pattern 
! 
Named Controller Objects! 
! 
Maintains modularity and avoids having 
to put data into scope objects, which 
hold so much other stuff, making it easier 
to debug. 
when('/login', { 
controller: 'LoginController as login', 
templateUrl: 'views/login.html', 
}) 
17
! 
class LoginController { 
$location: ng.ILocationService; 
password: string; 
userName: string; 
constructor($location:ng.ILocationService) { 
this.$location = $location; 
this.userName = ""; 
this.password = ""; 
} 
doLogin() { 
// Call your login code with a call back to loginSuccessFn 
callSomeAuthCode(this.userName, this.password) 
.then((response) => this.onLoginSuccess(response)) 
} 
onLoginSuccess(response){ 
if (response.status) { 
this.$location.path("/showProjects"); 
} else { 
this.$location.path("/login"); 
console.log("Login failed. You cannot proceed.") 
} 
} 
} 
! 
! function LoginController($scope, $location) { 
$scope.userName = ""; 
$scope.password = ""; 
$scope.doLogin = function() { 
// Call your login code with a call back to loginSuccessFn 
callSomeAuthCode($scope.userName, $scope.password) 
.then($scope.onLoginSuccess(response)) 
} 
$scope.onLoginSuccess = function(response){ 
if (response.status) { 
$location.path("/showProjects"); 
} else { 
$location.path("/login"); 
console.log("Login failed. You cannot proceed.") 
} 
} 
} 
!! 
Typescript provides 
more readable structure 
More funky 
All initialization is in constructor 
Code completion; No $scope 
Initialization is not clearly demarcated, 
declarations interspersed with execution 
! 
No code completion 
18 
Controller in Typescript Classes
19
19
Single-Page Application 
Rich interactions within a single page, loaded only once 
Navigation 
Workspace 
Actions 
Menu 
#smartorgdev 20
#smartorgdev 21
#smartorgdev 21
If we have only one controller and one view, 
how do we prevent our code from getting 
bloated? 
22
Directives to the rescue 
index.html 
<body ng-app="myApp" ng-controller="myController"> 
<div class="row"> 
<div top-menu-view …></div> 
</div> 
<div class="row"> 
<div id="leftPanel"> 
<div nav-widget …></div> 
</div> 
<div id="middlePanel"> 
<div workspace-contents …></div> 
</div> 
<div id="rightPanel" > 
<div action-menu …></div> 
</div> 
</div> 
</div> 
… 
</body> 
23 
template.html 
directive.js
Directives to the rescue 
index.html 
<body ng-app="myApp" ng-controller="myController"> 
<div class="row"> 
<div top-menu-view …></div> 
</div> 
<div class="row"> 
<div id="leftPanel"> 
<div nav-widget …></div> 
</div> 
<div id="middlePanel"> 
<div workspace-contents …></div> 
</div> 
<div id="rightPanel" > 
<div action-menu …></div> 
</div> 
</div> 
</div> 
… 
</body> 
23 
template.html 
directive.js 
Pattern: ! 
Decompose view with directives
Directives to the rescue 
index.html 
<body ng-app="myApp" ng-controller="myController"> 
<div class="row"> 
<div top-menu-view …></div> 
</div> 
<div class="row"> 
<div id="leftPanel"> 
<div nav-widget …></div> 
</div> 
<div id="middlePanel"> 
<div workspace-contents …></div> 
</div> 
<div id="rightPanel" > 
<div action-menu …></div> 
</div> 
</div> 
</div> 
… 
</body> 
23 
template.html 
directive.js
But wait, how do we pass data between 
directives? 
Navigation 
Workspace 
Actions 
Menu 
e.g. How does the Navigation directive find out about a 
selection in the Menu directive? 
#smartorgdev 24
But wait, how do we pass data between 
directives? 
Navigation 
Workspace 
Actions 
Menu 
e.g. list of trees 
e.g. How does the Navigation directive find out which 
tree has been selected in the Menu directive? 
#smartorgdev 25
myController 
angular.module('myApp') 
.controller('myController', 
function ($scope, $route, $routeParams, $location) { 
$scope.action = 'login'; 
$scope.$on("$routeChangeSuccess", 
function( $currentRoute, $previousRoute ) { 
$scope.action = $route.current.action; 
}); 
$scope.login = function() { 
// do login, and on success: 
$location.path('/home'); 
} 
$scope.onSelectTree = function(treeID) { 
// go fetch the tree using the treeID, … 
// myTree holds the tree 
$scope.$broadcast("treeLoaded", { 
"myTree": myTree 
}); 
} 
}); 
navigation 
directive 
angular.module('myApp') 
.directive("navigation", function() { 
return { 
restrict: 'A', 
scope: { 
myTree: '=' 
} 
}, 
templateUrl: '...', 
link: function(scope) { 
scope.$on('treeLoaded', function() { 
makeTreeWith(scope.myTree); 
//.. 
} 
} 
}); 
menu 
directive 
angular.module('myApp') 
.directive("topMenuView", function() { 
return { 
restrict: 'A', 
scope: { 
menuData: '=', 
onSelectTree: '&' 
} 
}, 
templateUrl: '...', 
link: function(scope) { 
scope.chooseTree = function(tree){ 
scope.onSelectTree({treeID: tree.id}); 
}; 
} 
}); 
26
myController 
angular.module('myApp') 
.controller('myController', 
function ($scope, $route, $routeParams, $location) { 
$scope.action = 'login'; 
$scope.$on("$routeChangeSuccess", 
function( $currentRoute, $previousRoute ) { 
$scope.action = $route.current.action; 
}); 
$scope.login = function() { 
// do login, and on success: 
$location.path('/home'); 
} 
$scope.onSelectTree = function(treeID) { 
// go fetch the tree using the treeID, … 
// myTree holds the tree 
$scope.$broadcast("treeLoaded", { 
"myTree": myTree 
}); 
} 
}); 
navigation 
directive 
angular.module('myApp') 
.directive("navigation", function() { 
return { 
restrict: 'A', 
scope: { 
myTree: '=' 
} 
}, 
templateUrl: '...', 
link: function(scope) { 
scope.$on('treeLoaded', function() { 
makeTreeWith(scope.myTree); 
//.. 
} 
} 
}); 
menu 
directive 
angular.module('myApp') 
.directive("topMenuView", function() { 
return { 
restrict: 'A', 
scope: { 
menuData: '=', 
onSelectTree: '&' 
} 
}, 
templateUrl: '...', 
link: function(scope) { 
scope.chooseTree = function(tree){ 
scope.onSelectTree({treeID: tree.id}); 
}; 
} 
}); 
26
myController 
angular.module('myApp') 
.controller('myController', 
function ($scope, $route, $routeParams, $location) { 
$scope.action = 'login'; 
$scope.$on("$routeChangeSuccess", 
function( $currentRoute, $previousRoute ) { 
$scope.action = $route.current.action; 
}); 
$scope.login = function() { 
// do login, and on success: 
$location.path('/home'); 
} 
$scope.onSelectTree = function(treeID) { 
// go fetch the tree using the treeID, … 
// myTree holds the tree 
$scope.$broadcast("treeLoaded", { 
"myTree": myTree 
}); 
} 
}); 
navigation 
directive 
angular.module('myApp') 
.directive("navigation", function() { 
return { 
restrict: 'A', 
scope: { 
myTree: '=' 
} 
}, 
templateUrl: '...', 
link: function(scope) { 
scope.$on('treeLoaded', function() { 
makeTreeWith(scope.myTree); 
//.. 
} 
} 
}); 
menu 
directive 
angular.module('myApp') 
.directive("topMenuView", function() { 
return { 
restrict: 'A', 
scope: { 
menuData: '=', 
onSelectTree: '&' 
} 
}, 
templateUrl: '...', 
link: function(scope) { 
scope.chooseTree = function(tree){ 
scope.onSelectTree({treeID: tree.id}); 
}; 
} 
}); 
26
myController 
angular.module('myApp') 
.controller('myController', 
function ($scope, $route, $routeParams, $location) { 
$scope.action = 'login'; 
$scope.$on("$routeChangeSuccess", 
function( $currentRoute, $previousRoute ) { 
$scope.action = $route.current.action; 
}); 
$scope.login = function() { 
// do login, and on success: 
$location.path('/home'); 
} 
$scope.onSelectTree = function(treeID) { 
// go fetch the tree using the treeID, … 
// myTree holds the tree 
$scope.$broadcast("treeLoaded", { 
"myTree": myTree 
}); 
} 
}); 
navigation 
directive 
angular.module('myApp') 
.directive("navigation", function() { 
return { 
restrict: 'A', 
scope: { 
myTree: '=' 
} 
}, 
templateUrl: '...', 
link: function(scope) { 
scope.$on('treeLoaded', function() { 
makeTreeWith(scope.myTree); 
//.. 
} 
} 
}); 
menu 
directive 
angular.module('myApp') 
.directive("topMenuView", function() { 
return { 
restrict: 'A', 
scope: { 
menuData: '=', 
onSelectTree: '&' 
} 
}, 
templateUrl: '...', 
link: function(scope) { 
scope.chooseTree = function(tree){ 
scope.onSelectTree({treeID: tree.id}); 
}; 
} 
}); 
26
myController 
angular.module('myApp') 
.controller('myController', 
function ($scope, $route, $routeParams, $location) { 
$scope.action = 'login'; 
$scope.$on("$routeChangeSuccess", 
function( $currentRoute, $previousRoute ) { 
$scope.action = $route.current.action; 
}); 
$scope.login = function() { 
// do login, and on success: 
$location.path('/home'); 
} 
$scope.onSelectTree = function(treeID) { 
// go fetch the tree using the treeID, … 
// myTree holds the tree 
$scope.$broadcast("treeLoaded", { 
"myTree": myTree 
}); 
} 
}); 
navigation 
directive 
angular.module('myApp') 
.directive("navigation", function() { 
return { 
restrict: 'A', 
scope: { 
myTree: '=' 
} 
}, 
templateUrl: '...', 
link: function(scope) { 
scope.$on('treeLoaded', function() { 
makeTreeWith(scope.myTree); 
//.. 
} 
} 
}); 
menu 
directive 
angular.module('myApp') 
.directive("topMenuView", function() { 
return { 
restrict: 'A', 
scope: { 
menuData: '=', 
onSelectTree: '&' 
} 
}, 
templateUrl: '...', 
link: function(scope) { 
scope.chooseTree = function(tree){ 
scope.onSelectTree({treeID: tree.id}); 
}; 
} 
}); 
26
myController 
angular.module('myApp') 
.controller('myController', 
function ($scope, $route, $routeParams, $location) { 
$scope.action = 'login'; 
$scope.$on("$routeChangeSuccess", 
function( $currentRoute, $previousRoute ) { 
$scope.action = $route.current.action; 
}); 
$scope.login = function() { 
// do login, and on success: 
$location.path('/home'); 
} 
$scope.onSelectTree = function(treeID) { 
// go fetch the tree using the treeID, … 
// myTree holds the tree 
$scope.$broadcast("treeLoaded", { 
"myTree": myTree 
}); 
} 
}); 
navigation 
directive 
angular.module('myApp') 
.directive("navigation", function() { 
return { 
restrict: 'A', 
scope: { 
myTree: '=' 
} 
}, 
templateUrl: '...', 
link: function(scope) { 
scope.$on('treeLoaded', function() { 
makeTreeWith(scope.myTree); 
//.. 
} 
} 
}); 
menu 
directive 
angular.module('myApp') 
.directive("topMenuView", function() { 
return { 
restrict: 'A', 
scope: { 
menuData: '=', 
onSelectTree: '&' 
} 
}, 
templateUrl: '...', 
link: function(scope) { 
scope.chooseTree = function(tree){ 
scope.onSelectTree({treeID: tree.id}); 
}; 
} 
}); 
26
myController 
angular.module('myApp') 
.controller('myController', 
function ($scope, $route, $routeParams, $location) { 
$scope.action = 'login'; 
$scope.$on("$routeChangeSuccess", 
function( $currentRoute, $previousRoute ) { 
$scope.action = $route.current.action; 
}); 
$scope.login = function() { 
// do login, and on success: 
$location.path('/home'); 
} 
$scope.onSelectTree = function(treeID) { 
// go fetch the tree using the treeID, … 
// myTree holds the tree 
$scope.$broadcast("treeLoaded", { 
"myTree": myTree 
}); 
} 
}); 
navigation 
directive 
angular.module('myApp') 
.directive("navigation", function() { 
return { 
restrict: 'A', 
scope: { 
myTree: '=' 
} 
}, 
templateUrl: '...', 
link: function(scope) { 
scope.$on('treeLoaded', function() { 
makeTreeWith(scope.myTree); 
//.. 
} 
} 
}); 
menu 
directive 
angular.module('myApp') 
.directive("topMenuView", function() { 
return { 
restrict: 'A', 
scope: { 
menuData: '=', 
onSelectTree: '&' 
} 
}, 
templateUrl: '...', 
link: function(scope) { 
scope.chooseTree = function(tree){ 
scope.onSelectTree({treeID: tree.id}); 
}; 
} 
}); 
But wait, how does scope.onSelectTree 
work correctly when called in the menu 
directive? 
26
index.html 
menu 
directive 
angular.module('myApp') 
.directive("topMenuView", function() { 
return { 
restrict: 'A', 
scope: { 
menuData: '=', 
onSelectTree: '&' 
} 
}, 
templateUrl: '...', 
link: function(scope) { 
scope.chooseTree = function(tree){ 
scope.onSelectTree({treeID: tree.id}); 
}; 
} 
}); 
27 
<body ng-app="myApp" ng-controller="myController"> 
<div id="homescope"> 
<div class="row"> 
<div top-menu-view …></div> 
</div> 
<div class="row"> 
<div id="leftPanel"> 
<div nav-widget …></div> 
</div> 
<div id="middlePanel"> 
<div workspace-contents …></div> 
</div> 
<div id="rightPanel" > 
<div action-menu …></div> 
</div> 
</div> 
</div> 
… 
</body>
index.html 
menu 
directive 
angular.module('myApp') 
.directive("topMenuView", function() { 
return { 
restrict: 'A', 
scope: { 
menuData: '=', 
onSelectTree: '&' 
} 
}, 
templateUrl: '...', 
link: function(scope) { 
scope.chooseTree = function(tree){ 
scope.onSelectTree({treeID: tree.id}); 
}; 
} 
}); 
27 
<body ng-app="myApp" ng-controller="myController"> 
<div id="homescope"> 
<div class="row"> 
<div top-menu-view …></div> 
</div> 
<div class="row"> 
<div id="leftPanel"> 
<div nav-widget …></div> 
</div> 
<div id="middlePanel"> 
<div workspace-contents …></div> 
</div> 
<div id="rightPanel" > 
<div action-menu …></div> 
</div> 
</div> 
</div> 
… 
</body>
index.html 
menu 
directive 
angular.module('myApp') 
.directive("topMenuView", function() { 
return { 
restrict: 'A', 
scope: { 
menuData: '=', 
onSelectTree: '&' 
} 
}, 
templateUrl: '...', 
link: function(scope) { 
scope.chooseTree = function(tree){ 
scope.onSelectTree({treeID: tree.id}); 
}; 
} 
}); 
27 
<body ng-app="myApp" ng-controller="myController"> 
<div id="homescope"> 
<div class="row"> 
<div top-menu-view …></div> 
</div> 
<div class="row"> 
<div id="leftPanel"> 
<div nav-widget …></div> 
</div> 
<div id="middlePanel"> 
<div workspace-contents …></div> 
</div> 
<div id="rightPanel" > 
<div action-menu …></div> 
</div> 
</div> 
</div> 
… 
</body>
<div top-menu-view menu-data="menuData" on-select-tree="onSelectTree(treeID)"></div> 
menu directive 
angular.module('myApp') 
.directive("topMenuView", function() { 
return { 
restrict: 'A', 
scope: { 
menuData: '=', 
onSelectTree: '&' 
} 
}, 
templateUrl: '...', 
link: function(scope) { 
scope.chooseTree = function(tree){ 
scope.onSelectTree({treeID: tree.id}); 
}; 
} 
}); 
28 
index.html 
<body ng-app="myApp" ng-controller="myController"> 
<div id="homescope"> 
<div class="row"> 
</div> 
<div class="row"> 
<div id="leftPanel"> 
<div nav-widget …></div> 
</div> 
<div id="middlePanel"> 
<div workspace-contents …></div> 
</div> 
<div id="rightPanel" > 
<div action-menu …></div> 
</div> 
</div> 
</div> 
… 
</body>
<div top-menu-view menu-data="menuData" on-select-tree="onSelectTree(treeID)"></div> 
menu directive 
angular.module('myApp') 
.directive("topMenuView", function() { 
return { 
restrict: 'A', 
scope: { 
menuData: '=', 
onSelectTree: '&' 
} 
}, 
templateUrl: '...', 
link: function(scope) { 
scope.chooseTree = function(tree){ 
scope.onSelectTree({treeID: tree.id}); 
}; 
} 
}); 
28 
index.html 
<body ng-app="myApp" ng-controller="myController"> 
<div id="homescope"> 
<div class="row"> 
</div> 
<div class="row"> 
<div id="leftPanel"> 
<div nav-widget …></div> 
</div> 
<div id="middlePanel"> 
<div workspace-contents …></div> 
</div> 
<div id="rightPanel" > 
<div action-menu …></div> 
</div> 
</div> 
</div> 
… 
</body>
index.html 
<body ng-app="myApp" ng-controller="myController"> 
<div id="homescope"> 
<div class="row"> 
<div top-menu-view menu-data="menuData" on-select-tree="onSelectTree(treeID)"></div> 
menu directive 
angular.module('myApp') 
.directive("topMenuView", function() { 
return { 
restrict: 'A', 
scope: { 
menuData: '=', 
onSelectTree: '&' 
} 
}, 
templateUrl: '...', 
link: function(scope) { 
scope.chooseTree = function(tree){ 
scope.onSelectTree({treeID: tree.id}); 
}; 
} 
}); 
28 
</div> 
<div class="row"> 
<div id="leftPanel"> 
<div nav-widget …></div> 
</div> 
<div id="middlePanel"> 
<div workspace-contents …></div> 
</div> 
<div id="rightPanel" > 
<div action-menu …></div> 
Pattern: ! 
Pass Controller Functions as Directive 
Attributes 
</div> 
</div> 
</div> 
… 
</body>
Legacy Application 
Non-JS MVC model and cannot make direct web calls from JS 
to your application 
Generate HTML stubs that invoke “widgets” 
using directives 
! 
#smartorgdev 29
30
30
In your legacy web application, render this html: 
<div id="mywidgetOuterDiv">! 
<div id="mywidget" class="" ng-controller="MyWidgetCtrl">! 
<div mywidget-directive! 
input-data="inputData" ! 
setup-data="setupData()">! 
</div> ! 
</div>! 
</div>! 
! 
and this JS: 
jsCode = "APP.myWidgetConfig = { inputData: {…} }"! 
In your JS code, bootstrap your widget: 
angular.bootstrap($('#mywidgetOuterDiv'), ['myApp']);! 
var scope = $('#mywidget').scope();! 
scope.setupData(APP.myWidgetConfig);! 
! 
31
In your legacy web application, render this html: 
<div id="mywidgetOuterDiv">! 
<div id="mywidget" class="" ng-controller="MyWidgetCtrl">! 
<div mywidget-directive! 
input-data="inputData" ! 
setup-data="setupData()">! 
</div> ! 
</div>! 
</div>! 
! 
widget 
directive 
and this JS: 
jsCode = "APP.myWidgetConfig = { inputData: {…} }"! 
In your JS code, bootstrap your widget: 
angular.bootstrap($('#mywidgetOuterDiv'), ['myApp']);! 
var scope = $('#mywidget').scope();! 
scope.setupData(APP.myWidgetConfig);! 
! 
31
In your legacy web application, render this html: 
<div id="mywidgetOuterDiv">! 
<div id="mywidget" class="" ng-controller="MyWidgetCtrl">! 
<div mywidget-directive! 
input-data="inputData" ! 
setup-data="setupData()">! 
</div> ! 
</div>! 
</div>! 
! 
widget 
directive 
In your JS code, bootstrap your widget: 
angular.bootstrap($('#mywidgetOuterDiv'), ['myApp']);! 
var scope = $('#mywidget').scope();! 
scope.setupData(APP.myWidgetConfig);! 
! 
widget 
controller 
and this JS: 
jsCode = "APP.myWidgetConfig = { inputData: {…} }"! 
31
In your legacy web application, render this html: 
<div id="mywidgetOuterDiv">! 
<div id="mywidget" class="" ng-controller="MyWidgetCtrl">! 
<div mywidget-directive! 
input-data="inputData" ! 
setup-data="setupData()">! 
</div> ! 
</div>! 
</div>! 
! 
widget 
directive 
In your JS code, bootstrap your widget: 
angular.bootstrap($('#mywidgetOuterDiv'), ['myApp']);! 
var scope = $('#mywidget').scope();! 
scope.setupData(APP.myWidgetConfig);! 
! 
widget 
controller 
widget 
container 
and this JS: 
jsCode = "APP.myWidgetConfig = { inputData: {…} }"! 
31
In your legacy web application, render this html: 
<div id="mywidgetOuterDiv">! 
<div id="mywidget" class="" ng-controller="MyWidgetCtrl">! 
<div mywidget-directive! 
input-data="inputData" ! 
setup-data="setupData()">! 
</div> ! 
</div>! 
</div>! 
! 
widget 
directive 
In your JS code, bootstrap your widget: 
angular.bootstrap($('#mywidgetOuterDiv'), ['myApp']);! 
var scope = $('#mywidget').scope();! 
scope.setupData(APP.myWidgetConfig);! 
! 
widget 
controller 
widget 
container 
and this JS: 
jsCode = "APP.myWidgetConfig = { inputData: {…} }"! 
31
In your legacy web application, render this html: 
<div id="mywidgetOuterDiv">! 
<div id="mywidget" class="" ng-controller="MyWidgetCtrl">! 
<div mywidget-directive! 
input-data="inputData" ! 
setup-data="setupData()">! 
</div> ! 
</div>! 
</div>! 
! 
widget 
directive 
In your JS code, bootstrap your widget: 
angular.bootstrap($('#mywidgetOuterDiv'), ['myApp']);! 
var scope = $('#mywidget').scope();! 
scope.setupData(APP.myWidgetConfig);! 
! 
widget 
controller 
widget 
container 
and this JS: 
jsCode = "APP.myWidgetConfig = { inputData: {…} }"! 
31
In your legacy web application, render this html: 
<div id="mywidgetOuterDiv">! 
<div id="mywidget" class="" ng-controller="MyWidgetCtrl">! 
<div mywidget-directive! 
input-data="inputData" ! 
setup-data="setupData()">! 
</div> ! 
</div>! 
</div>! 
! 
widget 
directive 
In your JS code, bootstrap your widget: 
angular.bootstrap($('#mywidgetOuterDiv'), ['myApp']);! 
var scope = $('#mywidget').scope();! 
scope.setupData(APP.myWidgetConfig);! 
! 
widget 
controller 
widget 
container 
and this JS: 
jsCode = "APP.myWidgetConfig = { inputData: {…} }"! 
31
In your legacy web application, render this html: 
<div id="mywidgetOuterDiv">! 
<div id="mywidget" class="" ng-controller="MyWidgetCtrl">! 
<div mywidget-directive! 
input-data="inputData" ! 
setup-data="setupData()">! 
</div> ! 
</div>! 
</div>! 
! 
widget 
directive 
In your JS code, bootstrap your widget: 
angular.bootstrap($('#mywidgetOuterDiv'), ['myApp']);! 
var scope = $('#mywidget').scope();! 
scope.setupData(APP.myWidgetConfig);! 
! 
widget 
controller 
widget 
container 
and this JS: 
jsCode = "APP.myWidgetConfig = { inputData: {…} }"! 
Pattern: ! 
Dynamically invoke angular application 
31
angular.module('myApp')! 
.controller('MyWidgetCtrl', function($scope) {! 
$scope.setupData = function(myWidgetConfig:MyWidgetConfig) {! 
if (!$scope.inputData && !myWidgetConfig) {! 
addTablesConfig = window.standalone.myWidgetConfig;! 
} else if ($scope.inputData && !myWidgetConfig) {! 
return;! 
}! 
$scope.inputData = myWidgetConfig.inputData;! 
};! 
})! 
.directive('mywidgetDirective', function() {! 
var templateUrl = '/path/to/template.html';! 
if (!window.production) {! 
templateUrl = 'localpath/to/template.html'! 
}! 
return {! 
restrict: 'A',! 
templateUrl: templateUrl,! 
scope: {! 
inputData: '=',! 
setupData: '&'! 
},! 
link: function (scope, elem, attrs, ctrl) {! 
}! 
}! 
});! 
! 
Data made available to 
directive scope 
standalone.js 
32
angular.module('myApp')! 
.controller('MyWidgetCtrl', function($scope) {! 
$scope.setupData = function(myWidgetConfig:MyWidgetConfig) {! 
if (!$scope.inputData && !myWidgetConfig) {! 
addTablesConfig = window.standalone.myWidgetConfig;! 
} else if ($scope.inputData && !myWidgetConfig) {! 
return;! 
}! 
$scope.inputData = myWidgetConfig.inputData;! 
};! 
})! 
.directive('mywidgetDirective', function() {! 
var templateUrl = '/path/to/template.html';! 
if (!window.production) {! 
templateUrl = 'localpath/to/template.html'! 
}! 
return {! 
restrict: 'A',! 
templateUrl: templateUrl,! 
scope: {! 
inputData: '=',! 
setupData: '&'! 
},! 
link: function (scope, elem, attrs, ctrl) {! 
}! 
}! 
});! 
! 
Data made available to 
directive scope 
standalone.js 
32
angular.module('myApp')! 
.controller('MyWidgetCtrl', function($scope) {! 
$scope.setupData = function(myWidgetConfig:MyWidgetConfig) {! 
if (!$scope.inputData && !myWidgetConfig) {! 
addTablesConfig = window.standalone.myWidgetConfig;! 
} else if ($scope.inputData && !myWidgetConfig) {! 
return;! 
}! 
$scope.inputData = myWidgetConfig.inputData;! 
};! 
})! 
.directive('mywidgetDirective', function() {! 
var templateUrl = '/path/to/template.html';! 
if (!window.production) {! 
templateUrl = 'localpath/to/template.html'! 
}! 
return {! 
restrict: 'A',! 
templateUrl: templateUrl,! 
scope: {! 
inputData: '=',! 
setupData: '&'! 
},! 
link: function (scope, elem, attrs, ctrl) {! 
}! 
}! 
});! 
! 
Data made available to 
directive scope 
standalone.js 
32
angular.module('myApp')! 
.controller('MyWidgetCtrl', function($scope) {! 
$scope.setupData = function(myWidgetConfig:MyWidgetConfig) {! 
if (!$scope.inputData && !myWidgetConfig) {! 
addTablesConfig = window.standalone.myWidgetConfig;! 
} else if ($scope.inputData && !myWidgetConfig) {! 
return;! 
}! 
$scope.inputData = myWidgetConfig.inputData;! 
};! 
})! 
.directive('mywidgetDirective', function() {! 
var templateUrl = '/path/to/template.html';! 
if (!window.production) {! 
Allows for standalone 
templateUrl = 'localpath/to/template.html'! 
}! 
return {! 
restrict: 'A',! 
templateUrl: templateUrl,! 
scope: {! 
inputData: '=',! 
setupData: '&'! 
},! 
link: function (scope, elem, attrs, ctrl) {! 
}! 
}! 
});! 
! 
Data made available to 
directive scope 
standalone.js 
32 
widget testing 
window.standalone = {! 
myWidgetConfig: {! 
inputData: …! 
}! 
}! 
! 
standalone index.html
angular.module('myApp')! 
.controller('MyWidgetCtrl', function($scope) {! 
$scope.setupData = function(myWidgetConfig:MyWidgetConfig) {! 
if (!$scope.inputData && !myWidgetConfig) {! 
addTablesConfig = window.standalone.myWidgetConfig;! 
} else if ($scope.inputData && !myWidgetConfig) {! 
return;! 
}! 
$scope.inputData = myWidgetConfig.inputData;! 
};! 
})! 
.directive('mywidgetDirective', function() {! 
var templateUrl = '/path/to/template.html';! 
if (!window.production) {! 
Allows for standalone 
templateUrl = 'localpath/to/template.html'! 
}! 
return {! 
restrict: 'A',! 
templateUrl: templateUrl,! 
scope: {! 
inputData: '=',! 
setupData: '&'! 
},! 
link: function (scope, elem, attrs, ctrl) {! 
}! 
}! 
});! 
! 
Data made available to 
directive scope 
standalone.js 
Pattern: ! 
Standalone Widget Mode 
32 
widget testing 
window.standalone = {! 
myWidgetConfig: {! 
inputData: …! 
}! 
}! 
! 
standalone index.html
Testing notes 
Avoid putting business logic in controllers 
! 
Put them in Typescript classes that can be independently 
tested 
! 
Use HttpMocks to ensure controllers work fine 
#smartorgdev 33
Old ideas like modularity, once-and-only-once, 
etc. still apply 
! 
They just look different 
#smartorgdev 34
Get expert help 
35
Join us tomorrow for a Test-Driven Development 
session 
! 
10:45 AM 
Check us out at http://rangal.com and http://smartorg.com 
#smartorgdev 36
Open House! 
! 
Pattern Summary! 
! 
Controller in Typescript classes (17) 
Named Controller objects (17) 
! 
Decompose View with Directives (22) 
! 
Pass Controller Functions as Directive 
Attributes (28) 
! 
Dynamically Invoke Angular Application (31) 
! 
Standalone Widget Mode (32) 
! 
37 
! 
Questions for reflection 
! 
What’s your AngularJS 
adoption experience? 
! 
What have you learned? 
Write to us! 
Somik Raha: sraha@smartorg.com 
Kai Wu: kwu@smartorg.com 
#smartorgdev

More Related Content

What's hot

Jquery In Rails
Jquery In RailsJquery In Rails
Jquery In Railsshen liu
 
https://www.facebook.com/valdyna.monna?fref=ts
https://www.facebook.com/valdyna.monna?fref=tshttps://www.facebook.com/valdyna.monna?fref=ts
https://www.facebook.com/valdyna.monna?fref=tsArif Alexi
 
날로 먹는 Django admin 활용
날로 먹는 Django admin 활용날로 먹는 Django admin 활용
날로 먹는 Django admin 활용KyeongMook "Kay" Cha
 
Play, Slick, play2-authの間で討死
Play, Slick, play2-authの間で討死Play, Slick, play2-authの間で討死
Play, Slick, play2-authの間で討死Kiwamu Okabe
 
Slimme Joomla! Templating Tips en Truuks
Slimme Joomla! Templating Tips en TruuksSlimme Joomla! Templating Tips en Truuks
Slimme Joomla! Templating Tips en TruuksThemePartner
 
Defenders quest cheat.dfq
Defenders quest cheat.dfqDefenders quest cheat.dfq
Defenders quest cheat.dfqaparootsa
 
Who Needs Ruby When You've Got CodeIgniter
Who Needs Ruby When You've Got CodeIgniterWho Needs Ruby When You've Got CodeIgniter
Who Needs Ruby When You've Got CodeIgniterciconf
 
Game jump: frontend introduction #1
Game jump: frontend introduction #1Game jump: frontend introduction #1
Game jump: frontend introduction #1Sebastian Pożoga
 
The Web beyond "usernames & passwords" (OSDC12)
The Web beyond "usernames & passwords" (OSDC12)The Web beyond "usernames & passwords" (OSDC12)
The Web beyond "usernames & passwords" (OSDC12)Francois Marier
 
Devoxx 2014-webComponents
Devoxx 2014-webComponentsDevoxx 2014-webComponents
Devoxx 2014-webComponentsCyril Balit
 
Persona: in your browsers, killing your passwords
Persona: in your browsers, killing your passwordsPersona: in your browsers, killing your passwords
Persona: in your browsers, killing your passwordsFrancois Marier
 
Design Patterns for Tablets and Smartphones
Design Patterns for Tablets and SmartphonesDesign Patterns for Tablets and Smartphones
Design Patterns for Tablets and SmartphonesMichael Galpin
 
Javascript MVC & Backbone Tips & Tricks
Javascript MVC & Backbone Tips & TricksJavascript MVC & Backbone Tips & Tricks
Javascript MVC & Backbone Tips & TricksHjörtur Hilmarsson
 

What's hot (20)

Jquery In Rails
Jquery In RailsJquery In Rails
Jquery In Rails
 
https://www.facebook.com/valdyna.monna?fref=ts
https://www.facebook.com/valdyna.monna?fref=tshttps://www.facebook.com/valdyna.monna?fref=ts
https://www.facebook.com/valdyna.monna?fref=ts
 
날로 먹는 Django admin 활용
날로 먹는 Django admin 활용날로 먹는 Django admin 활용
날로 먹는 Django admin 활용
 
Play, Slick, play2-authの間で討死
Play, Slick, play2-authの間で討死Play, Slick, play2-authの間で討死
Play, Slick, play2-authの間で討死
 
Slimme Joomla! Templating Tips en Truuks
Slimme Joomla! Templating Tips en TruuksSlimme Joomla! Templating Tips en Truuks
Slimme Joomla! Templating Tips en Truuks
 
Defenders quest cheat.dfq
Defenders quest cheat.dfqDefenders quest cheat.dfq
Defenders quest cheat.dfq
 
Geb qa fest2017
Geb qa fest2017Geb qa fest2017
Geb qa fest2017
 
Who Needs Ruby When You've Got CodeIgniter
Who Needs Ruby When You've Got CodeIgniterWho Needs Ruby When You've Got CodeIgniter
Who Needs Ruby When You've Got CodeIgniter
 
Game jump: frontend introduction #1
Game jump: frontend introduction #1Game jump: frontend introduction #1
Game jump: frontend introduction #1
 
Send.php
Send.phpSend.php
Send.php
 
The Web beyond "usernames & passwords" (OSDC12)
The Web beyond "usernames & passwords" (OSDC12)The Web beyond "usernames & passwords" (OSDC12)
The Web beyond "usernames & passwords" (OSDC12)
 
Devoxx 2014-webComponents
Devoxx 2014-webComponentsDevoxx 2014-webComponents
Devoxx 2014-webComponents
 
Php if
Php ifPhp if
Php if
 
[ WrocLoveRb 2012] user perspective testing using ruby
[ WrocLoveRb 2012] user perspective testing using ruby[ WrocLoveRb 2012] user perspective testing using ruby
[ WrocLoveRb 2012] user perspective testing using ruby
 
Borrador del blog
Borrador del blogBorrador del blog
Borrador del blog
 
Persona: in your browsers, killing your passwords
Persona: in your browsers, killing your passwordsPersona: in your browsers, killing your passwords
Persona: in your browsers, killing your passwords
 
Design Patterns for Tablets and Smartphones
Design Patterns for Tablets and SmartphonesDesign Patterns for Tablets and Smartphones
Design Patterns for Tablets and Smartphones
 
Symfony2. Form and Validation
Symfony2. Form and ValidationSymfony2. Form and Validation
Symfony2. Form and Validation
 
Javascript MVC & Backbone Tips & Tricks
Javascript MVC & Backbone Tips & TricksJavascript MVC & Backbone Tips & Tricks
Javascript MVC & Backbone Tips & Tricks
 
Modular and Event-Driven JavaScript
Modular and Event-Driven JavaScriptModular and Event-Driven JavaScript
Modular and Event-Driven JavaScript
 

Viewers also liked

Failing forward v2
Failing forward v2Failing forward v2
Failing forward v2SmartOrg
 
Innovation story
Innovation storyInnovation story
Innovation storySmartOrg
 
Embedded Decision Analysis: Systems Design Patterns
Embedded Decision Analysis: Systems Design PatternsEmbedded Decision Analysis: Systems Design Patterns
Embedded Decision Analysis: Systems Design PatternsSmartOrg
 
Principles of strategic portfolio management
Principles of strategic portfolio managementPrinciples of strategic portfolio management
Principles of strategic portfolio managementSmartOrg
 
Embedded DA vs Consultative DA: Audience Workshop
Embedded DA vs Consultative DA: Audience WorkshopEmbedded DA vs Consultative DA: Audience Workshop
Embedded DA vs Consultative DA: Audience WorkshopSmartOrg
 
Agile business development
Agile business developmentAgile business development
Agile business developmentSmartOrg
 
Portfolio management 101
Portfolio management 101Portfolio management 101
Portfolio management 101SmartOrg
 
Framing Patterns
Framing PatternsFraming Patterns
Framing PatternsSmartOrg
 
Decision Quality Tools and Techniques
Decision Quality Tools and TechniquesDecision Quality Tools and Techniques
Decision Quality Tools and Techniquesfadinajdi
 
Head and Heart in Decision Quality
Head and Heart in Decision QualityHead and Heart in Decision Quality
Head and Heart in Decision QualitySmartOrg
 
Experimenting with Values
Experimenting with ValuesExperimenting with Values
Experimenting with ValuesSmartOrg
 
Stanford-SDG Webinar Six critical principles of strategic portfolio management
Stanford-SDG Webinar Six critical principles of strategic portfolio managementStanford-SDG Webinar Six critical principles of strategic portfolio management
Stanford-SDG Webinar Six critical principles of strategic portfolio managementSmartOrg
 
Embedded Decision Analysis
Embedded Decision AnalysisEmbedded Decision Analysis
Embedded Decision AnalysisSmartOrg
 
Accelerating the deployment of technology to business opportunities-chevron T...
Accelerating the deployment of technology to business opportunities-chevron T...Accelerating the deployment of technology to business opportunities-chevron T...
Accelerating the deployment of technology to business opportunities-chevron T...SmartOrg
 
Agile business development
Agile business developmentAgile business development
Agile business developmentSmartOrg
 

Viewers also liked (15)

Failing forward v2
Failing forward v2Failing forward v2
Failing forward v2
 
Innovation story
Innovation storyInnovation story
Innovation story
 
Embedded Decision Analysis: Systems Design Patterns
Embedded Decision Analysis: Systems Design PatternsEmbedded Decision Analysis: Systems Design Patterns
Embedded Decision Analysis: Systems Design Patterns
 
Principles of strategic portfolio management
Principles of strategic portfolio managementPrinciples of strategic portfolio management
Principles of strategic portfolio management
 
Embedded DA vs Consultative DA: Audience Workshop
Embedded DA vs Consultative DA: Audience WorkshopEmbedded DA vs Consultative DA: Audience Workshop
Embedded DA vs Consultative DA: Audience Workshop
 
Agile business development
Agile business developmentAgile business development
Agile business development
 
Portfolio management 101
Portfolio management 101Portfolio management 101
Portfolio management 101
 
Framing Patterns
Framing PatternsFraming Patterns
Framing Patterns
 
Decision Quality Tools and Techniques
Decision Quality Tools and TechniquesDecision Quality Tools and Techniques
Decision Quality Tools and Techniques
 
Head and Heart in Decision Quality
Head and Heart in Decision QualityHead and Heart in Decision Quality
Head and Heart in Decision Quality
 
Experimenting with Values
Experimenting with ValuesExperimenting with Values
Experimenting with Values
 
Stanford-SDG Webinar Six critical principles of strategic portfolio management
Stanford-SDG Webinar Six critical principles of strategic portfolio managementStanford-SDG Webinar Six critical principles of strategic portfolio management
Stanford-SDG Webinar Six critical principles of strategic portfolio management
 
Embedded Decision Analysis
Embedded Decision AnalysisEmbedded Decision Analysis
Embedded Decision Analysis
 
Accelerating the deployment of technology to business opportunities-chevron T...
Accelerating the deployment of technology to business opportunities-chevron T...Accelerating the deployment of technology to business opportunities-chevron T...
Accelerating the deployment of technology to business opportunities-chevron T...
 
Agile business development
Agile business developmentAgile business development
Agile business development
 

Similar to Design strategies for AngularJS

Private slideshow
Private slideshowPrivate slideshow
Private slideshowsblackman
 
Building an End-to-End AngularJS Application
Building an End-to-End AngularJS ApplicationBuilding an End-to-End AngularJS Application
Building an End-to-End AngularJS ApplicationDan Wahlin
 
Advanced jQuery
Advanced jQueryAdvanced jQuery
Advanced jQuerysergioafp
 
Mobile Open Day: React Native: Crossplatform fast dive
Mobile Open Day: React Native: Crossplatform fast diveMobile Open Day: React Native: Crossplatform fast dive
Mobile Open Day: React Native: Crossplatform fast diveepamspb
 
[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM patternNAVER Engineering
 
Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsFrancois Zaninotto
 
Oracle APEX migration to 5.1 - Our experience
Oracle APEX migration to 5.1 - Our experienceOracle APEX migration to 5.1 - Our experience
Oracle APEX migration to 5.1 - Our experienceLino Schildenfeld
 
Functional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwiftFunctional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwiftRodrigo Leite
 
The Art of AngularJS in 2015 - Angular Summit 2015
The Art of AngularJS in 2015 - Angular Summit 2015The Art of AngularJS in 2015 - Angular Summit 2015
The Art of AngularJS in 2015 - Angular Summit 2015Matt Raible
 
ui-router and $state
ui-router and $stateui-router and $state
ui-router and $stategarbles
 
Mashing up JavaScript – Advanced Techniques for modern Web Apps
Mashing up JavaScript – Advanced Techniques for modern Web AppsMashing up JavaScript – Advanced Techniques for modern Web Apps
Mashing up JavaScript – Advanced Techniques for modern Web AppsBastian Hofmann
 
Angular js routing options
Angular js routing optionsAngular js routing options
Angular js routing optionsNir Kaufman
 
Angular js - 4developers 12 kwietnia 2013
Angular js - 4developers 12 kwietnia 2013Angular js - 4developers 12 kwietnia 2013
Angular js - 4developers 12 kwietnia 2013Marcin Wosinek
 
Some Advanced Tracking in Google Analytics in 5 mins - PhillyJS meet up
Some Advanced Tracking in Google Analytics in 5 mins - PhillyJS meet up Some Advanced Tracking in Google Analytics in 5 mins - PhillyJS meet up
Some Advanced Tracking in Google Analytics in 5 mins - PhillyJS meet up Nico Miceli
 
ReactJs presentation
ReactJs presentationReactJs presentation
ReactJs presentationnishasowdri
 
DrupalCon Dublin 2016 - Automated browser testing with Nightwatch.js
DrupalCon Dublin 2016 - Automated browser testing with Nightwatch.jsDrupalCon Dublin 2016 - Automated browser testing with Nightwatch.js
DrupalCon Dublin 2016 - Automated browser testing with Nightwatch.jsVladimir Roudakov
 
Ditching JQuery
Ditching JQueryDitching JQuery
Ditching JQueryhowlowck
 

Similar to Design strategies for AngularJS (20)

Angular Workshop_Sarajevo2
Angular Workshop_Sarajevo2Angular Workshop_Sarajevo2
Angular Workshop_Sarajevo2
 
Clean Javascript
Clean JavascriptClean Javascript
Clean Javascript
 
Private slideshow
Private slideshowPrivate slideshow
Private slideshow
 
Building an End-to-End AngularJS Application
Building an End-to-End AngularJS ApplicationBuilding an End-to-End AngularJS Application
Building an End-to-End AngularJS Application
 
Advanced jQuery
Advanced jQueryAdvanced jQuery
Advanced jQuery
 
Mobile Open Day: React Native: Crossplatform fast dive
Mobile Open Day: React Native: Crossplatform fast diveMobile Open Day: React Native: Crossplatform fast dive
Mobile Open Day: React Native: Crossplatform fast dive
 
[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern
 
Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node js
 
Oracle APEX migration to 5.1 - Our experience
Oracle APEX migration to 5.1 - Our experienceOracle APEX migration to 5.1 - Our experience
Oracle APEX migration to 5.1 - Our experience
 
Functional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwiftFunctional Reactive Programming - RxSwift
Functional Reactive Programming - RxSwift
 
The Art of AngularJS in 2015 - Angular Summit 2015
The Art of AngularJS in 2015 - Angular Summit 2015The Art of AngularJS in 2015 - Angular Summit 2015
The Art of AngularJS in 2015 - Angular Summit 2015
 
Mashing up JavaScript
Mashing up JavaScriptMashing up JavaScript
Mashing up JavaScript
 
ui-router and $state
ui-router and $stateui-router and $state
ui-router and $state
 
Mashing up JavaScript – Advanced Techniques for modern Web Apps
Mashing up JavaScript – Advanced Techniques for modern Web AppsMashing up JavaScript – Advanced Techniques for modern Web Apps
Mashing up JavaScript – Advanced Techniques for modern Web Apps
 
Angular js routing options
Angular js routing optionsAngular js routing options
Angular js routing options
 
Angular js - 4developers 12 kwietnia 2013
Angular js - 4developers 12 kwietnia 2013Angular js - 4developers 12 kwietnia 2013
Angular js - 4developers 12 kwietnia 2013
 
Some Advanced Tracking in Google Analytics in 5 mins - PhillyJS meet up
Some Advanced Tracking in Google Analytics in 5 mins - PhillyJS meet up Some Advanced Tracking in Google Analytics in 5 mins - PhillyJS meet up
Some Advanced Tracking in Google Analytics in 5 mins - PhillyJS meet up
 
ReactJs presentation
ReactJs presentationReactJs presentation
ReactJs presentation
 
DrupalCon Dublin 2016 - Automated browser testing with Nightwatch.js
DrupalCon Dublin 2016 - Automated browser testing with Nightwatch.jsDrupalCon Dublin 2016 - Automated browser testing with Nightwatch.js
DrupalCon Dublin 2016 - Automated browser testing with Nightwatch.js
 
Ditching JQuery
Ditching JQueryDitching JQuery
Ditching JQuery
 

Recently uploaded

ICT Role in 21st Century Education & its Challenges.pptx
ICT Role in 21st Century Education & its Challenges.pptxICT Role in 21st Century Education & its Challenges.pptx
ICT Role in 21st Century Education & its Challenges.pptxAreebaZafar22
 
UGC NET Paper 1 Mathematical Reasoning & Aptitude.pdf
UGC NET Paper 1 Mathematical Reasoning & Aptitude.pdfUGC NET Paper 1 Mathematical Reasoning & Aptitude.pdf
UGC NET Paper 1 Mathematical Reasoning & Aptitude.pdfNirmal Dwivedi
 
Beyond_Borders_Understanding_Anime_and_Manga_Fandom_A_Comprehensive_Audience_...
Beyond_Borders_Understanding_Anime_and_Manga_Fandom_A_Comprehensive_Audience_...Beyond_Borders_Understanding_Anime_and_Manga_Fandom_A_Comprehensive_Audience_...
Beyond_Borders_Understanding_Anime_and_Manga_Fandom_A_Comprehensive_Audience_...Pooja Bhuva
 
2024-NATIONAL-LEARNING-CAMP-AND-OTHER.pptx
2024-NATIONAL-LEARNING-CAMP-AND-OTHER.pptx2024-NATIONAL-LEARNING-CAMP-AND-OTHER.pptx
2024-NATIONAL-LEARNING-CAMP-AND-OTHER.pptxMaritesTamaniVerdade
 
Understanding Accommodations and Modifications
Understanding  Accommodations and ModificationsUnderstanding  Accommodations and Modifications
Understanding Accommodations and ModificationsMJDuyan
 
Application orientated numerical on hev.ppt
Application orientated numerical on hev.pptApplication orientated numerical on hev.ppt
Application orientated numerical on hev.pptRamjanShidvankar
 
Single or Multiple melodic lines structure
Single or Multiple melodic lines structureSingle or Multiple melodic lines structure
Single or Multiple melodic lines structuredhanjurrannsibayan2
 
Fostering Friendships - Enhancing Social Bonds in the Classroom
Fostering Friendships - Enhancing Social Bonds  in the ClassroomFostering Friendships - Enhancing Social Bonds  in the Classroom
Fostering Friendships - Enhancing Social Bonds in the ClassroomPooky Knightsmith
 
Holdier Curriculum Vitae (April 2024).pdf
Holdier Curriculum Vitae (April 2024).pdfHoldier Curriculum Vitae (April 2024).pdf
Holdier Curriculum Vitae (April 2024).pdfagholdier
 
Kodo Millet PPT made by Ghanshyam bairwa college of Agriculture kumher bhara...
Kodo Millet  PPT made by Ghanshyam bairwa college of Agriculture kumher bhara...Kodo Millet  PPT made by Ghanshyam bairwa college of Agriculture kumher bhara...
Kodo Millet PPT made by Ghanshyam bairwa college of Agriculture kumher bhara...pradhanghanshyam7136
 
The basics of sentences session 3pptx.pptx
The basics of sentences session 3pptx.pptxThe basics of sentences session 3pptx.pptx
The basics of sentences session 3pptx.pptxheathfieldcps1
 
TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...
TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...
TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...Nguyen Thanh Tu Collection
 
ICT role in 21st century education and it's challenges.
ICT role in 21st century education and it's challenges.ICT role in 21st century education and it's challenges.
ICT role in 21st century education and it's challenges.MaryamAhmad92
 
On National Teacher Day, meet the 2024-25 Kenan Fellows
On National Teacher Day, meet the 2024-25 Kenan FellowsOn National Teacher Day, meet the 2024-25 Kenan Fellows
On National Teacher Day, meet the 2024-25 Kenan FellowsMebane Rash
 
How to Add New Custom Addons Path in Odoo 17
How to Add New Custom Addons Path in Odoo 17How to Add New Custom Addons Path in Odoo 17
How to Add New Custom Addons Path in Odoo 17Celine George
 
Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...
Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...
Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...Pooja Bhuva
 
This PowerPoint helps students to consider the concept of infinity.
This PowerPoint helps students to consider the concept of infinity.This PowerPoint helps students to consider the concept of infinity.
This PowerPoint helps students to consider the concept of infinity.christianmathematics
 
How to Manage Global Discount in Odoo 17 POS
How to Manage Global Discount in Odoo 17 POSHow to Manage Global Discount in Odoo 17 POS
How to Manage Global Discount in Odoo 17 POSCeline George
 
80 ĐỀ THI THỬ TUYỂN SINH TIẾNG ANH VÀO 10 SỞ GD – ĐT THÀNH PHỐ HỒ CHÍ MINH NĂ...
80 ĐỀ THI THỬ TUYỂN SINH TIẾNG ANH VÀO 10 SỞ GD – ĐT THÀNH PHỐ HỒ CHÍ MINH NĂ...80 ĐỀ THI THỬ TUYỂN SINH TIẾNG ANH VÀO 10 SỞ GD – ĐT THÀNH PHỐ HỒ CHÍ MINH NĂ...
80 ĐỀ THI THỬ TUYỂN SINH TIẾNG ANH VÀO 10 SỞ GD – ĐT THÀNH PHỐ HỒ CHÍ MINH NĂ...Nguyen Thanh Tu Collection
 
HMCS Max Bernays Pre-Deployment Brief (May 2024).pptx
HMCS Max Bernays Pre-Deployment Brief (May 2024).pptxHMCS Max Bernays Pre-Deployment Brief (May 2024).pptx
HMCS Max Bernays Pre-Deployment Brief (May 2024).pptxEsquimalt MFRC
 

Recently uploaded (20)

ICT Role in 21st Century Education & its Challenges.pptx
ICT Role in 21st Century Education & its Challenges.pptxICT Role in 21st Century Education & its Challenges.pptx
ICT Role in 21st Century Education & its Challenges.pptx
 
UGC NET Paper 1 Mathematical Reasoning & Aptitude.pdf
UGC NET Paper 1 Mathematical Reasoning & Aptitude.pdfUGC NET Paper 1 Mathematical Reasoning & Aptitude.pdf
UGC NET Paper 1 Mathematical Reasoning & Aptitude.pdf
 
Beyond_Borders_Understanding_Anime_and_Manga_Fandom_A_Comprehensive_Audience_...
Beyond_Borders_Understanding_Anime_and_Manga_Fandom_A_Comprehensive_Audience_...Beyond_Borders_Understanding_Anime_and_Manga_Fandom_A_Comprehensive_Audience_...
Beyond_Borders_Understanding_Anime_and_Manga_Fandom_A_Comprehensive_Audience_...
 
2024-NATIONAL-LEARNING-CAMP-AND-OTHER.pptx
2024-NATIONAL-LEARNING-CAMP-AND-OTHER.pptx2024-NATIONAL-LEARNING-CAMP-AND-OTHER.pptx
2024-NATIONAL-LEARNING-CAMP-AND-OTHER.pptx
 
Understanding Accommodations and Modifications
Understanding  Accommodations and ModificationsUnderstanding  Accommodations and Modifications
Understanding Accommodations and Modifications
 
Application orientated numerical on hev.ppt
Application orientated numerical on hev.pptApplication orientated numerical on hev.ppt
Application orientated numerical on hev.ppt
 
Single or Multiple melodic lines structure
Single or Multiple melodic lines structureSingle or Multiple melodic lines structure
Single or Multiple melodic lines structure
 
Fostering Friendships - Enhancing Social Bonds in the Classroom
Fostering Friendships - Enhancing Social Bonds  in the ClassroomFostering Friendships - Enhancing Social Bonds  in the Classroom
Fostering Friendships - Enhancing Social Bonds in the Classroom
 
Holdier Curriculum Vitae (April 2024).pdf
Holdier Curriculum Vitae (April 2024).pdfHoldier Curriculum Vitae (April 2024).pdf
Holdier Curriculum Vitae (April 2024).pdf
 
Kodo Millet PPT made by Ghanshyam bairwa college of Agriculture kumher bhara...
Kodo Millet  PPT made by Ghanshyam bairwa college of Agriculture kumher bhara...Kodo Millet  PPT made by Ghanshyam bairwa college of Agriculture kumher bhara...
Kodo Millet PPT made by Ghanshyam bairwa college of Agriculture kumher bhara...
 
The basics of sentences session 3pptx.pptx
The basics of sentences session 3pptx.pptxThe basics of sentences session 3pptx.pptx
The basics of sentences session 3pptx.pptx
 
TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...
TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...
TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...
 
ICT role in 21st century education and it's challenges.
ICT role in 21st century education and it's challenges.ICT role in 21st century education and it's challenges.
ICT role in 21st century education and it's challenges.
 
On National Teacher Day, meet the 2024-25 Kenan Fellows
On National Teacher Day, meet the 2024-25 Kenan FellowsOn National Teacher Day, meet the 2024-25 Kenan Fellows
On National Teacher Day, meet the 2024-25 Kenan Fellows
 
How to Add New Custom Addons Path in Odoo 17
How to Add New Custom Addons Path in Odoo 17How to Add New Custom Addons Path in Odoo 17
How to Add New Custom Addons Path in Odoo 17
 
Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...
Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...
Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...
 
This PowerPoint helps students to consider the concept of infinity.
This PowerPoint helps students to consider the concept of infinity.This PowerPoint helps students to consider the concept of infinity.
This PowerPoint helps students to consider the concept of infinity.
 
How to Manage Global Discount in Odoo 17 POS
How to Manage Global Discount in Odoo 17 POSHow to Manage Global Discount in Odoo 17 POS
How to Manage Global Discount in Odoo 17 POS
 
80 ĐỀ THI THỬ TUYỂN SINH TIẾNG ANH VÀO 10 SỞ GD – ĐT THÀNH PHỐ HỒ CHÍ MINH NĂ...
80 ĐỀ THI THỬ TUYỂN SINH TIẾNG ANH VÀO 10 SỞ GD – ĐT THÀNH PHỐ HỒ CHÍ MINH NĂ...80 ĐỀ THI THỬ TUYỂN SINH TIẾNG ANH VÀO 10 SỞ GD – ĐT THÀNH PHỐ HỒ CHÍ MINH NĂ...
80 ĐỀ THI THỬ TUYỂN SINH TIẾNG ANH VÀO 10 SỞ GD – ĐT THÀNH PHỐ HỒ CHÍ MINH NĂ...
 
HMCS Max Bernays Pre-Deployment Brief (May 2024).pptx
HMCS Max Bernays Pre-Deployment Brief (May 2024).pptxHMCS Max Bernays Pre-Deployment Brief (May 2024).pptx
HMCS Max Bernays Pre-Deployment Brief (May 2024).pptx
 

Design strategies for AngularJS

  • 2. Design Strategies with AngularJS 1 Somik Raha
  • 3. Design Strategies with AngularJS Somik Raha Kai Wu 1
  • 4. Design Strategies with AngularJS Somik Raha Kai Wu 1 #smartorgdev
  • 5. Rip Van Winkle Slept through the American Revolution Slept off in 2004 and woke up in 2014 2
  • 6. 3
  • 11. 2004… C++ Java Microsoft 3
  • 12. 2004… C++ Java Microsoft Sun 3
  • 13. 2004… C++ Java Microsoft Sun #smartorgdev 3
  • 14. 2014. C++ Java Microsoft Sun #smartorgdev 4
  • 23. First reaction I don’t get it. Backbone is great for us. #smartorgdev 8
  • 24. Second reaction Whoa!! #smartorgdev 9
  • 25. Backbone learning curve AngularJS learning curve Flattens quickly Minimal concepts 10 Service Directives Controllers and views Staircase … !
  • 26. Backbone learning curve AngularJS learning curve Flattens quickly Minimal concepts 10 Ben Nadel’s blog
  • 27. Stats comparing Backbone with AngularJS AppStructure Wizard #smartorgdev 11
  • 28. Stats comparing Backbone with AngularJS AppStructure Wizard #smartorgdev 11
  • 29. Stats comparing Backbone with AngularJS AppStructure Wizard #smartorgdev 11
  • 30. Three Kinds of Scenarios Single Page Application Multi-Page Application Legacy Application Classic Sophisticated Convoluted #smartorgdev 12
  • 31. Multi-Page Application Browser loads entire page e.g. Login e.g. Show Projects Page 1 Page 2 Page 3 View 1 View 2 View 3 Controller 1 Controller 2 Controller 3 Route 1 Route 2 Route 3 /login /showProjects /… Map routes to controllers and views #smartorgdev 13
  • 32. angular.module('myApp', [ 'ngRoute', … ]).config(function ($routeProvider) { $routeProvider.when('/route1', { controller: 'Controller1 as c1', templateUrl: 'views/view1.html', }).when('/route2', { controller: 'Controller2 as c2', templateUrl: 'views/view2.html', }).otherwise({ redirectTo: '/route1' }); }); Route1 Controller1 View1 Route2 Controller2 View2 Map routes to controllers and views app.js 14
  • 33. angular.module('myApp', [ 'ngRoute', … ]).config(function ($routeProvider) { $routeProvider.when('/showProjects', { controller: 'ShowProjectsController as show', templateUrl: ‘views/showProjects.html’, }).when('/login', { controller: 'LoginController as login', templateUrl: 'views/login.html', }).otherwise({ redirectTo: '/login' }); }); Routes! http://../login http://../showProjects app.js 15
  • 34. angular.module('myApp', [ 'ngRoute', … ]).config(function ($routeProvider) { $routeProvider.when('/showProjects', { controller: 'ShowProjectsController as show', templateUrl: ‘views/showProjects.html’, }).when('/login', { controller: 'LoginController as login', templateUrl: 'views/login.html', }).otherwise({ redirectTo: '/login' }); }); Routes! http://../login http://../showProjects app.js 15
  • 35. when('/login', { controller: 'LoginController as login', templateUrl: 'views/login.html', }) Routes! http://../login http://../showProjects app.js 16
  • 36. ! class LoginController { $location: ng.ILocationService; password: string; userName: string; constructor($location:ng.ILocationService) { this.$location = $location; this.userName = ""; this.password = ""; } doLogin() { // Call your login code with a call back to loginSuccessFn callSomeAuthCode(this.userName, this.password) .then((response) => this.onLoginSuccess(response)) } onLoginSuccess(response){ if (response.status) { this.$location.path("/showProjects"); } else { this.$location.path("/login"); console.log("Login failed. You cannot proceed.") } } } ! ! <form name="loginForm" class="navbar-form navbar-left form-signin"> <input ng-model="login.userName" class="form-control" placeholder="User Name" required autofocus> <input ng-model="login.password" type="password" class="form-control" placeholder="Password" required> <button ng-click="login.doLogin()" class="btn btn-primary" type="submit">Sign in</button> </form> login.html TypeScript Simple OO Code AngularJS Creates an object in app.js app.js when('/login', { controller: 'LoginController as login', templateUrl: 'views/login.html', }) 17
  • 37. ! class LoginController { $location: ng.ILocationService; password: string; userName: string; constructor($location:ng.ILocationService) { this.$location = $location; this.userName = ""; this.password = ""; } doLogin() { // Call your login code with a call back to loginSuccessFn callSomeAuthCode(this.userName, this.password) .then((response) => this.onLoginSuccess(response)) } onLoginSuccess(response){ if (response.status) { this.$location.path("/showProjects"); } else { this.$location.path("/login"); console.log("Login failed. You cannot proceed.") } } } ! ! <form name="loginForm" class="navbar-form navbar-left form-signin"> <input ng-model="login.userName" class="form-control" placeholder="User Name" required autofocus> <input ng-model="login.password" type="password" class="form-control" placeholder="Password" required> <button ng-click="login.doLogin()" class="btn btn-primary" type="submit">Sign in</button> </form> login.html TypeScript Simple OO Code AngularJS Creates an object in app.js app.js Pattern ! Controller in Typescript Classes! ! Packages the controller logic and makes it much easier to read and test. when('/login', { controller: 'LoginController as login', templateUrl: 'views/login.html', }) 17
  • 38. ! class LoginController { $location: ng.ILocationService; password: string; userName: string; constructor($location:ng.ILocationService) { this.$location = $location; this.userName = ""; this.password = ""; } doLogin() { // Call your login code with a call back to loginSuccessFn callSomeAuthCode(this.userName, this.password) .then((response) => this.onLoginSuccess(response)) } onLoginSuccess(response){ if (response.status) { this.$location.path("/showProjects"); } else { this.$location.path("/login"); console.log("Login failed. You cannot proceed.") } } } ! ! <form name="loginForm" class="navbar-form navbar-left form-signin"> <input ng-model="login.userName" class="form-control" placeholder="User Name" required autofocus> <input ng-model="login.password" type="password" class="form-control" placeholder="Password" required> <button ng-click="login.doLogin()" class="btn btn-primary" type="submit">Sign in</button> </form> login.html TypeScript Simple OO Code AngularJS Creates an object in app.js app.js when('/login', { controller: 'LoginController as login', templateUrl: 'views/login.html', }) 17
  • 39. ! class LoginController { $location: ng.ILocationService; password: string; userName: string; constructor($location:ng.ILocationService) { this.$location = $location; this.userName = ""; this.password = ""; } doLogin() { // Call your login code with a call back to loginSuccessFn callSomeAuthCode(this.userName, this.password) .then((response) => this.onLoginSuccess(response)) } onLoginSuccess(response){ if (response.status) { this.$location.path("/showProjects"); } else { this.$location.path("/login"); console.log("Login failed. You cannot proceed.") } } } ! ! <form name="loginForm" class="navbar-form navbar-left form-signin"> <input ng-model="login.userName" class="form-control" placeholder="User Name" required autofocus> <input ng-model="login.password" type="password" class="form-control" placeholder="Password" required> <button ng-click="login.doLogin()" class="btn btn-primary" type="submit">Sign in</button> </form> login.html TypeScript Simple OO Code AngularJS Creates an object in app.js app.js when('/login', { controller: 'LoginController as login', templateUrl: 'views/login.html', }) 17
  • 40. ! class LoginController { $location: ng.ILocationService; password: string; userName: string; constructor($location:ng.ILocationService) { this.$location = $location; this.userName = ""; this.password = ""; } doLogin() { // Call your login code with a call back to loginSuccessFn callSomeAuthCode(this.userName, this.password) .then((response) => this.onLoginSuccess(response)) } onLoginSuccess(response){ if (response.status) { this.$location.path("/showProjects"); } else { this.$location.path("/login"); console.log("Login failed. You cannot proceed.") } } } ! ! <form name="loginForm" class="navbar-form navbar-left form-signin"> <input ng-model="login.userName" class="form-control" placeholder="User Name" required autofocus> <input ng-model="login.password" type="password" class="form-control" placeholder="Password" required> <button ng-click="login.doLogin()" class="btn btn-primary" type="submit">Sign in</button> </form> login.html TypeScript Simple OO Code AngularJS Creates an object in app.js app.js when('/login', { controller: 'LoginController as login', templateUrl: 'views/login.html', }) 17
  • 41. ! class LoginController { $location: ng.ILocationService; password: string; userName: string; constructor($location:ng.ILocationService) { this.$location = $location; this.userName = ""; this.password = ""; } doLogin() { // Call your login code with a call back to loginSuccessFn callSomeAuthCode(this.userName, this.password) .then((response) => this.onLoginSuccess(response)) } onLoginSuccess(response){ if (response.status) { this.$location.path("/showProjects"); } else { this.$location.path("/login"); console.log("Login failed. You cannot proceed.") } } } ! ! <form name="loginForm" class="navbar-form navbar-left form-signin"> <input ng-model="login.userName" class="form-control" placeholder="User Name" required autofocus> <input ng-model="login.password" type="password" class="form-control" placeholder="Password" required> <button ng-click="login.doLogin()" class="btn btn-primary" type="submit">Sign in</button> </form> login.html TypeScript Simple OO Code AngularJS Creates an object in app.js app.js when('/login', { controller: 'LoginController as login', templateUrl: 'views/login.html', }) 17
  • 42. ! class LoginController { $location: ng.ILocationService; password: string; userName: string; constructor($location:ng.ILocationService) { this.$location = $location; this.userName = ""; this.password = ""; } doLogin() { // Call your login code with a call back to loginSuccessFn callSomeAuthCode(this.userName, this.password) .then((response) => this.onLoginSuccess(response)) } onLoginSuccess(response){ if (response.status) { this.$location.path("/showProjects"); } else { this.$location.path("/login"); console.log("Login failed. You cannot proceed.") } } } ! ! <form name="loginForm" class="navbar-form navbar-left form-signin"> <input ng-model="login.userName" class="form-control" placeholder="User Name" required autofocus> <input ng-model="login.password" type="password" class="form-control" placeholder="Password" required> <button ng-click="login.doLogin()" class="btn btn-primary" type="submit">Sign in</button> </form> login.html TypeScript Simple OO Code AngularJS Creates an object in app.js app.js Optional Pattern ! Named Controller Objects! ! Maintains modularity and avoids having to put data into scope objects, which hold so much other stuff, making it easier to debug. when('/login', { controller: 'LoginController as login', templateUrl: 'views/login.html', }) 17
  • 43. ! class LoginController { $location: ng.ILocationService; password: string; userName: string; constructor($location:ng.ILocationService) { this.$location = $location; this.userName = ""; this.password = ""; } doLogin() { // Call your login code with a call back to loginSuccessFn callSomeAuthCode(this.userName, this.password) .then((response) => this.onLoginSuccess(response)) } onLoginSuccess(response){ if (response.status) { this.$location.path("/showProjects"); } else { this.$location.path("/login"); console.log("Login failed. You cannot proceed.") } } } ! ! function LoginController($scope, $location) { $scope.userName = ""; $scope.password = ""; $scope.doLogin = function() { // Call your login code with a call back to loginSuccessFn callSomeAuthCode($scope.userName, $scope.password) .then($scope.onLoginSuccess(response)) } $scope.onLoginSuccess = function(response){ if (response.status) { $location.path("/showProjects"); } else { $location.path("/login"); console.log("Login failed. You cannot proceed.") } } } !! Typescript provides more readable structure More funky All initialization is in constructor Code completion; No $scope Initialization is not clearly demarcated, declarations interspersed with execution ! No code completion 18 Controller in Typescript Classes
  • 44. 19
  • 45. 19
  • 46. Single-Page Application Rich interactions within a single page, loaded only once Navigation Workspace Actions Menu #smartorgdev 20
  • 49. If we have only one controller and one view, how do we prevent our code from getting bloated? 22
  • 50. Directives to the rescue index.html <body ng-app="myApp" ng-controller="myController"> <div class="row"> <div top-menu-view …></div> </div> <div class="row"> <div id="leftPanel"> <div nav-widget …></div> </div> <div id="middlePanel"> <div workspace-contents …></div> </div> <div id="rightPanel" > <div action-menu …></div> </div> </div> </div> … </body> 23 template.html directive.js
  • 51. Directives to the rescue index.html <body ng-app="myApp" ng-controller="myController"> <div class="row"> <div top-menu-view …></div> </div> <div class="row"> <div id="leftPanel"> <div nav-widget …></div> </div> <div id="middlePanel"> <div workspace-contents …></div> </div> <div id="rightPanel" > <div action-menu …></div> </div> </div> </div> … </body> 23 template.html directive.js Pattern: ! Decompose view with directives
  • 52. Directives to the rescue index.html <body ng-app="myApp" ng-controller="myController"> <div class="row"> <div top-menu-view …></div> </div> <div class="row"> <div id="leftPanel"> <div nav-widget …></div> </div> <div id="middlePanel"> <div workspace-contents …></div> </div> <div id="rightPanel" > <div action-menu …></div> </div> </div> </div> … </body> 23 template.html directive.js
  • 53. But wait, how do we pass data between directives? Navigation Workspace Actions Menu e.g. How does the Navigation directive find out about a selection in the Menu directive? #smartorgdev 24
  • 54. But wait, how do we pass data between directives? Navigation Workspace Actions Menu e.g. list of trees e.g. How does the Navigation directive find out which tree has been selected in the Menu directive? #smartorgdev 25
  • 55. myController angular.module('myApp') .controller('myController', function ($scope, $route, $routeParams, $location) { $scope.action = 'login'; $scope.$on("$routeChangeSuccess", function( $currentRoute, $previousRoute ) { $scope.action = $route.current.action; }); $scope.login = function() { // do login, and on success: $location.path('/home'); } $scope.onSelectTree = function(treeID) { // go fetch the tree using the treeID, … // myTree holds the tree $scope.$broadcast("treeLoaded", { "myTree": myTree }); } }); navigation directive angular.module('myApp') .directive("navigation", function() { return { restrict: 'A', scope: { myTree: '=' } }, templateUrl: '...', link: function(scope) { scope.$on('treeLoaded', function() { makeTreeWith(scope.myTree); //.. } } }); menu directive angular.module('myApp') .directive("topMenuView", function() { return { restrict: 'A', scope: { menuData: '=', onSelectTree: '&' } }, templateUrl: '...', link: function(scope) { scope.chooseTree = function(tree){ scope.onSelectTree({treeID: tree.id}); }; } }); 26
  • 56. myController angular.module('myApp') .controller('myController', function ($scope, $route, $routeParams, $location) { $scope.action = 'login'; $scope.$on("$routeChangeSuccess", function( $currentRoute, $previousRoute ) { $scope.action = $route.current.action; }); $scope.login = function() { // do login, and on success: $location.path('/home'); } $scope.onSelectTree = function(treeID) { // go fetch the tree using the treeID, … // myTree holds the tree $scope.$broadcast("treeLoaded", { "myTree": myTree }); } }); navigation directive angular.module('myApp') .directive("navigation", function() { return { restrict: 'A', scope: { myTree: '=' } }, templateUrl: '...', link: function(scope) { scope.$on('treeLoaded', function() { makeTreeWith(scope.myTree); //.. } } }); menu directive angular.module('myApp') .directive("topMenuView", function() { return { restrict: 'A', scope: { menuData: '=', onSelectTree: '&' } }, templateUrl: '...', link: function(scope) { scope.chooseTree = function(tree){ scope.onSelectTree({treeID: tree.id}); }; } }); 26
  • 57. myController angular.module('myApp') .controller('myController', function ($scope, $route, $routeParams, $location) { $scope.action = 'login'; $scope.$on("$routeChangeSuccess", function( $currentRoute, $previousRoute ) { $scope.action = $route.current.action; }); $scope.login = function() { // do login, and on success: $location.path('/home'); } $scope.onSelectTree = function(treeID) { // go fetch the tree using the treeID, … // myTree holds the tree $scope.$broadcast("treeLoaded", { "myTree": myTree }); } }); navigation directive angular.module('myApp') .directive("navigation", function() { return { restrict: 'A', scope: { myTree: '=' } }, templateUrl: '...', link: function(scope) { scope.$on('treeLoaded', function() { makeTreeWith(scope.myTree); //.. } } }); menu directive angular.module('myApp') .directive("topMenuView", function() { return { restrict: 'A', scope: { menuData: '=', onSelectTree: '&' } }, templateUrl: '...', link: function(scope) { scope.chooseTree = function(tree){ scope.onSelectTree({treeID: tree.id}); }; } }); 26
  • 58. myController angular.module('myApp') .controller('myController', function ($scope, $route, $routeParams, $location) { $scope.action = 'login'; $scope.$on("$routeChangeSuccess", function( $currentRoute, $previousRoute ) { $scope.action = $route.current.action; }); $scope.login = function() { // do login, and on success: $location.path('/home'); } $scope.onSelectTree = function(treeID) { // go fetch the tree using the treeID, … // myTree holds the tree $scope.$broadcast("treeLoaded", { "myTree": myTree }); } }); navigation directive angular.module('myApp') .directive("navigation", function() { return { restrict: 'A', scope: { myTree: '=' } }, templateUrl: '...', link: function(scope) { scope.$on('treeLoaded', function() { makeTreeWith(scope.myTree); //.. } } }); menu directive angular.module('myApp') .directive("topMenuView", function() { return { restrict: 'A', scope: { menuData: '=', onSelectTree: '&' } }, templateUrl: '...', link: function(scope) { scope.chooseTree = function(tree){ scope.onSelectTree({treeID: tree.id}); }; } }); 26
  • 59. myController angular.module('myApp') .controller('myController', function ($scope, $route, $routeParams, $location) { $scope.action = 'login'; $scope.$on("$routeChangeSuccess", function( $currentRoute, $previousRoute ) { $scope.action = $route.current.action; }); $scope.login = function() { // do login, and on success: $location.path('/home'); } $scope.onSelectTree = function(treeID) { // go fetch the tree using the treeID, … // myTree holds the tree $scope.$broadcast("treeLoaded", { "myTree": myTree }); } }); navigation directive angular.module('myApp') .directive("navigation", function() { return { restrict: 'A', scope: { myTree: '=' } }, templateUrl: '...', link: function(scope) { scope.$on('treeLoaded', function() { makeTreeWith(scope.myTree); //.. } } }); menu directive angular.module('myApp') .directive("topMenuView", function() { return { restrict: 'A', scope: { menuData: '=', onSelectTree: '&' } }, templateUrl: '...', link: function(scope) { scope.chooseTree = function(tree){ scope.onSelectTree({treeID: tree.id}); }; } }); 26
  • 60. myController angular.module('myApp') .controller('myController', function ($scope, $route, $routeParams, $location) { $scope.action = 'login'; $scope.$on("$routeChangeSuccess", function( $currentRoute, $previousRoute ) { $scope.action = $route.current.action; }); $scope.login = function() { // do login, and on success: $location.path('/home'); } $scope.onSelectTree = function(treeID) { // go fetch the tree using the treeID, … // myTree holds the tree $scope.$broadcast("treeLoaded", { "myTree": myTree }); } }); navigation directive angular.module('myApp') .directive("navigation", function() { return { restrict: 'A', scope: { myTree: '=' } }, templateUrl: '...', link: function(scope) { scope.$on('treeLoaded', function() { makeTreeWith(scope.myTree); //.. } } }); menu directive angular.module('myApp') .directive("topMenuView", function() { return { restrict: 'A', scope: { menuData: '=', onSelectTree: '&' } }, templateUrl: '...', link: function(scope) { scope.chooseTree = function(tree){ scope.onSelectTree({treeID: tree.id}); }; } }); 26
  • 61. myController angular.module('myApp') .controller('myController', function ($scope, $route, $routeParams, $location) { $scope.action = 'login'; $scope.$on("$routeChangeSuccess", function( $currentRoute, $previousRoute ) { $scope.action = $route.current.action; }); $scope.login = function() { // do login, and on success: $location.path('/home'); } $scope.onSelectTree = function(treeID) { // go fetch the tree using the treeID, … // myTree holds the tree $scope.$broadcast("treeLoaded", { "myTree": myTree }); } }); navigation directive angular.module('myApp') .directive("navigation", function() { return { restrict: 'A', scope: { myTree: '=' } }, templateUrl: '...', link: function(scope) { scope.$on('treeLoaded', function() { makeTreeWith(scope.myTree); //.. } } }); menu directive angular.module('myApp') .directive("topMenuView", function() { return { restrict: 'A', scope: { menuData: '=', onSelectTree: '&' } }, templateUrl: '...', link: function(scope) { scope.chooseTree = function(tree){ scope.onSelectTree({treeID: tree.id}); }; } }); But wait, how does scope.onSelectTree work correctly when called in the menu directive? 26
  • 62. index.html menu directive angular.module('myApp') .directive("topMenuView", function() { return { restrict: 'A', scope: { menuData: '=', onSelectTree: '&' } }, templateUrl: '...', link: function(scope) { scope.chooseTree = function(tree){ scope.onSelectTree({treeID: tree.id}); }; } }); 27 <body ng-app="myApp" ng-controller="myController"> <div id="homescope"> <div class="row"> <div top-menu-view …></div> </div> <div class="row"> <div id="leftPanel"> <div nav-widget …></div> </div> <div id="middlePanel"> <div workspace-contents …></div> </div> <div id="rightPanel" > <div action-menu …></div> </div> </div> </div> … </body>
  • 63. index.html menu directive angular.module('myApp') .directive("topMenuView", function() { return { restrict: 'A', scope: { menuData: '=', onSelectTree: '&' } }, templateUrl: '...', link: function(scope) { scope.chooseTree = function(tree){ scope.onSelectTree({treeID: tree.id}); }; } }); 27 <body ng-app="myApp" ng-controller="myController"> <div id="homescope"> <div class="row"> <div top-menu-view …></div> </div> <div class="row"> <div id="leftPanel"> <div nav-widget …></div> </div> <div id="middlePanel"> <div workspace-contents …></div> </div> <div id="rightPanel" > <div action-menu …></div> </div> </div> </div> … </body>
  • 64. index.html menu directive angular.module('myApp') .directive("topMenuView", function() { return { restrict: 'A', scope: { menuData: '=', onSelectTree: '&' } }, templateUrl: '...', link: function(scope) { scope.chooseTree = function(tree){ scope.onSelectTree({treeID: tree.id}); }; } }); 27 <body ng-app="myApp" ng-controller="myController"> <div id="homescope"> <div class="row"> <div top-menu-view …></div> </div> <div class="row"> <div id="leftPanel"> <div nav-widget …></div> </div> <div id="middlePanel"> <div workspace-contents …></div> </div> <div id="rightPanel" > <div action-menu …></div> </div> </div> </div> … </body>
  • 65. <div top-menu-view menu-data="menuData" on-select-tree="onSelectTree(treeID)"></div> menu directive angular.module('myApp') .directive("topMenuView", function() { return { restrict: 'A', scope: { menuData: '=', onSelectTree: '&' } }, templateUrl: '...', link: function(scope) { scope.chooseTree = function(tree){ scope.onSelectTree({treeID: tree.id}); }; } }); 28 index.html <body ng-app="myApp" ng-controller="myController"> <div id="homescope"> <div class="row"> </div> <div class="row"> <div id="leftPanel"> <div nav-widget …></div> </div> <div id="middlePanel"> <div workspace-contents …></div> </div> <div id="rightPanel" > <div action-menu …></div> </div> </div> </div> … </body>
  • 66. <div top-menu-view menu-data="menuData" on-select-tree="onSelectTree(treeID)"></div> menu directive angular.module('myApp') .directive("topMenuView", function() { return { restrict: 'A', scope: { menuData: '=', onSelectTree: '&' } }, templateUrl: '...', link: function(scope) { scope.chooseTree = function(tree){ scope.onSelectTree({treeID: tree.id}); }; } }); 28 index.html <body ng-app="myApp" ng-controller="myController"> <div id="homescope"> <div class="row"> </div> <div class="row"> <div id="leftPanel"> <div nav-widget …></div> </div> <div id="middlePanel"> <div workspace-contents …></div> </div> <div id="rightPanel" > <div action-menu …></div> </div> </div> </div> … </body>
  • 67. index.html <body ng-app="myApp" ng-controller="myController"> <div id="homescope"> <div class="row"> <div top-menu-view menu-data="menuData" on-select-tree="onSelectTree(treeID)"></div> menu directive angular.module('myApp') .directive("topMenuView", function() { return { restrict: 'A', scope: { menuData: '=', onSelectTree: '&' } }, templateUrl: '...', link: function(scope) { scope.chooseTree = function(tree){ scope.onSelectTree({treeID: tree.id}); }; } }); 28 </div> <div class="row"> <div id="leftPanel"> <div nav-widget …></div> </div> <div id="middlePanel"> <div workspace-contents …></div> </div> <div id="rightPanel" > <div action-menu …></div> Pattern: ! Pass Controller Functions as Directive Attributes </div> </div> </div> … </body>
  • 68. Legacy Application Non-JS MVC model and cannot make direct web calls from JS to your application Generate HTML stubs that invoke “widgets” using directives ! #smartorgdev 29
  • 69. 30
  • 70. 30
  • 71. In your legacy web application, render this html: <div id="mywidgetOuterDiv">! <div id="mywidget" class="" ng-controller="MyWidgetCtrl">! <div mywidget-directive! input-data="inputData" ! setup-data="setupData()">! </div> ! </div>! </div>! ! and this JS: jsCode = "APP.myWidgetConfig = { inputData: {…} }"! In your JS code, bootstrap your widget: angular.bootstrap($('#mywidgetOuterDiv'), ['myApp']);! var scope = $('#mywidget').scope();! scope.setupData(APP.myWidgetConfig);! ! 31
  • 72. In your legacy web application, render this html: <div id="mywidgetOuterDiv">! <div id="mywidget" class="" ng-controller="MyWidgetCtrl">! <div mywidget-directive! input-data="inputData" ! setup-data="setupData()">! </div> ! </div>! </div>! ! widget directive and this JS: jsCode = "APP.myWidgetConfig = { inputData: {…} }"! In your JS code, bootstrap your widget: angular.bootstrap($('#mywidgetOuterDiv'), ['myApp']);! var scope = $('#mywidget').scope();! scope.setupData(APP.myWidgetConfig);! ! 31
  • 73. In your legacy web application, render this html: <div id="mywidgetOuterDiv">! <div id="mywidget" class="" ng-controller="MyWidgetCtrl">! <div mywidget-directive! input-data="inputData" ! setup-data="setupData()">! </div> ! </div>! </div>! ! widget directive In your JS code, bootstrap your widget: angular.bootstrap($('#mywidgetOuterDiv'), ['myApp']);! var scope = $('#mywidget').scope();! scope.setupData(APP.myWidgetConfig);! ! widget controller and this JS: jsCode = "APP.myWidgetConfig = { inputData: {…} }"! 31
  • 74. In your legacy web application, render this html: <div id="mywidgetOuterDiv">! <div id="mywidget" class="" ng-controller="MyWidgetCtrl">! <div mywidget-directive! input-data="inputData" ! setup-data="setupData()">! </div> ! </div>! </div>! ! widget directive In your JS code, bootstrap your widget: angular.bootstrap($('#mywidgetOuterDiv'), ['myApp']);! var scope = $('#mywidget').scope();! scope.setupData(APP.myWidgetConfig);! ! widget controller widget container and this JS: jsCode = "APP.myWidgetConfig = { inputData: {…} }"! 31
  • 75. In your legacy web application, render this html: <div id="mywidgetOuterDiv">! <div id="mywidget" class="" ng-controller="MyWidgetCtrl">! <div mywidget-directive! input-data="inputData" ! setup-data="setupData()">! </div> ! </div>! </div>! ! widget directive In your JS code, bootstrap your widget: angular.bootstrap($('#mywidgetOuterDiv'), ['myApp']);! var scope = $('#mywidget').scope();! scope.setupData(APP.myWidgetConfig);! ! widget controller widget container and this JS: jsCode = "APP.myWidgetConfig = { inputData: {…} }"! 31
  • 76. In your legacy web application, render this html: <div id="mywidgetOuterDiv">! <div id="mywidget" class="" ng-controller="MyWidgetCtrl">! <div mywidget-directive! input-data="inputData" ! setup-data="setupData()">! </div> ! </div>! </div>! ! widget directive In your JS code, bootstrap your widget: angular.bootstrap($('#mywidgetOuterDiv'), ['myApp']);! var scope = $('#mywidget').scope();! scope.setupData(APP.myWidgetConfig);! ! widget controller widget container and this JS: jsCode = "APP.myWidgetConfig = { inputData: {…} }"! 31
  • 77. In your legacy web application, render this html: <div id="mywidgetOuterDiv">! <div id="mywidget" class="" ng-controller="MyWidgetCtrl">! <div mywidget-directive! input-data="inputData" ! setup-data="setupData()">! </div> ! </div>! </div>! ! widget directive In your JS code, bootstrap your widget: angular.bootstrap($('#mywidgetOuterDiv'), ['myApp']);! var scope = $('#mywidget').scope();! scope.setupData(APP.myWidgetConfig);! ! widget controller widget container and this JS: jsCode = "APP.myWidgetConfig = { inputData: {…} }"! 31
  • 78. In your legacy web application, render this html: <div id="mywidgetOuterDiv">! <div id="mywidget" class="" ng-controller="MyWidgetCtrl">! <div mywidget-directive! input-data="inputData" ! setup-data="setupData()">! </div> ! </div>! </div>! ! widget directive In your JS code, bootstrap your widget: angular.bootstrap($('#mywidgetOuterDiv'), ['myApp']);! var scope = $('#mywidget').scope();! scope.setupData(APP.myWidgetConfig);! ! widget controller widget container and this JS: jsCode = "APP.myWidgetConfig = { inputData: {…} }"! Pattern: ! Dynamically invoke angular application 31
  • 79. angular.module('myApp')! .controller('MyWidgetCtrl', function($scope) {! $scope.setupData = function(myWidgetConfig:MyWidgetConfig) {! if (!$scope.inputData && !myWidgetConfig) {! addTablesConfig = window.standalone.myWidgetConfig;! } else if ($scope.inputData && !myWidgetConfig) {! return;! }! $scope.inputData = myWidgetConfig.inputData;! };! })! .directive('mywidgetDirective', function() {! var templateUrl = '/path/to/template.html';! if (!window.production) {! templateUrl = 'localpath/to/template.html'! }! return {! restrict: 'A',! templateUrl: templateUrl,! scope: {! inputData: '=',! setupData: '&'! },! link: function (scope, elem, attrs, ctrl) {! }! }! });! ! Data made available to directive scope standalone.js 32
  • 80. angular.module('myApp')! .controller('MyWidgetCtrl', function($scope) {! $scope.setupData = function(myWidgetConfig:MyWidgetConfig) {! if (!$scope.inputData && !myWidgetConfig) {! addTablesConfig = window.standalone.myWidgetConfig;! } else if ($scope.inputData && !myWidgetConfig) {! return;! }! $scope.inputData = myWidgetConfig.inputData;! };! })! .directive('mywidgetDirective', function() {! var templateUrl = '/path/to/template.html';! if (!window.production) {! templateUrl = 'localpath/to/template.html'! }! return {! restrict: 'A',! templateUrl: templateUrl,! scope: {! inputData: '=',! setupData: '&'! },! link: function (scope, elem, attrs, ctrl) {! }! }! });! ! Data made available to directive scope standalone.js 32
  • 81. angular.module('myApp')! .controller('MyWidgetCtrl', function($scope) {! $scope.setupData = function(myWidgetConfig:MyWidgetConfig) {! if (!$scope.inputData && !myWidgetConfig) {! addTablesConfig = window.standalone.myWidgetConfig;! } else if ($scope.inputData && !myWidgetConfig) {! return;! }! $scope.inputData = myWidgetConfig.inputData;! };! })! .directive('mywidgetDirective', function() {! var templateUrl = '/path/to/template.html';! if (!window.production) {! templateUrl = 'localpath/to/template.html'! }! return {! restrict: 'A',! templateUrl: templateUrl,! scope: {! inputData: '=',! setupData: '&'! },! link: function (scope, elem, attrs, ctrl) {! }! }! });! ! Data made available to directive scope standalone.js 32
  • 82. angular.module('myApp')! .controller('MyWidgetCtrl', function($scope) {! $scope.setupData = function(myWidgetConfig:MyWidgetConfig) {! if (!$scope.inputData && !myWidgetConfig) {! addTablesConfig = window.standalone.myWidgetConfig;! } else if ($scope.inputData && !myWidgetConfig) {! return;! }! $scope.inputData = myWidgetConfig.inputData;! };! })! .directive('mywidgetDirective', function() {! var templateUrl = '/path/to/template.html';! if (!window.production) {! Allows for standalone templateUrl = 'localpath/to/template.html'! }! return {! restrict: 'A',! templateUrl: templateUrl,! scope: {! inputData: '=',! setupData: '&'! },! link: function (scope, elem, attrs, ctrl) {! }! }! });! ! Data made available to directive scope standalone.js 32 widget testing window.standalone = {! myWidgetConfig: {! inputData: …! }! }! ! standalone index.html
  • 83. angular.module('myApp')! .controller('MyWidgetCtrl', function($scope) {! $scope.setupData = function(myWidgetConfig:MyWidgetConfig) {! if (!$scope.inputData && !myWidgetConfig) {! addTablesConfig = window.standalone.myWidgetConfig;! } else if ($scope.inputData && !myWidgetConfig) {! return;! }! $scope.inputData = myWidgetConfig.inputData;! };! })! .directive('mywidgetDirective', function() {! var templateUrl = '/path/to/template.html';! if (!window.production) {! Allows for standalone templateUrl = 'localpath/to/template.html'! }! return {! restrict: 'A',! templateUrl: templateUrl,! scope: {! inputData: '=',! setupData: '&'! },! link: function (scope, elem, attrs, ctrl) {! }! }! });! ! Data made available to directive scope standalone.js Pattern: ! Standalone Widget Mode 32 widget testing window.standalone = {! myWidgetConfig: {! inputData: …! }! }! ! standalone index.html
  • 84. Testing notes Avoid putting business logic in controllers ! Put them in Typescript classes that can be independently tested ! Use HttpMocks to ensure controllers work fine #smartorgdev 33
  • 85. Old ideas like modularity, once-and-only-once, etc. still apply ! They just look different #smartorgdev 34
  • 87. Join us tomorrow for a Test-Driven Development session ! 10:45 AM Check us out at http://rangal.com and http://smartorg.com #smartorgdev 36
  • 88. Open House! ! Pattern Summary! ! Controller in Typescript classes (17) Named Controller objects (17) ! Decompose View with Directives (22) ! Pass Controller Functions as Directive Attributes (28) ! Dynamically Invoke Angular Application (31) ! Standalone Widget Mode (32) ! 37 ! Questions for reflection ! What’s your AngularJS adoption experience? ! What have you learned? Write to us! Somik Raha: sraha@smartorg.com Kai Wu: kwu@smartorg.com #smartorgdev