Sr Software Engineer, Lift
Supports asynchronous loading of modules in the future
Features / Recipe type | Factory | Service | Provider |
---|---|---|---|
can have dependencies | yes | yes | yes |
uses type friendly injection | no | yes | no |
object available in config phase | no | no | yes |
can create functions/primitives | yes | no | yes |
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?
$q
// 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 );
$q.all
$q.when
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: "",
replace: true,
restrict: "E",
scope: {
type: "@enemyType",
currentLives: "=lives",
onDestroy: "&"
},
link: function(scope, iElement, iAttrs) { ... }
};
});
<my-directive>
<div my-directive>
<div class="my-directive">
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", "Hello World!
");
$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() {
//...
});