Your SlideShare is downloading. ×
0
MEAN - Notes from the field
Chris Clarke
Hydrahack Birmingham
18th March 2014
Full-Stack Development with Javascript
• Mongo
• Express
• AngularJS
• NodeJS
http://github.com/linnovate/mean
What’s MEAN?
Who are Talis?
• MongoDB ~2.5yrs
• Using express/node ~2yrs
• Angular ~9 months
Angular AppAngular App
Typical MEAN Shape
DBDB
APIAPI
Server side
pages
Server side
pages
StaticsStatics
JSON
JSON
HTMLHTM...
Typical structure
Typical structure
Typical structure
Typical structure
Video Timeline Editor
Textbook Player
Angular 101
• Single page web app framework, by Google
• Extends HTML vocabulary to provide dynamic
views
• Broadly MVC (m...
Angular 101
• Routing
• Templates
• Controllers
• Directives
Routing
$routeProvider.when('/modules/:module_id', {
templateUrl: 'partials/module.html',
controller: 'TeachCtrl',
loginRe...
Routing
$routeProvider.when('/modules/:module_id', {
templateUrl: 'partials/module.html',
controller: 'TeachCtrl',
loginRe...
Routing
$routeProvider.when('/modules/:module_id', {
templateUrl: 'partials/module.html',
controller: 'TeachCtrl',
loginRe...
Routing
$routeProvider.when('/modules/:module_id', {
templateUrl: 'partials/module.html',
controller: 'TeachCtrl',
loginRe...
Routing
$routeProvider.when('/modules/:module_id', {
templateUrl: 'partials/module.html',
controller: 'TeachCtrl',
loginRe...
<ul ng-show="modules!=null">
<li ng-repeat="m in modules | orderBy:'title'"
ng-class="{active:module._id==m._id}">
<a ng-h...
<ul ng-show="modules!=null">
<li ng-repeat="m in modules | orderBy:'title'"
ng-class="{active:module._id==m._id}">
<a ng-h...
<ul ng-show="modules!=null">
<li ng-repeat="m in modules | orderBy:'title'"
ng-class="{active:module._id==m._id}">
<a ng-h...
<ul ng-show="modules!=null">
<li ng-repeat="m in modules | orderBy:'title'"
ng-class="{active:module._id==m._id}">
<a ng-h...
<ul ng-show="modules!=null">
<li ng-repeat="m in modules | orderBy:'title'"
ng-class="{active:module._id==m._id}">
<a ng-h...
<ul ng-show="modules!=null">
<li ng-repeat="m in modules | orderBy:'title'"
ng-class="{active:module._id==m._id}">
<a ng-h...
<ul ng-show="modules!=null">
<li ng-repeat="m in modules | orderBy:'title'"
ng-class="{active:module._id==m._id}">
<a ng-h...
<input ng-model="profile.first_name" type="text" required>
<input ng-model="profile.surname" type="text" required>
<input ...
<input ng-model="profile.first_name" type="text" required>
<input ng-model="profile.surname" type="text" required>
<input ...
<input ng-model="profile.first_name" type="text" required>
<input ng-model="profile.surname" type="text" required>
<input ...
<input ng-model="profile.first_name" type="text" required>
<input ng-model="profile.surname" type="text" required>
<input ...
QuickTime™ and a
'avc1' decompressor
are needed to see this picture.
Controllers Horizontal
angular.module('talis.controllers.user', [])
.controller('AccountCtrl',function($scope, userSvc) {
...
Controllers Horizontal
angular.module('talis.controllers.user', [])
.controller('AccountCtrl',function($scope, userSvc) {
...
Controllers Horizontal
angular.module('talis.controllers.user', [])
.controller('AccountCtrl',function($scope, userSvc) {
...
Controllers Horizontal
angular.module('talis.controllers.user', [])
.controller('AccountCtrl',function($scope, userSvc) {
...
Controllers Horizontal
angular.module('talis.controllers.user', [])
.controller('AccountCtrl',function($scope, userSvc) {
...
Controllers Horizontal
angular.module('talis.controllers.user', [])
.controller('AccountCtrl',function($scope, userSvc) {
...
Directives
<textbook-player user="user" textbook="textbook">
...
</textbook-player>
Directives
<textbook-player user="user" textbook="textbook">
...
</textbook-player>
Directives
<div user="user" textbook="textbook" textbook-player>
...
</div>
Directives
<div user="user" textbook="textbook" textbook-player>
...
</div>
angular.module('talis.directives.player.textbo...
Directives
<div user="user" textbook="textbook" textbook-player>
...
</div>
angular.module('talis.directives.player.textbo...
Directives
<div user="user" textbook="textbook" textbook-player>
...
</div>
angular.module('talis.directives.player.textbo...
Directives
<div user="user" textbook="textbook" textbook-player>
...
</div>
angular.module('talis.directives.player.textbo...
–Jonny Clientside
“Waat?”
Notes From the Field
Act I: The Basics
Elem vs. Attr directives
<textbook-player user="user" textbook="textbook">
...
</textbook-player>
<div user="user" textboo...
Minification
angular.module('talis.controllers.user', [])
.controller('AccountCtrl',function($scope, userSvc) {
..
});
ang...
Minification
angular.module('talis.controllers.user', [])
.controller('AccountCtrl',function($scope, userSvc) {
..
});
ang...
Mongo _id
{
_id: ObjectId(1234),
name: “Jonny Clientside”,
age: 24,
interests: [‘JQuery’,‘HTML5’
}
<a ng-href="#/people/{{...
Notes From the Field
Act II: Advanced
Angular AppAngular App
Typical MEAN Shape
DBDB
APIAPI
Server side
pages
Server side
pages
StaticsStatics
JSON
JSON
HTMLHTM...
Angular AppAngular App
JSON
9090
9090
9090
9090
Users
API
Users
API
APIAPI
Server side
pages
Server side
pages
StaticsStat...
Logging
• A lot of activity in the client side
• Some within Express/Node server side
• More behind your API proxy
var loggingModule = angular.module('talis.services.logging', []);
loggingModule.factory(
"traceService",
function(){
retur...
var loggingModule = angular.module('talis.services.logging', []);
loggingModule.factory(
"traceService",
function(){
retur...
loggingModule.factory(
"exceptionLoggingService",
["$log","$window", "traceService",
function($log, $window, traceService)...
Logging
Security
• APIs secured with OAuth 2.0 Bearer tokens
• Tokens obtained with a key/secret
• If your app is downloaded and r...
Security
• Have node return the OAuth token as JSON behind
a login barrier
• Angular requests this JSON when a route that
...
Security
• Dealing with tokens on every service call is a PITA
• Tokens expiring is normal
• Deal with it globally using a...
.run(function($rootScope,$injector) {
$injector.get("$http").defaults.transformRequest =
function(data, headersGetter) {
h...
$httpProvider.responseInterceptors.push(
function ($rootScope, $q, $injector, $location) {
return function(promise) {
retu...
$httpProvider.responseInterceptors.push(
function ($rootScope, $q, $injector, $location) {
return function(promise) {
retu...
$httpProvider.responseInterceptors.push(
function ($rootScope, $q, $injector, $location) {
return function(promise) {
retu...
$httpProvider.responseInterceptors.push(
function ($rootScope, $q, $injector, $location) {
return function(promise) {
retu...
$httpProvider.responseInterceptors.push(
function ($rootScope, $q, $injector, $location) {
return function(promise) {
retu...
$httpProvider.responseInterceptors.push(
function ($rootScope, $q, $injector, $location) {
return function(promise) {
retu...
$httpProvider.responseInterceptors.push(
function ($rootScope, $q, $injector, $location) {
return function(promise) {
retu...
Environments
• Pretty usual to deal with prod, dev, testing
environment config on the server side
• Inject this into your ...
Environments
<script type="text/javascript" src="env/config.js"></script>
Environments
angular.module('talis.environment', [], function($provide)
constant('API_ENDPOINT', 'http://localhost:3000')....
Environments
angular.module('talis.environment', [], function($provide)
constant('API_ENDPOINT', 'https://talis.com').
con...
That’s it.
http://engineering.talis.com
We are hiring!
http://www.talis.com/jobs
@talis
facebook.com/talisgroup
+44 (0) 121 374 2740
talis.com
info@talis.com
48 Frederick Street
Birmingham
B1 3HN
MEAN - Notes from the field (Full-Stack Development with Javascript)
MEAN - Notes from the field (Full-Stack Development with Javascript)
MEAN - Notes from the field (Full-Stack Development with Javascript)
Upcoming SlideShare
Loading in...5
×

MEAN - Notes from the field (Full-Stack Development with Javascript)

848

Published on

Full-Stack Development with Javascript Angular/Node (AngularJS/NodeJS)

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
848
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
30
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide
  • Anyone been playing this?
  • WTF?
  • Check out the score - weird!
    Back to work…
  • Whole stack is JS, from the DB, the native data format (BSON), the server side and the front end. Even your DB queries are JSON and the DB client for debugging
  • Birmingham
    EdTech learning platform
    65 unis
    1 million students 50% UK + other campuses on 3 continents
    ~15M hits per week page/api
    Legacy in PHP
  • Node app here.
  • Angular app here
  • Note the node app serves index.html which includes app.js which bootstraps the angular app
  • Some things we are building with MEAN
    Timeline editing UI
  • Textbooks
    Copy and paste from OCRed images
    Rich annotations + notes
    In-book search
  • Bi directional is AWESOME!
    Whistle stop tour
  • Bi directional is AWESOME!
    Whistle stop tour
  • Pretty familiar
  • bind to params in URL
  • our HTML template
  • the controller
  • can add extra properties to the route
  • update() is a method in the CONTROLLER
  • Does this via event loop
  • dependancy injection
    $scope and userSvc are singletons
  • SERVICES - generally logic for persisting models or other interactions with the server side
  • wrapping behavior and logic to specific tags, independent from view-level controller
    independent scope, many on same view
    great for re-use
  • user and textbook passed as parameters from view scope
  • More common to use as attribute name
  • namespace
  • directive function
  • view scope -&amp;gt; directive scope (user/entity)
  • directive controller - $scope is injected by framework
  • If you’re a client side developer then Angular comes as a shock
    Actually if you’re a server side guy you’ll be more at home
    Resist JQuery
  • First some common gotchas
  • For IE compat use attribute
  • Dependancy injection
    Minification will shorten $scope, userSvc
    Array param will assume last param func, preceeding params strings mapping to vars
  • End result
  • Caught us out moving to 1.2
    Angular assumes all _ properties are private
    PITA in templates
    Backed out in later versions
  • You can find these on our blog
  • Remember this?
  • Talis App shape - SOA
    Proxy API
    CORS
    ripple of change protection - what version of angular app is it?
    api versioning
  • Angular has built in $exceptionHandler that will log to console :-/
  • Why are we using $.ajax?
  • Collect more feedback from user
    Propigate unexpected errors up to $rootScope
    Form in your master template
  • Logstash/ElasticSearch/Kibana
    Consider watermarking log statements through stack using an ID
  • https!!
  • transformRequest
    in your app’s run method
    adds the token (where available) to every single request made with $http
  • Dealing with token expiry and re-request
    responseInterceptor
  • Dealing with token expiry and re-request
    responseInterceptor
  • Dealing with token expiry and re-request
    responseInterceptor
  • Dealing with token expiry and re-request
    responseInterceptor
  • Dealing with token expiry and re-request
    responseInterceptor
  • Dealing with token expiry and re-request
    responseInterceptor
  • Dealing with token expiry and re-request
    responseInterceptor
  • In your main page
  • Dynamically generated by a service in node
    constants available anywhere by dependancy injection
  • in production
  • You can find these on our blog
  • Transcript of "MEAN - Notes from the field (Full-Stack Development with Javascript)"

    1. 1. MEAN - Notes from the field Chris Clarke Hydrahack Birmingham 18th March 2014 Full-Stack Development with Javascript
    2. 2. • Mongo • Express • AngularJS • NodeJS http://github.com/linnovate/mean What’s MEAN?
    3. 3. Who are Talis?
    4. 4. • MongoDB ~2.5yrs • Using express/node ~2yrs • Angular ~9 months
    5. 5. Angular AppAngular App Typical MEAN Shape DBDB APIAPI Server side pages Server side pages StaticsStatics JSON JSON HTMLHTML JSON Client side Server side
    6. 6. Typical structure
    7. 7. Typical structure
    8. 8. Typical structure
    9. 9. Typical structure
    10. 10. Video Timeline Editor
    11. 11. Textbook Player
    12. 12. Angular 101 • Single page web app framework, by Google • Extends HTML vocabulary to provide dynamic views • Broadly MVC (more accurately MVVM) • Bi-directional data binding to HTML
    13. 13. Angular 101 • Routing • Templates • Controllers • Directives
    14. 14. Routing $routeProvider.when('/modules/:module_id', { templateUrl: 'partials/module.html', controller: 'TeachCtrl', loginRequired: true, activeTab:"teach" });
    15. 15. Routing $routeProvider.when('/modules/:module_id', { templateUrl: 'partials/module.html', controller: 'TeachCtrl', loginRequired: true, activeTab:"teach" });
    16. 16. Routing $routeProvider.when('/modules/:module_id', { templateUrl: 'partials/module.html', controller: 'TeachCtrl', loginRequired: true, activeTab:"teach" });
    17. 17. Routing $routeProvider.when('/modules/:module_id', { templateUrl: 'partials/module.html', controller: 'TeachCtrl', loginRequired: true, activeTab:"teach" });
    18. 18. Routing $routeProvider.when('/modules/:module_id', { templateUrl: 'partials/module.html', controller: 'TeachCtrl', loginRequired: true, activeTab:"teach" });
    19. 19. <ul ng-show="modules!=null"> <li ng-repeat="m in modules | orderBy:'title'" ng-class="{active:module._id==m._id}"> <a ng-href="#/modules/{{ m._id }}">{{m.title}}</a> </li> <li> <a ng-click="add()">Add new</a> </li> </ul>
    20. 20. <ul ng-show="modules!=null"> <li ng-repeat="m in modules | orderBy:'title'" ng-class="{active:module._id==m._id}"> <a ng-href="#/modules/{{ m._id }}">{{m.title}}</a> </li> <li> <a ng-click="add()">Add new</a> </li> </ul>
    21. 21. <ul ng-show="modules!=null"> <li ng-repeat="m in modules | orderBy:'title'" ng-class="{active:module._id==m._id}"> <a ng-href="#/modules/{{ m._id }}">{{m.title}}</a> </li> <li> <a ng-click="add()">Add new</a> </li> </ul>
    22. 22. <ul ng-show="modules!=null"> <li ng-repeat="m in modules | orderBy:'title'" ng-class="{active:module._id==m._id}"> <a ng-href="#/modules/{{ m._id }}">{{m.title}}</a> </li> <li> <a ng-click="add()">Add new</a> </li> </ul>
    23. 23. <ul ng-show="modules!=null"> <li ng-repeat="m in modules | orderBy:'title'" ng-class="{active:module._id==m._id}"> <a ng-href="#/modules/{{ m._id }}">{{m.title}}</a> </li> <li> <a ng-click="add()">Add new</a> </li> </ul>
    24. 24. <ul ng-show="modules!=null"> <li ng-repeat="m in modules | orderBy:'title'" ng-class="{active:module._id==m._id}"> <a ng-href="#/modules/{{ m._id }}">{{m.title}}</a> </li> <li> <a ng-click="add()">Add new</a> </li> </ul>
    25. 25. <ul ng-show="modules!=null"> <li ng-repeat="m in modules | orderBy:'title'" ng-class="{active:module._id==m._id}"> <a ng-href="#/modules/{{ m._id }}">{{m.title}}</a> </li> <li> <a ng-click="add()">Add new</a> </li> </ul>
    26. 26. <input ng-model="profile.first_name" type="text" required> <input ng-model="profile.surname" type="text" required> <input ng-model="profile.email" type="email" required> <button ng-disabled="!profile.email" ng-click="update()">Update</button>
    27. 27. <input ng-model="profile.first_name" type="text" required> <input ng-model="profile.surname" type="text" required> <input ng-model="profile.email" type="email" required> <button ng-disabled="!profile.email" ng-click="update()">Update</button>
    28. 28. <input ng-model="profile.first_name" type="text" required> <input ng-model="profile.surname" type="text" required> <input ng-model="profile.email" type="email" required> <button ng-disabled="!profile.email" ng-click="update()">Update</button>
    29. 29. <input ng-model="profile.first_name" type="text" required> <input ng-model="profile.surname" type="text" required> <input ng-model="profile.email" type="email" required> <button ng-disabled="!profile.email" ng-click="update()">Update</button>
    30. 30. QuickTime™ and a 'avc1' decompressor are needed to see this picture.
    31. 31. Controllers Horizontal angular.module('talis.controllers.user', []) .controller('AccountCtrl',function($scope, userSvc) { // update the profile $scope.update = function() { userSvc.updateProfile($scope.profile,function(err,profile) { if (!err) { $scope.profile = profile; } }); }) .controller('SomeOtherCtrl',....);
    32. 32. Controllers Horizontal angular.module('talis.controllers.user', []) .controller('AccountCtrl',function($scope, userSvc) { // update the profile $scope.update = function() { userSvc.updateProfile($scope.profile,function(err,profile) { if (!err) { $scope.profile = profile; } }); }) .controller('SomeOtherCtrl',....);
    33. 33. Controllers Horizontal angular.module('talis.controllers.user', []) .controller('AccountCtrl',function($scope, userSvc) { // update the profile $scope.update = function() { userSvc.updateProfile($scope.profile,function(err,profile) { if (!err) { $scope.profile = profile; } }); }) .controller('SomeOtherCtrl',....);
    34. 34. Controllers Horizontal angular.module('talis.controllers.user', []) .controller('AccountCtrl',function($scope, userSvc) { // update the profile $scope.update = function() { userSvc.updateProfile($scope.profile,function(err,profile) { if (!err) { $scope.profile = profile; } }); }) .controller('SomeOtherCtrl',....);
    35. 35. Controllers Horizontal angular.module('talis.controllers.user', []) .controller('AccountCtrl',function($scope, userSvc) { // update the profile $scope.update = function() { userSvc.updateProfile($scope.profile,function(err,profile) { if (!err) { $scope.profile = profile; } }); }) .controller('SomeOtherCtrl',....);
    36. 36. Controllers Horizontal angular.module('talis.controllers.user', []) .controller('AccountCtrl',function($scope, userSvc) { // update the profile $scope.update = function() { userSvc.updateProfile($scope.profile,function(err,profile) { if (!err) { $scope.profile = profile; } }); }) .controller('SomeOtherCtrl',....);
    37. 37. Directives <textbook-player user="user" textbook="textbook"> ... </textbook-player>
    38. 38. Directives <textbook-player user="user" textbook="textbook"> ... </textbook-player>
    39. 39. Directives <div user="user" textbook="textbook" textbook-player> ... </div>
    40. 40. Directives <div user="user" textbook="textbook" textbook-player> ... </div> angular.module('talis.directives.player.textbook', []) .directive("textbookPlayer", function() { return { restrict: "A", scope: { user: '=', entity: '=' }, controller: function($scope,textbookSvc) { // textbook logic in here } } });
    41. 41. Directives <div user="user" textbook="textbook" textbook-player> ... </div> angular.module('talis.directives.player.textbook', []) .directive("textbookPlayer", function() { return { restrict: "A", scope: { user: '=', entity: '=' }, controller: function($scope,textbookSvc) { // textbook logic in here } } });
    42. 42. Directives <div user="user" textbook="textbook" textbook-player> ... </div> angular.module('talis.directives.player.textbook', []) .directive("textbookPlayer", function() { return { restrict: "A", scope: { user: '=', entity: '=' }, controller: function($scope,textbookSvc) { // textbook logic in here } } });
    43. 43. Directives <div user="user" textbook="textbook" textbook-player> ... </div> angular.module('talis.directives.player.textbook', []) .directive("textbookPlayer", function() { return { restrict: "A", scope: { user: '=', entity: '=' }, controller: function($scope,textbookSvc) { // textbook logic in here } } });
    44. 44. –Jonny Clientside “Waat?”
    45. 45. Notes From the Field Act I: The Basics
    46. 46. Elem vs. Attr directives <textbook-player user="user" textbook="textbook"> ... </textbook-player> <div user="user" textbook="textbook" textbook-player> ... </div>
    47. 47. Minification angular.module('talis.controllers.user', []) .controller('AccountCtrl',function($scope, userSvc) { .. }); angular.module('talis.controllers.user', []) .controller('AccountCtrl',['$scope','userSvc’, function($scope, userSvc) { ... } ]);
    48. 48. Minification angular.module('talis.controllers.user', []) .controller('AccountCtrl',function($scope, userSvc) { .. }); angular.module('talis.controllers.user', []) .controller('AccountCtrl',['$scope','userSvc’, function($scope, userSvc) { ... } ]); a.m('talis.controllers.user', []) .c('AccountCtrl',['$scope','userSvc’, function(s, u) { ... } ]);
    49. 49. Mongo _id { _id: ObjectId(1234), name: “Jonny Clientside”, age: 24, interests: [‘JQuery’,‘HTML5’ } <a ng-href="#/people/{{ p._id }}">{{p.name}}</a>
    50. 50. Notes From the Field Act II: Advanced
    51. 51. Angular AppAngular App Typical MEAN Shape DBDB APIAPI Server side pages Server side pages StaticsStatics JSON JSON HTMLHTML JSON Client side Server side
    52. 52. Angular AppAngular App JSON 9090 9090 9090 9090 Users API Users API APIAPI Server side pages Server side pages StaticsStatics JSON JSON JSON Client side Meta API Meta API Files API Files API Anno API Anno API JSON DBDBDBDBDBDB DBDB RedisRedisRedisRedis HTMLHTML JSON
    53. 53. Logging • A lot of activity in the client side • Some within Express/Node server side • More behind your API proxy
    54. 54. var loggingModule = angular.module('talis.services.logging', []); loggingModule.factory( "traceService", function(){ return({ print: printStackTrace }); } ); loggingModule.provider( "$exceptionHandler",{ $get: function(exceptionLoggingService){ return(exceptionLoggingService); } } );
    55. 55. var loggingModule = angular.module('talis.services.logging', []); loggingModule.factory( "traceService", function(){ return({ print: printStackTrace }); } ); loggingModule.provider( "$exceptionHandler",{ $get: function(exceptionLoggingService){ return(exceptionLoggingService); } } );
    56. 56. loggingModule.factory( "exceptionLoggingService", ["$log","$window", "traceService", function($log, $window, traceService){ function error(exception, cause){ $log.error.apply($log, arguments); try{ var errorMessage = exception.toString(); var stackTrace = traceService.print({e: exception}); $.ajax({ type: "POST", url: "/logger", contentType: "application/json", data: angular.toJson({ url: $window.location.href, message: errorMessage, type: "exception", stackTrace: stackTrace, cause: ( cause || "") }) }); } catch (loggingError){ $log.warn("Error server-side logging failed"); $log.log(loggingError); } } return(error); }] );
    57. 57. Logging
    58. 58. Security • APIs secured with OAuth 2.0 Bearer tokens • Tokens obtained with a key/secret • If your app is downloaded and run on the client, where do you put the secret?
    59. 59. Security • Have node return the OAuth token as JSON behind a login barrier • Angular requests this JSON when a route that requires login is first requsted • If status != 200, Angular app redirects browser to login page • User logs in, repeat
    60. 60. Security • Dealing with tokens on every service call is a PITA • Tokens expiring is normal • Deal with it globally using a couple of advanced $http features
    61. 61. .run(function($rootScope,$injector) { $injector.get("$http").defaults.transformRequest = function(data, headersGetter) { headersGetter()['Authorization']="Bearer "+$rootScope.token if (data) { return angular.toJson(data); } }; });
    62. 62. $httpProvider.responseInterceptors.push( function ($rootScope, $q, $injector, $location) { return function(promise) { return promise.then(function(response) { return response; // no action, was successful }, function (response) { // error - was it 401 or something else? if (response.status===401 && response.data.error && response.data.error === "invalid_token") { var deferred = $q.defer(); // defer until we can re-request a new token // Get a new token... (cannot inject $http directly as will cause a circular ref) $injector.get("$http").jsonp('/some/endpoint/that/reissues/tokens?cb=JSON_CALLBACK') .then(function(loginResponse) { if (loginResponse.data) { $rootScope.oauth = loginResponse.data.oauth; // we have a new oauth token - set at $rootScope // now let's retry the original request $injector.get("$http")(response.config).then(function(response) { // we have a successful response - resolve it using deferred deferred.resolve(response); },function(response) { deferred.reject(); // something went wrong }); } else { deferred.reject(); // login.json didn't give us data } }, function(response) { deferred.reject(); // token retry failed, redirect so user can login again $location.path('/user/sign/in'); return; }); return deferred.promise; // return the deferred promise } return $q.reject(response); // not a recoverable error }); }; });
    63. 63. $httpProvider.responseInterceptors.push( function ($rootScope, $q, $injector, $location) { return function(promise) { return promise.then(function(response) { return response; // no action, was successful }, function (response) { // error - was it 401 or something else? if (response.status===401 && response.data.error && response.data.error === "invalid_token") { var deferred = $q.defer(); // defer until we can re-request a new token // Get a new token... (cannot inject $http directly as will cause a circular ref) $injector.get("$http").jsonp('/some/endpoint/that/reissues/tokens?cb=JSON_CALLBACK') .then(function(loginResponse) { if (loginResponse.data) { $rootScope.oauth = loginResponse.data.oauth; // we have a new oauth token - set at $rootScope // now let's retry the original request $injector.get("$http")(response.config).then(function(response) { // we have a successful response - resolve it using deferred deferred.resolve(response); },function(response) { deferred.reject(); // something went wrong }); } else { deferred.reject(); // login.json didn't give us data } }, function(response) { deferred.reject(); // token retry failed, redirect so user can login again $location.path('/user/sign/in'); return; }); return deferred.promise; // return the deferred promise } return $q.reject(response); // not a recoverable error }); }; });
    64. 64. $httpProvider.responseInterceptors.push( function ($rootScope, $q, $injector, $location) { return function(promise) { return promise.then(function(response) { return response; // no action, was successful }, function (response) { // error - was it 401 or something else? if (response.status===401 && response.data.error && response.data.error === "invalid_token") { var deferred = $q.defer(); // defer until we can re-request a new token // Get a new token... (cannot inject $http directly as will cause a circular ref) $injector.get("$http").jsonp('/some/endpoint/that/reissues/tokens?cb=JSON_CALLBACK') .then(function(loginResponse) { if (loginResponse.data) { $rootScope.oauth = loginResponse.data.oauth; // we have a new oauth token - set at $rootScope // now let's retry the original request $injector.get("$http")(response.config).then(function(response) { // we have a successful response - resolve it using deferred deferred.resolve(response); },function(response) { deferred.reject(); // something went wrong }); } else { deferred.reject(); // login.json didn't give us data } }, function(response) { deferred.reject(); // token retry failed, redirect so user can login again $location.path('/user/sign/in'); return; }); return deferred.promise; // return the deferred promise } return $q.reject(response); // not a recoverable error }); }; });
    65. 65. $httpProvider.responseInterceptors.push( function ($rootScope, $q, $injector, $location) { return function(promise) { return promise.then(function(response) { return response; // no action, was successful }, function (response) { // error - was it 401 or something else? if (response.status===401 && response.data.error && response.data.error === "invalid_token") { var deferred = $q.defer(); // defer until we can re-request a new token // Get a new token... (cannot inject $http directly as will cause a circular ref) $injector.get("$http").jsonp('/some/endpoint/that/reissues/tokens?cb=JSON_CALLBACK') .then(function(loginResponse) { if (loginResponse.data) { $rootScope.oauth = loginResponse.data.oauth; // we have a new oauth token - set at $rootScope // now let's retry the original request $injector.get("$http")(response.config).then(function(response) { // we have a successful response - resolve it using deferred deferred.resolve(response); },function(response) { deferred.reject(); // something went wrong }); } else { deferred.reject(); // login.json didn't give us data } }, function(response) { deferred.reject(); // token retry failed, redirect so user can login again $location.path('/user/sign/in'); return; }); return deferred.promise; // return the deferred promise } return $q.reject(response); // not a recoverable error }); }; });
    66. 66. $httpProvider.responseInterceptors.push( function ($rootScope, $q, $injector, $location) { return function(promise) { return promise.then(function(response) { return response; // no action, was successful }, function (response) { // error - was it 401 or something else? if (response.status===401 && response.data.error && response.data.error === "invalid_token") { var deferred = $q.defer(); // defer until we can re-request a new token // Get a new token... (cannot inject $http directly as will cause a circular ref) $injector.get("$http").jsonp('/some/endpoint/that/reissues/tokens?cb=JSON_CALLBACK') .then(function(loginResponse) { if (loginResponse.data) { $rootScope.oauth = loginResponse.data.oauth; // we have a new oauth token - set at $rootScope // now let's retry the original request $injector.get("$http")(response.config).then(function(response) { // we have a successful response - resolve it using deferred deferred.resolve(response); },function(response) { deferred.reject(); // something went wrong }); } else { deferred.reject(); // login.json didn't give us data } }, function(response) { deferred.reject(); // token retry failed, redirect so user can login again $location.path('/user/sign/in'); return; }); return deferred.promise; // return the deferred promise } return $q.reject(response); // not a recoverable error }); }; });
    67. 67. $httpProvider.responseInterceptors.push( function ($rootScope, $q, $injector, $location) { return function(promise) { return promise.then(function(response) { return response; // no action, was successful }, function (response) { // error - was it 401 or something else? if (response.status===401 && response.data.error && response.data.error === "invalid_token") { var deferred = $q.defer(); // defer until we can re-request a new token // Get a new token... (cannot inject $http directly as will cause a circular ref) $injector.get("$http").jsonp('/some/endpoint/that/reissues/tokens?cb=JSON_CALLBACK') .then(function(loginResponse) { if (loginResponse.data) { $rootScope.oauth = loginResponse.data.oauth; // we have a new oauth token - set at $rootScope // now let's retry the original request $injector.get("$http")(response.config).then(function(response) { // we have a successful response - resolve it using deferred deferred.resolve(response); },function(response) { deferred.reject(); // something went wrong }); } else { deferred.reject(); // login.json didn't give us data } }, function(response) { deferred.reject(); // token retry failed, redirect so user can login again $location.path('/user/sign/in'); return; }); return deferred.promise; // return the deferred promise } return $q.reject(response); // not a recoverable error }); }; });
    68. 68. $httpProvider.responseInterceptors.push( function ($rootScope, $q, $injector, $location) { return function(promise) { return promise.then(function(response) { return response; // no action, was successful }, function (response) { // error - was it 401 or something else? if (response.status===401 && response.data.error && response.data.error === "invalid_token") { var deferred = $q.defer(); // defer until we can re-request a new token // Get a new token... (cannot inject $http directly as will cause a circular ref) $injector.get("$http").jsonp('/some/endpoint/that/reissues/tokens?cb=JSON_CALLBACK') .then(function(loginResponse) { if (loginResponse.data) { $rootScope.oauth = loginResponse.data.oauth; // we have a new oauth token - set at $rootScope // now let's retry the original request $injector.get("$http")(response.config).then(function(response) { // we have a successful response - resolve it using deferred deferred.resolve(response); },function(response) { deferred.reject(); // something went wrong }); } else { deferred.reject(); // login.json didn't give us data } }, function(response) { deferred.reject(); // token retry failed, redirect so user can login again $location.path('/user/sign/in'); return; }); return deferred.promise; // return the deferred promise } return $q.reject(response); // not a recoverable error }); }; });
    69. 69. Environments • Pretty usual to deal with prod, dev, testing environment config on the server side • Inject this into your client side app using a dynamic JS include
    70. 70. Environments <script type="text/javascript" src="env/config.js"></script>
    71. 71. Environments angular.module('talis.environment', [], function($provide) constant('API_ENDPOINT', 'http://localhost:3000'). constant('ACTIVATE_FEATURE_FLIPS',true);
    72. 72. Environments angular.module('talis.environment', [], function($provide) constant('API_ENDPOINT', 'https://talis.com'). constant('ACTIVATE_FEATURE_FLIPS',false);
    73. 73. That’s it.
    74. 74. http://engineering.talis.com
    75. 75. We are hiring! http://www.talis.com/jobs
    76. 76. @talis facebook.com/talisgroup +44 (0) 121 374 2740 talis.com info@talis.com 48 Frederick Street Birmingham B1 3HN
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×