An intensive, practical introduction to AngularJS 1.x.
Slides are here
A polyfill is a piece of code that provides the technology that you expect the browser to provide natively. A shim that mimics a future API providing fallback functionality to older browsers
JSPM is (another) JavaScript Package Manager. It uses SystemJS, an universal module loader for JavaScript: it can load CommonJS modules, AMD and globals. It can also translate from ES6 to ES5, i.e. we don't need another tool for that.
-
start with a blank HTML file http://bit.ly/formation-angularjs
-
add some content
<h1>Hello Zaiste</h1> -
install JSPM
npm install jspm -
initialize the project
jspm init -
add JSPM initialization at the bottom
<script src="jspm_packages/system.js" charset="utf-8"></script> <script src="config.js" charset="utf-8"></script> <script type="text/javascript"> System.import("app/main"); </script> -
add
app/main.jswithconsole.log("Hello World") -
jspm install jquery -
import jQuery
import $ from 'jquery'; $('h1').css({'color': 'red'});
- install
fetchpolyfill - fetch current Paris weather from OpenWeatherMap API
Curly braces in AngularJS represent binding, they render expressions out:
{{ "This is foo" }}string values{{ 1 + 3 }}arithmetic expressions
Binding with object's property: AngularJS engine will watch for changes to each defined property.
ng-model directive creates a binding between an object and the view layer element (input, select, textarea, etc.). It is a representation of a view model i.e. an abstraction of the view exposing public properties and commands.
ng-bind directive can be used to render a property value from a binding instead of curly braces.
To bootstrap the framework we need AngularJS script and ng-app attribute on <html> tag.
-
start with JSPM stub
-
jspm install angular -
import AngularJS
import angular from 'angular'; -
bootstrap Angular with
ng-appdirective -
create some binding using
ng-modeldirective<input type="text" name="name" value="" ng-model="foo.name"> <input type="text" name="name" value="" ng-model="foo.class"> <div class="callout {{ foo.class }}"> <h1>Hello {{ foo.name }}</h1> </div>
- replace curly brackets bindings with
ng-binddirective, what is different ?
Controllers are used to set up initial state or to add behavior to modify the current state.
ng-controller="MainController as propertyName" binds methods and properties directly onto the controller using this. Controller's properties will be available in the view through propertyName.
-
start with JSPM
-
jspm install angular -
import AngularJS
import angular from 'angular'; -
define a controller in
app/main.jsclass MainController { constructor() { this.title = "This is my favorite title"; } } -
use
ng-controllerto add a controller to the view; useasto define an alias for it i.e. a more convenient name to use in the view<body ng-controller='MainController as mc'> -
define a module with
angular.moduleand attach defined controller to itangular.module('app', []) .controller('MainController', MainController);
angular.module defines where controllers will be stored; 1st parameter is the name of the module, 2nd parameter is a list of dependencies.
- make sure module name must match up with
ng-appvalue
<html ng-app='app'>
- add a method named
foo()to the controller which returns a string and display it usingng-bind
ngClick allows to specify a custom behavior when an element is clicked.
- start from [AngularJS 02 : Controllers]
- use
ng-clickto add a button with associated behavior
<button class="button" ng-click="mc.alertMe()">Alert me</button>
- add
alertMe()method toMainController
class MainController {
...
alertMe() {
alert("Hello World");
}
}
- use
ng-clickto change the current title
ngIf directive removes or re-creates an DOM element based on an expression.
ngShow and ngHide show and hide elements based on an expression using CSS and without modifying DOM
- add
ngIfdirective to the view
<button class="button" ng-click="foo.toggle = !foo.toggle">Show</button>
<div id="toggler" ng-if="foo.toggle">
Lorem ipsum sit arithmetic
</div>
- extract the expression into a method in
MainControllerwhich togglesdivvisibility - reimplement the example using
ngShoworngHide, check CSS attributes fordiv#togglerelement
ngRepeat directive allows to iterate over a collection.
ngRepeat provide special properties for each iterated element
$index:integercurrent element$first:booleantrueif current element is first element$middle:booleantrueif current element is last element$last:booleantrueif current element is last element$even:booleantrueif current element has even index$odd:booleantrueif current element has odd index
- initialize
taskscollection inMainController
class MainController {
constructor() {
...
this.tasks = [
"Do laundry",
"Buy socks",
"Buy milk",
"Laminate kitchen",
"Buy apples"
];
}
}
- add
ng-repeatdirective to iterate over a collection in the view
<ul ng-repeat="task in mc.tasks">
<li ng-bind="task"></li>
</ul>
- add CSS class to differentiate even and odd elements from a collection
- display first element in bold
- create a form for adding tasks and use
ng-clickto submit it
jsonserializes an object into a JSONuppercaselowercasetransforms string into uppercase or lowercasecurrencyformatsnumberas a currencydate:formatformats adateas a string based on given format
-
make the title uppercase
<div ng-bind="mc.title | uppercase"></div> -
add a field
pricewith value1234.50and a fieldtodaywith current date toMainControllerclass MainController { constructor() { ... this.price = 1234.527; this.today = Date.now(); } } -
format
pricefield as a currency<div ng-bind="price | currency"></div> -
format
todayfield as a date<div ng-bind="today | date:'fullDate'"></div>
- format
priceusing EUR - format
priceto 2 decimal digits - format
todayso it displays only day name
Dependency injection is a software design pattern that deals with how components get hold of their dependencies. The Angular injector subsystem is in charge of creating components, resolving their dependencies, and providing them to other components as requested.
$filter is a service provided by AngularJS used for formatting data that can be used in controllers through DI.
Injections in AngularJS happen through constructor.
-
inject
$filterintoMainControllerclass MainController { constructor($filter) { this.title = $filter('uppercase')("This is a simple title"); ... } }
- use
$filter'sfilterfilter to select a subset of tasks (named To Buy) to buy and display that list
-
add
filterfilter on the view and connect it withsearchmodel usingng-model<input ng-model="search.name" type="text" /> <ul ng-repeat="task in mc.tasks | filter:search"> <li ng-bind="task"></li> </ul>
- replace strings from
taskscollection with objects having two fieldsnameanddonei.e.Buy socksbecomes{ "name": "Buy socks", "done": "false"}
-
we change previous example to deal with JavaScript objects
<input ng-model="search.name" type="text" /> <input type="checkbox" ng-model="search.done"/> <ul ng-repeat="task in mc.tasks | filter:search "> <li> <input type="checkbox" ng-model="task.done" /> <span ng-bind="task.name"></span> </li> </ul>
- add an input to specify a limit of tasks to display, use its value for
limitTofilter
There are 3 major directory structures for AngularJS applications:
- Basic Structure files are directly placed in
appfolder - Standard Structure files are grouped by their purposes (separation of concerns). Once each concern grows big enough it will be more convenient to group files as modules/components.
- Modular Structure we distinguish
sharedandcomponentsdirectories. Each component is a independent set of files: its static views, directives, services etc. In that structure a component can be seen as a small MVC application.
- create
SecondControllerthat displaysI'm secondfrom its propertydescon adivinsidebody - create a module named
controllersinsidecontrollers/index.jsand include it inappmodule insidemain.js
- start from [AngularJS 02 : Controllers]
- define a filter in
app/filters.js
let reverse = () => {
return (text) => {
return text.split("").reverse().join("");
}
}
export { reverse };
- attach defined filter to the module
import { reverse } from './filters';
angular.module('app', [])
.filter('reverse', reverse);
-
create a filter name
startsWithKthat is compatible withngRepeatwhere we only want to show names from a collection starting withK -
use the following
this.friendscollectionthis.friends = [ { "name": "Andrew Kazinski", }, { "name": "Andrew Kazinski", }, { "name": "Andrew Kazinski", }, ]; -
use the
startsWithKfilter on the view
function BasicService() {
this.widget = 'Widget 1';
}
BasicService.prototype.makeWidget = function() {
return "Preparing " + this.name
}
service = new BasicService();
When you use new on a function, JavaScript will create a variable this and assign it an empty object. Then, it will augment this variable with defined variables and/or with methods you assigned to the prototype. Lastly, it returns this out of the function.
Use .service on a module to tell Angular that passed function is a constructor function.
-
start from [AngularJS 02 : Controllers](#AngularJS 02 : Controllers)
-
define a service in
app/main.jsexport default class WidgetService { constructor() { this.widget = "Widget 1"; } makeWidget() { return "Making widget: " + this.widget; } } -
attach defined service to the module
angular.module('app', []) .controller('MainController', MainController) .service('widget', WidgetService) -
inject
widgetservice insideMainControllerclass MainController constructor(basic) { this.message = basic.makeWidget(); }
- get your projects from Github using the following API:
https://api.github.com/users/{username}/repos
Task model
idstatusenum { open, closed }namecreated_atDateTime
Create an API using preferred programming language that implements a CRUD operations for Task model
GET /tasksget all tasksGET /tasks/:idget a single taskPOST /taskscreate a new taskPUT /tasks/:idupdate a single taskDELETE /tasks/:iddelete a single tasks
API should use JSON as its format.
Create a dedicated service called TaskService that wraps $http and allows to call CRUD action from defined API.
Create a form on the view and connect its submission with TaskService.
Module pattern involves returning an object (defining your public API) from the function.
function widgetFactory() {
return {
name: 'Widget 1',
makeWidget: function() {
return "Preparing... " + this.name;
}
}
}
To create an instance of an object using our factory, we just invoke the (factory) function.
widget = widgetFactory();
Use .factory on a module to tell Angular that passed function is a factory (module pattern).
We define a state for a controller using .config which allows to configure the
It is preferred to use one controller per application state instead of using it on an element.
ui.router needs to be added as a dependency.
application before it boots up.
$stateProvider on which we define our states.
Each state has a name and its configuration.
$stateProvider.state("main", {
url: "",
controller: "MainController",
templateUrl: "templates/main.html"
})
ui-view places a template based on given application state.
- Define a controller
- Define a template
- Display JSON from memory
- Define
GET /tasksfor fetching all taskshttp GET localhost:9292/tasks - Define
POST /tasksfor creating a taskhttp POST localhost:9292/tasks name="This is example" state=open - Install
angular-resourcejspm install angular-resource - Use
$resourceto fetch data from the API - Create
servicesmodule and add as a dependency tocontrollers
- By default there is
ngRoute ui-routeris a better alternative, it supports states and nested views- Install
ui-routerjspm install angular-ui-router - Add
ui-routeras a dependency - Define two routes using Angular module
.config:/and/tasksfor dashboard and task list respectively - Extract
indexintotasksandhometemplates and move them toviewdirectory
- Define view for a single task
- Define
getforTaskService - Define router's state
- Use
ui-sreffor state management