On Github alicial / leveling-up-angular-talk
Sr Software Engineer, Lift
Supports asynchronous loading of modules in the future
var Mario = function() { this.size = 'small'; }; Mario.prototype.eatMushroom = function() { this.size = 'large'; }; myModule.service('marioService', Mario); // calls new Mario()
myModule.factory('marioService', function() { var mario = { size: 'small' }; return { eatMushroom: function() { mario.size = 'large'; } }; });
myModule.provider('marioService', function() { var config = { size: 'small' }; return { setSize: function(size) { config.size = size; }, $get: function(utilities) { return { eatMushroom: function() { ... }, // other methods } } }; });
// in some service $rootScope.$broadcast(MARIO_LOADED, { mario: data}); // when data loads
// in some controller $scope.$on(MARIO_LOADED, doStuffWithMario);
What happens if the data doesn't load?
// in some service var deferred = $q.defer(); svc.getMario = function() { return deferred.promise; }; ... deferred.resolve(data); // when some data loads ... deferred.reject(error); // if data doesn't load
// in some controller svc.getMario().then( doStuffWithMario, displayError );
myModule.config(function($routeProvider) { $routeProvider.when('/character/:id', { templateUrl: 'profile.html', controller: 'CharacterCtrl', resolve: { character: function($route, CharacterService) { // returns a promise return CharacterService.get($route.current.params.id); } } }); });
myModule.controller('CharacterCtrl', function($scope, character) { $scope.character = character; });
Why Write Custom Directives?
<enemy enemy-type="{{enemyType}}" lives="lives" on-destroy="destroy()"></enemy>
angular.module('demo.characters').directive('enemy', function() { return { template: "<div ng-class="{goomba: type == \"small\", bowser: type == \"big\"}"></div>", replace: true, restrict: "E", scope: { type: "@enemyType", currentLives: "=lives", onDestroy: "&" }, link: function(scope, iElement, iAttrs) { ... } }; });
scope: { type: "@enemyType", currentLives: "=lives", onDestroy: "&" }
link: function(scope, iElement, iAttrs) { var $enemy = $(iElement[0]); scope.$watch("currentLives", function(newLives, oldLives) { if (newLives > oldLives) { $enemy.animate({width:"+=10px",height:"+=10px"},150).animate({width:"-=10px",height:"-=10px"},150); } else if (newLives < oldLives) { $enemy.animate({width:"-=10px",height:"-=10px"},150).animate({width:"+=10px",height:"+=10px"},150); } }); }
// jQuery Land $(document).on('click.fireball', function(e) { var $fireball = $("<div class='fire-ball'></div>"); var offset = $character.offset(); $fireball.css({top: offset.top + ($character.height()/2), left: offset.left + $character.width()}); $character.after($fireball); $fireball.animate({top: e.pageY, left: e.pageX}, function() { $fireball.remove(); $(e.target).trigger("attack"); }); });
// in Angular directive link: function(scope, iElement, iAttrs) { var $enemy = $(iElement[0]); $enemy.on("attack", function(e) { scope.$apply(function() { scope.currentLives = scope.currentLives - 1; if (scope.currentLives === 0) { $enemy.remove(); scope.onDestroy(); } }); }); }
http://alicialiu.net/leveling-up-angular-talk
Images adapted from Mario Wiki
angular.module('static').directive('staticLinky', function ($filter) { return { restrict: 'A', compile: function(tElem) { var $elem = $(tElem); $elem.html($filter('linky')($elem.text(), "_blank")); } }; });
<p static-linky>Link this http://example.com</p>
// templates.js angular.module('myTemplates', []) .run(['$templateCache', function($templateCache) { $templateCache.put("template1.html", "<h1>Hello World!</h1>"); $templateCache.put("template2.html", "..."); }]);
Rails/Ruby example
// templates.js.erb angular.module('myTemplates', []) .run(['$templateCache', function($templateCache) { <% Dir.glob(Rails.root.join('app','assets','templates', '*.html')).each do |f| %> $templateCache.put("<%= File.basename(f) %>", <%= File.read(f).to_json %>); <% end %> }]);
<mario fire-mode="{{mario.mode}}"></mario>
myModule.directive('mario', function() { return { restrict: "E", scope: { fireMode: "@" } } }). directive('fireMode', function() { return { restrict: "A", link: function(scope, iElement, iAttrs) { var $character = $(iElement[0]); iAttrs.$observe('fireMode', function(mode) { if (mode === "fire") { $character.addClass("fire-mode"); } else { $character.removeClass("fire-mode"); } }); } }; })
Angular sample code
// declare a module var myAppModule = angular.module('myApp', []); // configure the module myAppModule.config( //... ); // Add a controller function Ctrl($scope) { //... }
Global variables WTF?!
angular.module('myApp').config( //... ); angular.module('myApp').controller('myController', function() { //... });