app.js
components/
users/
users-service.js
users-directive.js
users-controller.js
users.scss
users.html
version/
version-directive.js
version-controller.js
version.scss
version.html
use the generator.
angular.module('myApp', [])
.factory(MyFactory);
function MyService($dep1, dep2, dep3) { ... }
angular.module('myApp', [])
.factory(['$dep1', 'dep2', 'dep3', MyService]);
function MayService($dep1, dep2, dep3) { ... }
angular.module('myApp', [])
.factory(MyService);
MyService.$inject = ['$dep1', 'dep2', 'dep3']; //Function properties as decorators?
function MyService($dep1, dep2, dep3) { ... }
the "right" way ⤴
angular.module('myApp', [])
.factory(MyService);
function MyService($dep1, dep2, dep3) {
'ngInject';
...
}
MWS uses the ng-annotate gulp plugin
Identify functions that need annotations with the 'ngInject';
prologue
use the generator!
app.directive('myComponent', myComponent);
function myComponent() {
return {
...
restrict: 'EA'
};
}
app.directive('myComponent', myComponent);
function myComponent() {
return {
...
link: linkFn
};
function linkFn(scope, element, attributes) {
//DOM manipulation here only!
}
}
app.directive('myComponent', myComponent);
function myComponent() {
return {
...
link: linkFn
};
function linkFn() {
scope.$watch('title', function (prevVal, currVal) {
//this is super taxing and will bog down your application
});
}
}
app.module('my-app')
.controller('myCompCtrl', myCompCtrl);
function myCompCtrl() {
return {
onChange: function (evt) {
// react to change.
}
};
}
//my-component.html template
<input type="text" ng-change="onChange" />
React to changes in the directive's controller.
app.module('my-app')
.directive('myComp', myComp);
function myComp() {
return {
restrict: 'EA',
controller: myCompCtrl,
controllerAs: 'myCompCtrl'
bindToController: true,
link: linkFn,
}
};
}
Watchers are the methamphetamine of any angular application. It serves a purpose, but it's highly addictive and extremely damaging. The worst part is, out of the box, Angular acts more like a dealer than a pharmacist.
$digest is the problem. The $digest cycle is a loop over all your bindings that checks for changes and re-renders accordingly.
Bindings happen automatically not only for `{{ }}` but expressions including ng-if, ng-class, and most damning, ng-repeat
Angular 1.3 introduced a syntax for binding a value only once. This should be the default, and in fact, in Angular 2 it is.
Until then, ALWAYS use one time binding.
{{:: myCtrl.user }}
<div ng-if="::myCtrl.user"></div>
<div ng-class="::{ user: myCtrl.user }"></div>
<ol>
<li ng-repeat="user in ::myCtrl.users"></li>
</ol>
Scopes are arranged in hierarchical structure which mimic the DOM structure of the application
- https://docs.angularjs.org/guide/scope
OMG wat?!?!?!
<div class="show-scope-demo" ng-controller="GreetController">
<h1>Hello {{name}}</h1>
<div ng-controller="ListController">
<ol>
<li ng-repeat="name in names">
{{name}} from {{department}} says hi to {{$parent.name}}
</li>
</ol>
</div>
</div>
OMG! (Oh my gross!)
Just. Why? When you don't have to?
<div ng-controller="MainCtrl">
{{ title }}
<div ng-controller="AnotherCtrl">
Scope title: {{ title }}
MainCtrl's title: {{ $parent.title }}
<div ng-controller="YetAnotherCtrl">
{{ title }}
YetAnotherCtrl's title: {{ $parent.title }}
MainCtrl's title: {{ $parent.$parent.title }}
</div>
</div>
</div>
Each nested controller has to have knowledge of controller(s) it's composed with and at what depth.
controllerAs
FTLHL<div ng-controller="MainCtrl as main">
{{ main.title }}
<div ng-controller="AnotherCtrl as another">
Scope title: {{ another.title }}
Parent title: {{ main.title }}
<div ng-controller="YetAnotherCtrl as yet">
Scope title: {{ yet.title }}
Parent title: {{ another.title }}
Parent parent title: {{ main.title }}
</div>
</div>
</div>
Child controllers can now reference parent controllers by their namespace, rather than through some convoluted $parent reference N times.
(For The Less Humiliating Loss)
controllerAs
FTLHL
app.controller('MainCtrl', MainCtrl);
MainCtrl.$inject = [];
function MainCtrl() {
this.title = 'Some title';
}
We're also able to treat our controller as a constructor and attach our context to this
(For The Less Humiliating Loss)
app.controller('MainCtrl', MainCtrl);
MainCtrl.$inject = [];
function MainCtrl() {
return {
title: 'Some title'
};
}
We could additionally compel our controller to act like a factory -- the way the rest of Angular methods do -- by returning an object.
Controversy ahead!!! Not advised for those with low blood pressure, those who are pregnant or may become pregnant, and the elderly.
.when('/', {
templateUrl: 'components/main/main.html',
controllerAs: 'main',
controller: 'MainCtrl'
});
Better right? Our templates are more reusable, our controllers are more reusable, and we've separated our concerns much better.
(For The Draw)
Stop thinking of your application in terms of views that need to be loaded. That kind of thinking aligns better with imperative frameworks but doesn't work well in angular.
An Angular 2 application consists of nested components. So an Angular 2 application will always have a component tree.
- Victor Savkin: core Angular 2 developer
App developers are responsible for updating models.
Angular reflects model changes into UI via components
Services,
Filters,
Factories
Views,
Directives,
Controllers
component = directive + controller
app.directive('myComponent', myComponent);
function myComponent() {
return {
restrict: 'EA',
scope: true,
templateUrl: 'my-component.html',
controllerAs: 'comp',
controller: MyComponentCtrl
bindToController: true,
link: linkFn
};
}
view = [[component]]
.when('/', {
templateUrl: 'views/main.html'
})
.when('/', {
template: '<my-home></my-home>'
})
The idea here is to avoid logic in your controller.
The effect of this is that virtually all functions that you may be tempted to write in a controller, should instead by put into a service.
This also simplifies controller code and clarifies the role of a controller.
angular.module('my-app')
.controller('myCtrl', MyCtrl);
function MyCtrl() {
//cache our reference
var vm = this;
//This is cool
vm.firstName = 'Cory';
//This too is cool
vm.lastName = 'Brown';
//Oh noes! logic!!!
//Yes it's contrived. What do you want from me?
vm.fullName = function () {
return vm.firstName + ' ' + vm.lastName;
};
}
angular.module('my-app')
.factory('fullNameinator', fullNameinator);
function fullNameinator() {
return function (first, last) {
//Oh yes, this is where logic goes!
return first + last;
}
}
angular.module('my-app').controller('myCtrl', MyCtrl);
MyCtrl.$inject = ['firstNameinator'];
function MyCtrl(fn) {
//cache our reference
var vm = this;
//This is cool
vm.firstName = 'Cory';
//This too is cool
vm.lastName = 'Brown';
//Now I'm cool too!
vm.fullName = fn(vm.firstName, vm.lastName);
}
.service
methods invoke
new
when they are called.
Since it is invoked with
new
, properties are attached via
this
.
Despite being a constructor, only one instance object is created for any given service. That one instance object is returned each time the service is injected.
angular.module('my-app')
.service('my-service', MyService);
function MyService() {
//this.now will always be the same time, when it was first instansiated
this.now = new Date();
}
.factory
methods do not invoke new
when they are called.
Because of this, an object needs to be returned. from the factory.
angular.module('my-app')
.factory('my-service', MyService);
function MyService() {
return {
//This too will always be the same time.
now = new Date();
};
}