Controllers & $scope
Services
Filters
Directives
controllers & $scope
* "familiar with controllers"
* from other MVCs...
* arguably more of a "ViewModel"
* c/w Knockout.js
* main function of controller:
* set up execution context for your views
* that's `$scope`
* putting your data onto `$scope`
* putting your methods etc. onto `$scope`
* example:
* `ng-hide="{{ thing == true }}"`
* and/but have that more than once and consider
* `ng-hide="hasThing()"`
* yes: you can have more than 1 per "page"
* and you _should_ b/c each controller should really just be responsible for
_one thing_
* `$scope` inheritance
* as prototypal...
* controllers w/in controllers...
* directives w/in controllers
* `$scope.thing` ... `thing` may _actually_ be from `$scope.$parent`
* see also: `$rootScope`
example controller
angular.module('books').controller('BookListController', [
// set up dependency injection:
'$scope', '$window',
// controller (DI through function arguments)
function($scope, $window) {
$scope.books = $window.BOOKS;
$scope.getStarClass = function(rating, compare) {
return 'glyphicon glyphicon-star' +
((rating > compare) ? '' : '-empty');
};
// and so on
}
]);
...and hook it up
<!DOCTYPE html>
<html lang="en" ng-app="books">
<body>
<div ng-controller="BookListController">
<table>
<tr ng-repeat="book in books">
<td>{{book.title}}</td>
<td>{{book.author}}</td>
<td>{{book.rating}}</td>
</tr>
</table>
</div>
</body>
</html>
roughly illustrated
* important point: `$scope` is not your model
* `$scope` is the surface area b/w controller + view
* remember: ng has not strong opinions about model
* "bring your own model" philosophy
* your model is largely a back-end concern
* (hence putting it in a little abstract cloud here)
* 2-way relationships, but mediated
Services
* anything you want to share b/w controllers
* you're not setting up inheritance chains b/w controllers
* share code through DI
* side note: the "DI" bit bothers me - you're just
passing references to the service singletons into your
controllers as function args
* _anyway_ - "inject" as args into contructor functions
for controllers, directives, other services
* side note: the DI winds up feeling a bit like "ceremony"
* THINK FUNCTIONALLY HERE
* w/ services you don't want side effects
* (just arguments and return values)
* (side note: Angular is designed to not have side effects - remember: "lethally allergic to global state")
example service
angular.module('books').factory('goodreadsService', ['$http',
function($http) {
function getShelf(userId, shelf) {
return $http.get(GOODREADS_HOST + REVIEWS_LIST, {
params: {
v: 2, key: API_KEY,
id: userId, shelf: shelf
}
});
}
return {
getShelf: getShelf
};
}
]);
Filters
* _don't_ transform data in your controllers
* data (on your model) goes _directly_ to the view via
`$scope`
* then: `{{ value | filter: filterArg }}`
* transformations happen like that
* AGAIN : the idea there is to minimize side effects
* i.e., don't transform the data "in place" (in the model)
* transform it for the view where it makes sense (in the
view)
* "pure functions" - simply returning the transformed value
to the rendered view
example filter
angular.module('books').filter('dropSubTitle', [
function() {
return function(input, token) {
return input.split(token || ':')[0];
};
}
]);
...and using it
<ul>
<li ng-repeat="book in books">
<em ng-bind="book.title | dropSubTitle:'-'"></em> by
<span ng-bind="book.author"></span>
</li>
</ul>
Directives
* THIS IS THE MAGIC
* "extending" HTML
* turns `
` into... whatever
you want
* directives and `$scope`
* compiling + linking
* templates + templateUrl
* restriction
* Attributes
* Classes
* Elements
* coMments
* the "core" ng directives make up the heart of the
framework
* `ng-app`? `ng-controller`? consider that those are
both directives necessary to even bootstrap the app
* EXCELLENT WAY to break your app down into small pieces
example directive
angular.module('books').directive('grRating', [
function() {
return {
replace: true,
restrict: 'AC',
templateUrl: '/ng-templates/gr-rating',
scope: { rating: '=' },
link: function(scope, element, attrs) {
scope.getStarCss = function(compare) {
return 'glyphicon-star' +
((scope.rating > compare) ? '' : '-empty';
};
}
};
}
]);
/ng-templates/gr-rating
<div>
<span class="glyphicon" ng-class="getStarCss(0)"></span>
<span class="glyphicon" ng-class="getStarCss(1)"></span>
<span class="glyphicon" ng-class="getStarCss(2)"></span>
<span class="glyphicon" ng-class="getStarCss(3)"></span>
<span class="glyphicon" ng-class="getStarCss(4)"></span>
</div>
using that directive
<div ng-controller="BookListController">
<ul>
<li ng-repeat="book in books">
<!-- and our directive: -->
<span gr-rating data-rating="book.rating"></span>
</li>
</ul>
</div>
* mention: could have done it using a class
...and much more!
core APIs
jqLite
routes
$apply
, $digest
, and $watch
...
testing
* plenty I'm not covering here
* core APIs
* called "global APIs" in the docs
* but hey... "global" is a dirty word
* the point: some utils to help keep other libraries out
* not _to_ keep them out but: why bring in all of jQuery
if all you need are some simple selectors?
* which leads us to...
* jqLite (explain!)
* routes (for making sane and sensible and shareable URLs
for client-side state...)
* `$apply`, `$digest`, + `$watch`
* noted: some of the "magic" around `$scope` objects
* the digests are on short but regular intervals
* ...to try to prevent ng from performing poorly
* "don't watch yourself to death"
* some added security there - i.e., throw errors if
items in the apply/digest loop start setting each
other recursively and dangerously
* testing (testable out of the box!)