Angular Fundamentals: Difference between revisions

From Training Material
Jump to navigation Jump to search
 
(34 intermediate revisions by the same user not shown)
Line 14: Line 14:
{{Can I use your material}}
{{Can I use your material}}


== Introduction ==
== Introduction ==


* '''Angular''' is a completely new framework  
* '''Angular''' is a completely new framework  
Line 26: Line 26:
** Emphasize '''mobile-first''' development
** Emphasize '''mobile-first''' development


=== Intro Con't ===
=== Intro Con't ===


* Angular  
* Angular  
Line 38: Line 38:
*** Better support for tooling and automation
*** Better support for tooling and automation


=== Intro Con't 1 ===
=== Intro Con't 1 ===


To accomplish these goals
To accomplish these goals
Line 49: Line 49:
** Abillity to build '''large-scale''' applications
** Abillity to build '''large-scale''' applications


=== Intro Con't 2 ===
=== Intro Con't 2 ===


* '''Angular2''', along with '''Typescript'''
* '''Angular2''', along with '''Typescript'''
Line 57: Line 57:
** IDEs can provide better '''IntelliSense and code completion''' support
** IDEs can provide better '''IntelliSense and code completion''' support


=== Bootstrap Angular app ===
=== Bootstrap Angular app ===


* Loading the modules  
* Loading the modules  
* App initialization process (bootstrapping)
* App initialization process (bootstrapping)


==== Loading the modules  ====
==== Loading the modules  ====


'''ES2015''' introduced a new syntax for '''module loading'''
'''ES2015''' introduced a new syntax for '''module loading'''
Line 73: Line 73:
** Each of TypeScript files will be compiled to a SystemJS module
** Each of TypeScript files will be compiled to a SystemJS module
** SystemJS will then load all the '''related dependencies''' and when requested the module itself
** SystemJS will then load all the '''related dependencies''' and when requested the module itself
* [[Angular_Fundamentals#Example_1_.E2.8C.98|Example]]
* [[Angular_Fundamentals#Example_1|Example]]
<syntaxhighlight lang="html">
<syntaxhighlight lang="html">
<script src="https://unpkg.com/systemjs@0.19.27/dist/system.js"></script>
<script src="https://unpkg.com/systemjs@0.19.27/dist/system.js"></script>
Line 79: Line 79:
</syntaxhighlight>
</syntaxhighlight>


==== Loading the modules con't ====
==== Loading the modules con't ====


* Not limited to SystemJS module loader or pure TypeScript compiler
* Not limited to SystemJS module loader or pure TypeScript compiler
Line 174: Line 174:
-->
-->


==== Bootstrapping ====
==== Bootstrapping ====


Bootstrapping process:
Bootstrapping process:
Line 186: Line 186:
* Post compilation '''links''' the view and the component class  
* Post compilation '''links''' the view and the component class  
** changes are '''synced across the model''' and viewed in '''real time''' as we interact with the app
** changes are '''synced across the model''' and viewed in '''real time''' as we interact with the app
* [[Angular_Fundamentals#Example_1_.E2.8C.98|Example]]
* [[Angular_Fundamentals#Example_1|Example]]


=== Components in Angular ===
=== Components in Angular ===


* '''Component Pattern''' instead of '''MVC'''
* '''Component Pattern''' instead of '''MVC'''
Line 195: Line 195:
* Change detection
* Change detection


==== Component pattern ====
==== Component pattern ====


* In general, involves combining smaller, discrete building blocks into larger finished products
* In general, involves combining smaller, discrete building blocks into larger finished products
Line 205: Line 205:
*** As long as the '''interfaces''' are not changed
*** As long as the '''interfaces''' are not changed


==== Component pattern Con't ====
==== Component pattern Con't ====


In web applications  
In web applications  
Line 216: Line 216:
** Makes the application easier to understand at a higher level of abstraction
** Makes the application easier to understand at a higher level of abstraction


==== Constructs ====
==== Constructs ====


* Usually they live in the HTML template that is inside the ''@Component'' '''decorator'''
* Usually they live in the HTML template that is inside the ''@Component'' '''decorator'''
Line 226: Line 226:
</syntaxhighlight>
</syntaxhighlight>


==== Interpolation ====
==== Interpolation ====


* Replaces the content of the '''markup''' with the value of the expression ('''howManyTries''')
* Replaces the content of the '''markup''' with the value of the expression ('''howManyTries''')
Line 237: Line 237:
** '''Easier debugging''' when we need to see the '''state''' of the model
** '''Easier debugging''' when we need to see the '''state''' of the model
** No need to put a breakpoint in code for the value of a component property or method call
** No need to put a breakpoint in code for the value of a component property or method call
* [[Angular_Fundamentals#Example_1_.E2.8C.98|Example]]
* [[Angular_Fundamentals#Example_1|Example]]
<syntaxhighlight lang="html">
<syntaxhighlight lang="html">
<p class="text-info">No of tries :
<p class="text-info">No of tries :
Line 244: Line 244:
</syntaxhighlight>
</syntaxhighlight>


==== Expressions ====
==== Expressions ====


* Pieces of plain JavaScript code
* Pieces of plain JavaScript code
Line 253: Line 253:
* New meanings for operators '''|''' and '''?.'''
* New meanings for operators '''|''' and '''?.'''


==== Expressions Con't ====
==== Expressions Con't ====


Restrictions
Restrictions
Line 263: Line 263:
* No calling ''console.log''
* No calling ''console.log''


===== Safe navigation operator =====
===== Safe navigation operator =====


'''?.''' checks for null values in lengthy property paths
'''?.''' checks for null values in lengthy property paths
Line 274: Line 274:
** Loads the data when it is available
** Loads the data when it is available


==== Binding data ====
==== Binding data ====
[[File:BindingDataAng4a.png|320px|Binding data in Angular]]
[[File:BindingDataAng4a.png|320px|Binding data in Angular]]
* Property binding
* Property binding
* Event binding
* Event binding
* [[Angular_Fundamentals#Example_1_.E2.8C.98|Example]]
* [[Angular_Fundamentals#Example_1|Example]]


===== Property binding =====
===== Property binding =====


<syntaxhighlight inline lang="js">[ ]</syntaxhighlight>
<syntaxhighlight inline lang="js">[ ]</syntaxhighlight>
Line 290: Line 290:


<syntaxhighlight lang="html">
<syntaxhighlight lang="html">
<input type="string" [value]="findJewel" (input)="findJewel = $event.target.value" />
<input type="text" [value]="findJewel" (input)="findJewel = $event.target.value" />
</syntaxhighlight>
</syntaxhighlight>


===== Event binding =====
===== Event binding =====


<syntaxhighlight inline lang="js">( )</syntaxhighlight>
<syntaxhighlight inline lang="js">( )</syntaxhighlight>
Line 306: Line 306:
</syntaxhighlight>
</syntaxhighlight>


==== Change detection ====
==== Change detection ====


* State maintenance  
* State maintenance  
* Change detection
* Change detection


===== State maintenance =====
===== State maintenance =====


* Angular apps are ''dynamic''
* Angular apps are ''dynamic''
Line 319: Line 319:
** we can use these values directly in expressions and bindings in the template without having to write any plumbing code to wire them up
** we can use these values directly in expressions and bindings in the template without having to write any plumbing code to wire them up


===== Change detection =====
===== Change detection =====


Angular keeps '''track of changes''' in the component as it runs
Angular keeps '''track of changes''' in the component as it runs
Line 329: Line 329:
** Updates the element in the view that is bound to property with the new value of it
** Updates the element in the view that is bound to property with the new value of it


===== Change detection Con't =====
===== Change detection Con't =====


* Multi-step process where Angular
* Multi-step process where Angular
Line 342: Line 342:
** It vastly improves the '''performance''' of Angular
** It vastly improves the '''performance''' of Angular


=== Web components ===
=== Web components ===


Standards for web browsers
Standards for web browsers
Line 350: Line 350:
* HTML imports
* HTML imports


==== Custom elements ====
==== Custom elements ====


Enable '''new types of element''' to be created  
Enable '''new types of element''' to be created  
Line 358: Line 358:
** Makes it possible to become truly '''self-contained'''
** Makes it possible to become truly '''self-contained'''


==== Shadow DOM ====
==== Shadow DOM ====


Provides a '''hidden''' area on the page for scripts, CSS, and HTML  
Provides a '''hidden''' area on the page for scripts, CSS, and HTML  
Line 366: Line 366:
* Component can use it to '''render''' its display
* Component can use it to '''render''' its display


==== Templates ====
==== Templates ====


'''Repeatable''' chunks of HTML
'''Repeatable''' chunks of HTML
Line 374: Line 374:
* Can be used to make the HTML and CSS inside the Shadow DOM used by the component ''dynamic''
* Can be used to make the HTML and CSS inside the Shadow DOM used by the component ''dynamic''


==== HTML imports ====
==== HTML imports ====


Provide a way to '''load''' resources (HTML, CSS, JS) in a '''single bundle'''
Provide a way to '''load''' resources (HTML, CSS, JS) in a '''single bundle'''
Line 380: Line 380:
** It relies on JavaScript '''module loading''' instead
** It relies on JavaScript '''module loading''' instead


=== Module loading ===
=== Module loading ===


'''Module''' provides a way for JavaScript files to be '''encapsulated'''  
'''Module''' provides a way for JavaScript files to be '''encapsulated'''  
Line 391: Line 391:
** SystemJS, etc
** SystemJS, etc


=== Transpilation ===
=== Transpilation ===


Convertion from '''ES2015''' to '''ES5'''
Convertion from '''ES2015''' to '''ES5'''
Line 399: Line 399:
** Traceur, Babel, TypeScript
** Traceur, Babel, TypeScript


=== Angular modules ===
=== Angular modules ===


'''Organise''' components into complete apps
'''Organise''' components into complete apps
Line 411: Line 411:
* Every Angular app '''must have''' at least one ''root module''
* Every Angular app '''must have''' at least one ''root module''


==== Angular modules con't ====
==== Angular modules con't ====


An Angular module defines:
An Angular module defines:
Line 419: Line 419:
* '''Services''' that the module wants to make available application wide
* '''Services''' that the module wants to make available application wide


=== Angular CLI, etc ===
=== Angular CLI, etc ===


* Tools
* Tools
* Resources
* Resources


==== Tools ====
==== Tools ====


* Browser's '''developer console''' - a lot of errors with code can be detected just by looking at it
* Browser's '''developer console''' - a lot of errors with code can be detected just by looking at it
** put in breakpoints, add a watch, etc
** put in breakpoints, add a watch, etc
* '''Augury''' (<small>https://augury.angular.io/</small>)
* '''Angular Devtools''' (<small>https://angular.io/devtools</small>)
** Helps visualise the application through component trees, and visual debugging tools
** Helps visualise the application through component trees, and visual debugging tools
** Gives insight into application structure, change detection and performance characteristics
** Gives insight into application structure, change detection and performance characteristics
** New feature, similar to Augury
* ''Augury'' (<small>https://augury.angular.io/</small>)
** Depricated
** Depricated
* Angular Devtools (<small>https://angular.io/devtools</small>)
** New feature, similar to Augury
* '''IDE extensions''': Visual Studio Code, JetBrains WebStorm, Sublime Text, Atom, etc
* '''IDE extensions''': Visual Studio Code, JetBrains WebStorm, Sublime Text, Atom, etc
* '''JSFiddle''' and '''Plunker'''
* '''JSFiddle''' and '''Plunker'''
Line 441: Line 441:
** <small>https://cli.angular.io/</small>
** <small>https://cli.angular.io/</small>


==== Resources ====
==== Resources ====


* Angular source code, documentation and blog
* Angular source code, documentation and blog
Line 452: Line 452:
** <small>https://groups.google.com/forum/#!forum/angular</small>
** <small>https://groups.google.com/forum/#!forum/angular</small>
* Built with Angular - <small>https://www.madewithangular.com/</small>
* Built with Angular - <small>https://www.madewithangular.com/</small>
* Angular new website (still Beta atm) - <small>https://angular.dev/</small>
** More here - <small>https://blog.angular.io/announcing-angular-dev-1e1205fa3039</small>
* Full-stack - <small>https://analogjs.org/docs</small>


== Introduction to TypeScript ==
== Introduction to TypeScript ==


Created by Microsoft as a superset of JavaScript
Created by Microsoft as a superset of JavaScript
Line 462: Line 465:
* For more, reference [[Typescript]]
* For more, reference [[Typescript]]


=== Types, Functions, Lambdas ===
=== Types, Functions, Lambdas ===


Types
Types
Line 470: Line 473:
** to make sure that the proper types are being used in the application
** to make sure that the proper types are being used in the application


=== Classes, Interfaces, Decorators, Modules ===
=== Classes, Interfaces, Decorators, Modules ===


'''Decorators''' are simple ''annotations''
'''Decorators''' are simple ''annotations''
Line 482: Line 485:
*** Attaching an HTML '''view''' to the component
*** Attaching an HTML '''view''' to the component


== Component Based Development ==
== Component Based Development ==


To develop applications in Angular
To develop applications in Angular
Line 489: Line 492:
# '''Bootstrap''' the application
# '''Bootstrap''' the application


=== Develop custom components ===
=== Develop custom components ===


Building user interface (UI)
Building user interface (UI)
Line 502: Line 505:
**** Provide the appropriate '''properties''' and '''methods''' needed to support the behavior
**** Provide the appropriate '''properties''' and '''methods''' needed to support the behavior


==== Example 1 ====
==== Example 1 ====


<pre>
<pre>
Line 583: Line 586:
       <p class="well lead">Find which Jewel computer likes more than the others.</p>
       <p class="well lead">Find which Jewel computer likes more than the others.</p>
       <label>Your Try: </label>
       <label>Your Try: </label>
       <input type="string" [value]="findJewel" (input)="findJewel = $event.target.value" />
       <input type="text" [value]="findJewel" (input)="findJewel = $event.target.value" />
       <button (click)="verifyTheTry()" class="btn btn-primary btn-sm">Verify</button>
       <button (click)="verifyTheTry()" class="btn btn-primary btn-sm">Verify</button>
       <button (click)="initGame()" class="btn btn-warning btn-sm">Restart</button>
       <button (click)="initGame()" class="btn btn-warning btn-sm">Restart</button>
Line 721: Line 724:
</syntaxhighlight>
</syntaxhighlight>


=== Component Tree ===
=== Component Tree ===


Example
Example
Line 727: Line 730:
** <small>popup-window</small>
** <small>popup-window</small>


==== Exercise ====
==== Exercise ====


<pre>
<pre>
Line 735: Line 738:
</pre>
</pre>


=== Advanced Components ===
=== Advanced Components ===


Examples
Examples
Line 753: Line 756:
-->
-->


== Directives and Pipes ==
== Directives and Pipes ==


Angular '''directives'''
Angular '''directives'''
Line 763: Line 766:
* Directives (custom or platform), like components, have to be registered on a module before they can be used
* Directives (custom or platform), like components, have to be registered on a module before they can be used


=== Attributes directives and Structural directives ===
=== Attributes directives and Structural directives ===


* Do not have their own view
* Do not have their own view
Line 777: Line 780:
*** Syntax <syntaxhighlight lang="html5" inline>[style.style-name]="expression"</syntaxhighlight>
*** Syntax <syntaxhighlight lang="html5" inline>[style.style-name]="expression"</syntaxhighlight>


==== Attributes directives ====
==== Attributes directives ====


* Enhance the '''appearance''' and/or '''behavior''' of existing elements/components
* Enhance the '''appearance''' and/or '''behavior''' of existing elements/components
Line 790: Line 793:
* '''ngClass'''<syntaxhighlight inline lang="html5"><div [ngClass]="{'required':inputRequired, 'email':whenEmail}"></div></syntaxhighlight>
* '''ngClass'''<syntaxhighlight inline lang="html5"><div [ngClass]="{'required':inputRequired, 'email':whenEmail}"></div></syntaxhighlight>


===== Exercises =====
===== Exercises =====


<pre>
<pre>
Line 797: Line 800:
3. Custom directive. Use highlight directive example from 'ngtraining' - for ftj main title ("Find The Jewel").
3. Custom directive. Use highlight directive example from 'ngtraining' - for ftj main title ("Find The Jewel").
</pre>
</pre>
<!--
  - template file
<div [style.font-size]="mySize" >
<div [attr.style]="'font-size:48px'" >
<div [ngStyle]="{'font-size': '48px'}"
<div class="{{ myCustomClass }}">
  <div [ngClass]="myCustomClass">
  - component file
  myCustomClass={'np-style':'np-style'}
-->


==== Structural directives ====
==== Structural directives ====


* Looks similar to data binding
* Looks similar to data binding
Line 813: Line 826:
</syntaxhighlight>
</syntaxhighlight>


===== Exercises =====
===== Exercises =====
<pre>
<pre>
1. Show all jewels in a nice lavender bootstrap grid, as the last element in the "no body, no crime"  (=
1. Show all jewels in a nice lavender bootstrap grid, as the last element in the "no body, no crime"  (=
Line 820: Line 833:
</pre>
</pre>


=== Pipes ===
=== Pipes ===


* Provide a mechanism to '''format view content'''
* Provide a mechanism to '''format view content'''
Line 835: Line 848:
</syntaxhighlight>
</syntaxhighlight>


==== Exercise ====
==== Exercise ====


<pre>
<pre>
Line 843: Line 856:
</pre>
</pre>


== Building blocks ==
== Building blocks ==


An Angular application consists of the following parts:
An Angular application consists of the following parts:
Line 855: Line 868:
* Dependency injection
* Dependency injection


=== Immutable.js ===
=== Immutable.js ===


* Immutable objects/collections are '''objects that cannot be changed once created'''
* Immutable objects/collections are '''objects that cannot be changed once created'''
Line 864: Line 877:
** enabling advanced memoization and change detection techniques with simple logic
** enabling advanced memoization and change detection techniques with simple logic


=== Observables ===
=== Observables ===


* Core '''Reactive Extensions''' features
* Core '''Reactive Extensions''' features
Line 884: Line 897:
<!-- TODO: prepare exercises -->
<!-- TODO: prepare exercises -->


=== Dependency Injection ===
=== Dependency Injection ===


Allows components and other building blocks
Allows components and other building blocks
Line 904: Line 917:
<!-- # Extract the hinting feature into the angular service creature (= -->
<!-- # Extract the hinting feature into the angular service creature (= -->


== Routing ==
== Routing ==


Routing uses the browser’s URL to manage the content displayed to the user
Routing uses the browser’s URL to manage the content displayed to the user
Line 915: Line 928:
*** Select a component whose template is displayed as the content of an HTML element called '''router-outlet'''
*** Select a component whose template is displayed as the content of an HTML element called '''router-outlet'''


=== Routes in Angular ===
=== Routes in Angular ===


* Navigation
* Navigation
Line 940: Line 953:
# Improve gems module with lazy loading
# Improve gems module with lazy loading


== Forms ==
== Forms ==


Angular provides set of constructs that make standard form-based operations easier
Angular provides set of constructs that make standard form-based operations easier
Line 949: Line 962:
* Submitting the data to the back-end server
* Submitting the data to the back-end server


=== Template Driven Forms ===
=== Template Driven Forms ===


'''Template-driven''' forms develops a form within an HTML template
'''Template-driven''' forms develops a form within an HTML template
Line 962: Line 975:
*** examples - a required field not provided, e-mail not in the right format, etc
*** examples - a required field not provided, e-mail not in the right format, etc
* Exercise
* Exercise
** Make your '''ftj''' form ''template-driven'' and validate it (required)
** Make your '''ftj''' engine component's form ''template-driven'' and validate it (required)


=== Form Builder ===
=== Form Builder ===


'''Model-driven''' forms
'''Model-driven''' forms
Line 979: Line 992:
Examples
Examples
* ngtraining
* ngtraining
* built-in validators
** <small>https://angular.io/api/forms/Validators</small>
<!--
<!--
* trainer app
* trainer app
Line 1,051: Line 1,066:
-->
-->


== REST and State Management ==
== REST and State Management ==


=== HTTP API ===
=== HTTP API ===


Asynchronous HTTP requests
Asynchronous HTTP requests
Line 1,070: Line 1,085:
** Rewrite 'ftj', connect with backend 'jewel-db'  
** Rewrite 'ftj', connect with backend 'jewel-db'  
<!-- TODO: provide some theory, examples and prepare the exercises
<!-- TODO: provide some theory, examples and prepare the exercises
=== RxJS ===
=== RxJS ===
-->
-->
=== Redux and Ngrx ===
=== Redux and Ngrx ===
* '''NgRx''' - framework for building reactive applications in Angular
* '''NgRx''' - framework for building reactive applications in Angular
** inspired by the Redux pattern
** inspired by the Redux pattern
Line 1,085: Line 1,100:


<!--
<!--
=== Ngrx Con't ===
=== Ngrx Con't ===
[[File:AnguNgrx.png|600px]]
[[File:AnguNgrx.png|600px]]


=== Ngrx elements ===
=== Ngrx elements ===
* '''Store''' - Sort-of client-side '''database'''
* '''Store''' - Sort-of client-side '''database'''
** application's single source of truth, reflects the '''current state''' of the app
** application's single source of truth, reflects the '''current state''' of the app
Line 1,103: Line 1,118:
<!-- Pure functions are functions that are predictable and have no side effects. Given the same set of inputs, a pure function will always return the same set of outputs. -->
<!-- Pure functions are functions that are predictable and have no side effects. Given the same set of inputs, a pure function will always return the same set of outputs. -->


== Migrating from Angular 1.x to Angular ==
== Migrating from Angular 1.x to Angular ==
* Using Angular guide:
* Using Angular guide:
** <small>https://angular.io/guide/upgrade </small>
** <small>https://angular.io/guide/upgrade </small>


=== Migration Steps ===
=== Migration Steps ===
=== Choosing a path ===
=== Choosing a path ===
=== ng-forward and ng-upgrade ===
=== ng-forward and ng-upgrade ===


== Migrating from Angular 2.x to Angular x.x ==
== Migrating from Angular 2.x to Angular x.x ==
* Using angular guide
* Using angular guide
** https://update.angular.io/
** https://update.angular.io/
Line 1,122: Line 1,137:
** repeat the steps
** repeat the steps


== More examples ==
== More examples ==
Angular docs
Angular docs
* <small>https://testing-angular.com/example-applications/</small>
* <small>https://testing-angular.com/example-applications/</small>
Line 1,129: Line 1,144:
* <small>https://angular.io/guide/cheatsheet</small>
* <small>https://angular.io/guide/cheatsheet</small>


== News ==
== News ==
What's new in Angular ...
What's new in Angular ...
* 16
* 16
* 17
* 17
<!--
* 18
* 18
-->
* ...next
* ...next


=== News Con't - 16 ===
=== News Con't - 16 ===
'''16''' - <small>https://blog.angular.io/angular-v16-is-here-4d7a28ec680d</small>
'''16''' - <small>https://blog.angular.io/angular-v16-is-here-4d7a28ec680d</small>
* '''Signals'''
* '''Signals'''
** <small>https://angular.io/api/core/signal</small>
** <small>https://angular.io/guide/signals</small>
** <small>https://blog.bitsrc.io/angular-16-is-out-now-learn-how-to-replace-rxjs-with-signals-c1f6f410809</small>
** <small>https://blog.bitsrc.io/angular-16-is-out-now-learn-how-to-replace-rxjs-with-signals-c1f6f410809</small>
** <small>https://blog.stackademic.com/angular-16-signals-99abd8c5cadd</small>
** <small>https://blog.stackademic.com/angular-16-signals-99abd8c5cadd</small>
Line 1,145: Line 1,162:
** <small>https://angular.io/guide/hydration</small>
** <small>https://angular.io/guide/hydration</small>
* '''Required Inputs''' and Router Inputs
* '''Required Inputs''' and Router Inputs
** <syntaxhighlight lang="ts" inline>@Input({ required: true }) title: string = '';</syntaxhighlight>
* TS 5.0 - '''decorators''' are no longer experimental
* TS 5.0 - '''decorators''' are no longer experimental
* '''Esbuild'''-based build system
* '''Esbuild'''-based build system
** <small>https://v16.angular.io/guide/esbuild</small>
* '''Standalone''' project support
* '''Standalone''' project support
** <small>https://angular.io/guide/standalone-components</small>
** <small>https://angular.io/guide/standalone-components</small>
Line 1,152: Line 1,171:
** <small>https://levelup.gitconnected.com/feature-flags-in-angular-16-472254256b0c</small>
** <small>https://levelup.gitconnected.com/feature-flags-in-angular-16-472254256b0c</small>


=== Router inputs ===
<syntaxhighlight lang="ts">
// To enable it:
//// use 'withComponentInputBinding' as part of the 'provideRouter'
const routes = [
  {
    path: 'about',
    loadComponent: import('./about'),
    resolve: { contact: () => getContact() }
  }
];
@Component(...)
export class About {
  // The value of "contact" is passed to the contact input
  @Input() contact?: string;
}
</syntaxhighlight>
=== Ex with 16 ===
'''Exercises:'''
'''Exercises:'''
# Make our ''jewels-list'' component working as a '''standalone''' one
# Make our ''jewels-list'' component working as a '''standalone''' one
# Create new app - '''bootstrapping''' one component only
# Create new app - '''bootstrapping''' one component only
# Migrate the '''whole''' ''ftj'' into standalone mode
# Add '''ssr''' to ''ftj'' app and enable '''hydration'''
# Add '''ssr''' to ''ftj'' app and enable '''hydration'''
# Turn on '''esbuild''' in our ''ftj''
<!-- TODO
# Signals exercise
-->
=== News Con't - 17 ===
'''17''' - <small>https://blog.angular.dev/introducing-angular-v17-4d7033312e4b</small>
* Built-in '''control flow'''
** new '''block''' template syntax - ''@if, @switch, @for''
** ng generate @angular/core:control-flow
* '''Deferrable''' views - better lazy loading
** ''@defer, @loading, @error, @placeholder, @defer''
** '''triggers''' - ''idle, on immediate, on timer, on viewport, on interaction, on hover, when''
** more here - <small>https://v17.angular.io/guide/defer</small>
* Improvements to server-side rendering (SSR) and static-site generation (SSG or prerendering)
** new lifecycle hooks - ''afterRender, afterNextRender''
* Vite and esbuild - are default now for new projects
* Dependency injection debugging in DevTools
** <small>https://v17.angular.io/guide/devtools</small>
* Standalone APIs - by default in ''ng generate''
** ng generate @angular/core:standalone
* New docs - <small>angular.dev</small>


== Testing JS ==
== Testing JS ==
[[File:TestingJS.jpeg|480px]]
[[File:TestingJS.jpeg|480px]]


Line 1,164: Line 1,225:
Hard to believe, but true (-8
Hard to believe, but true (-8


== Testing tools and frameworks ==
== Testing tools and frameworks ==


* '''Karma''' - the '''test runner''' for JavaScript
* '''Karma''' - the '''test runner''' for JavaScript
Line 1,174: Line 1,235:
* '''PhantomJS''' - the '''headless''' webkit browser
* '''PhantomJS''' - the '''headless''' webkit browser


== Test-Driven Development ==
== Test-Driven Development ==
[[File:TddJS.jpg|480px]]
[[File:TddJS.jpg|480px]]


== Test-Driven Development Con't ==
== Test-Driven Development Con't ==
* An '''approach''' to development
* An '''approach''' to development
** We write a '''test before''' we write just enough '''production''' code to fulfill that test and its '''refactoring'''
** We write a '''test before''' we write just enough '''production''' code to fulfill that test and its '''refactoring'''
Line 1,200: Line 1,261:
An expectation acts as a contract and can be used to see how a method should or can be used. This makes the code readable and easier to understand. -->
An expectation acts as a contract and can be used to see how a method should or can be used. This makes the code readable and easier to understand. -->


=== Practical TDD with JavaScript ===
=== Practical TDD with JavaScript ===
TDD life cycle  
TDD life cycle  
* '''Test''' first
* '''Test''' first
Line 1,209: Line 1,270:
** '''Improve''' the code '''without''' changing its '''behavior'''
** '''Improve''' the code '''without''' changing its '''behavior'''


== Unit VS E2E Testing ==
== Unit VS E2E Testing ==
[[File:UnitVSe2eJS.png|480px]]
[[File:UnitVSe2eJS.png|480px]]


== Unit Testing ==
== Unit Testing ==
* Jasmine and Karma
* Jasmine and Karma
* Unit Testing in Angular
* Unit Testing in Angular
Line 1,221: Line 1,282:
* Testing '''Router'''
* Testing '''Router'''


=== Unit Testing Con't ===
=== Unit Testing Con't ===


* Jasmine test '''framework'''
* Jasmine test '''framework'''
* Karma test '''runner'''
* Karma test '''runner'''


=== Jasmine ===
=== Jasmine ===
[[File:JasmineJS.jpg|480px]]
[[File:JasmineJS.jpg|480px]]


Line 1,235: Line 1,296:
Well, the choice is yours (-;
Well, the choice is yours (-;


=== Jasmine Con't ===
=== Jasmine Con't ===
* '''Fast''' - core has no external dependencies
* '''Fast''' - core has no external dependencies
* Full out of the box '''set of everything''' we need to test our code
* Full out of the box '''set of everything''' we need to test our code
Line 1,241: Line 1,302:
* <small>https://jasmine.github.io/</small>
* <small>https://jasmine.github.io/</small>


=== Example ===
=== Example ===
{| class="wikitable"
{| class="wikitable"
|-
|-
Line 1,269: Line 1,330:
** '''Reports''' to Jasmine if the expectation is true or false - then the '''spec''' will be '''passed or failed'''
** '''Reports''' to Jasmine if the expectation is true or false - then the '''spec''' will be '''passed or failed'''


=== Jasmine Built-in Matchers ===
=== Jasmine Built-in Matchers ===
<syntaxhighlight lang="javascript">
<syntaxhighlight lang="javascript">
expect(array).toContain(member);
expect(array).toContain(member);
Line 1,292: Line 1,353:
* https://jasmine.github.io/tutorials/your_first_suite#section-Matchers
* https://jasmine.github.io/tutorials/your_first_suite#section-Matchers


=== Jasmine Custom Matchers ===
=== Jasmine Custom Matchers ===
* Example
* Example
** testing_f\src\testing\jasmine-matchers.ts
** testing_f\src\testing\jasmine-matchers.ts


=== A few useful global functions ===
=== A few useful global functions ===
To avoid any '''duplicated''' setup and to '''teardown''' code
To avoid any '''duplicated''' setup and to '''teardown''' code
* '''beforeAll(), afterAll()'''
* '''beforeAll(), afterAll()'''
Line 1,303: Line 1,364:
** Called '''before/after each test''' specification, '''it()''' function, has been run.
** Called '''before/after each test''' specification, '''it()''' function, has been run.


==== Example ====
==== Example ====
<syntaxhighlight lang="javascript" highlight="5,9">
<syntaxhighlight lang="javascript" highlight="5,9">
describe('Hello world', () => {
describe('Hello world', () => {
Line 1,324: Line 1,385:
</syntaxhighlight>
</syntaxhighlight>


==== Exercise ====
==== Exercise ====
* testing-angular/just-jasmine ('''live coding''')
* testing-angular/just-jasmine ('''live coding''')


=== Karma ===
=== Karma ===
[[File:KarmaJS.jpg|480px]]
[[File:KarmaJS.jpg|480px]]


Line 1,334: Line 1,395:
Let's feed our NG App properly! (-8
Let's feed our NG App properly! (-8


=== Karma Con't ===
=== Karma Con't ===
* '''Manually''' running Jasmine tests by '''refreshing''' a browser tab repeatedly in different browsers '''every-time''' we edit some code can become '''tiresome'''.
* '''Manually''' running Jasmine tests by '''refreshing''' a browser tab repeatedly in different browsers '''every-time''' we edit some code can become '''tiresome'''.
* Karma is a tool which lets us '''spawn browsers''' and run jasmine tests inside of them all from the '''command line'''. The '''results''' of the tests are also displayed on the command line.
* Karma is a tool which lets us '''spawn browsers''' and run jasmine tests inside of them all from the '''command line'''. The '''results''' of the tests are also displayed on the command line.
Line 1,343: Line 1,404:
* <small>https://karma-runner.github.io/latest/index.html</small>
* <small>https://karma-runner.github.io/latest/index.html</small>


=== x and f ===
=== x and f ===
* x: disables tests
* x: disables tests
* f: focused tests
* f: focused tests
Line 1,363: Line 1,424:
</syntaxhighlight>
</syntaxhighlight>


=== Unit Testing in Angular ===
=== Unit Testing in Angular ===
* Angular '''components''' and '''directives''' require special support for testing
* Angular '''components''' and '''directives''' require special support for testing
** their interactions with other parts of the application infrastructure can be '''isolated''' and '''inspected'''
** their interactions with other parts of the application infrastructure can be '''isolated''' and '''inspected'''
Line 1,375: Line 1,436:
* Let's do it together by following the steps below
* Let's do it together by following the steps below


=== Test it first ===
=== Test it first ===
* Add new file - ''ftjt/src/app/selling-jewels/selling-jewels.component.spec.ts''
* Add new file - ''ftjt/src/app/selling-jewels/selling-jewels.component.spec.ts''
* Start with the base Jasmine template format
* Start with the base Jasmine template format
Line 1,389: Line 1,450:
</syntaxhighlight>
</syntaxhighlight>


==== Assemble Part 1 ====
==== Assemble Part 1 ====
* '''initialize''' the component, '''execute''' the contractor of the class, '''setup''' Angular testing '''APIs'''
* '''initialize''' the component, '''execute''' the contractor of the class, '''setup''' Angular testing '''APIs'''
<syntaxhighlight lang="javascript">
<syntaxhighlight lang="javascript">
Line 1,405: Line 1,466:
</syntaxhighlight>
</syntaxhighlight>


==== Assemble Part 2 ====
==== Assemble Part 2 ====
* '''define''' the component object, '''wrap''' it into fixture - Angular's '''utility'''
* '''define''' the component object, '''wrap''' it into fixture - Angular's '''utility'''
<syntaxhighlight lang="javascript">
<syntaxhighlight lang="javascript">
Line 1,419: Line 1,480:
</syntaxhighlight>
</syntaxhighlight>


==== Act Part ====
==== Act Part ====
* Nothing to do here yet
* Nothing to do here yet
* We will see that part later with bigger example
* We will see that part later with bigger example


==== Assert Part ====
==== Assert Part ====
<syntaxhighlight lang="javascript">
<syntaxhighlight lang="javascript">
it('Should define a title', () => {  
it('Should define a title', () => {  
Line 1,434: Line 1,495:
</syntaxhighlight>
</syntaxhighlight>


=== Make it run ===
=== Make it run ===
* Run command '''ng test''' (keep it running in the separate terminal, '''rerun''' it after adding new c, s, etc)
* Run command '''ng test''' (keep it running in the separate terminal, '''rerun''' it after adding new c, s, etc)
* Add the title property - <syntaxhighlight lang="javascript" inline>title: string;</syntaxhighlight>
* Add the title property - <syntaxhighlight lang="javascript" inline>title: string;</syntaxhighlight>
Line 1,443: Line 1,504:
* Both tests passed (-:
* Both tests passed (-:


=== Make it better ===
=== Make it better ===
* Interpolate the '''title''' in component's '''template'''
* Interpolate the '''title''' in component's '''template'''
* We'll use the same module and controller, so the same test file
* We'll use the same module and controller, so the same test file
Line 1,479: Line 1,540:
</syntaxhighlight>
</syntaxhighlight>


==== Make it run ====
==== Make it run ====
* Tests failed
* Tests failed
* Interpolate the title property in the template - <syntaxhighlight lang="javascript" inline>template: `<p>{{title}}</p>`,</syntaxhighlight>
* Interpolate the title property in the template - <syntaxhighlight lang="javascript" inline>template: `<p>{{title}}</p>`,</syntaxhighlight>
* Test passed (-:
* Test passed (-:


=== Testing Components ===
=== Testing Components ===
* Component combines an '''HTML template''' and a '''TypeScript class'''
* Component combines an '''HTML template''' and a '''TypeScript class'''
* We test that they work together as intended, by
* We test that they work together as intended, by
Line 1,492: Line 1,553:
* '''TestBed''' (with DOM)
* '''TestBed''' (with DOM)


==== Nested Components ====
==== Nested Components ====
* Stubbing
* Stubbing
* NO_ERRORS_SCHEMA
* NO_ERRORS_SCHEMA
Line 1,503: Line 1,564:
** Fix ftjt's main '''app''' component test
** Fix ftjt's main '''app''' component test


==== Class alone ====
==== Class alone ====
* Examples
* Examples
** app/demo/demo.ts (LightswitchComp)
** app/demo/demo.ts (LightswitchComp)
Line 1,516: Line 1,577:
*** It should show text 'Game Description' by default and when clicked - 'Find which Jewel computer likes more than the others.'
*** It should show text 'Game Description' by default and when clicked - 'Find which Jewel computer likes more than the others.'


==== With DOM ====
==== With DOM ====
* '''TestBed.configureTestingModule()''' - to setup the TestBed
* '''TestBed.configureTestingModule()''' - to setup the TestBed
* '''createComponent()''' - creates an instance, adds a corresponding element to the test-runner DOM, and returns a ComponentFixture
* '''createComponent()''' - creates an instance, adds a corresponding element to the test-runner DOM, and returns a ComponentFixture
Line 1,525: Line 1,586:
* '''By.css()''' - run on a different platform
* '''By.css()''' - run on a different platform


==== With DOM con't ====
==== With DOM con't ====
* Examples
* Examples
** app/banner/banner.component.ts
** app/banner/banner.component.ts
Line 1,536: Line 1,597:
<!-- banner-external.component.spec.ts -->
<!-- banner-external.component.spec.ts -->


==== Testing binding ====
==== Testing binding ====
* ''fixture.nativeElement'''''.querySelector()''' - querying for html
* ''fixture.nativeElement'''''.querySelector()''' - querying for html
* ''createComponent()'' with ''fixture'''''.detectChanges()''' - '''manual''' change detection
* ''createComponent()'' with ''fixture'''''.detectChanges()''' - '''manual''' change detection
Line 1,542: Line 1,603:
* '''dispatchEvent()''' - changing an '''input''' value
* '''dispatchEvent()''' - changing an '''input''' value


==== Testing binding Con't ====
==== Testing binding Con't ====
* Examples
* Examples
** app/banner/banner.component.spec.ts
** app/banner/banner.component.spec.ts
Line 1,551: Line 1,612:
** Improve it with <syntaxhighlight lang="html" inline> <p id="providedJewel">Your gues is {{findJewel}}</p> </syntaxhighlight>
** Improve it with <syntaxhighlight lang="html" inline> <p id="providedJewel">Your gues is {{findJewel}}</p> </syntaxhighlight>


==== External file, dependencies ====
==== External file, dependencies ====
* Service test '''doubles''' - ''stubs, fakes, spies, mocks''
* Service test '''doubles''' - ''stubs, fakes, spies, mocks''
* '''injector.get(service_name)''' - from the injector of the '''component-under-test'''
* '''injector.get(service_name)''' - from the injector of the '''component-under-test'''
Line 1,557: Line 1,618:
* '''Async''' service
* '''Async''' service


==== External file, dependencies Con't ====
==== External file, dependencies Con't ====
* Examples
* Examples
** app/banner/banner-external.component.spec.ts (vs banner.component.spec.ts)
** app/banner/banner-external.component.spec.ts (vs banner.component.spec.ts)
Line 1,569: Line 1,630:
*** mock, stub, test-double, injected
*** mock, stub, test-double, injected


==== Testing a list of items ====
==== Testing a list of items ====
* Examples
* Examples
** app/hero/hero-list.component.spec.ts
** app/hero/hero-list.component.spec.ts
Line 1,582: Line 1,643:
-->
-->


==== Input, Output ====
==== Input, Output ====
* Appears inside the '''view''' template of a '''host''' component
* Appears inside the '''view''' template of a '''host''' component
** host uses a '''property binding''' to set the '''input''' property
** host uses a '''property binding''' to set the '''input''' property
Line 1,593: Line 1,654:
** '''test-host''' - disables distractions (*ngFor, components, layout HTML, other bindings, injected services, etc)
** '''test-host''' - disables distractions (*ngFor, components, layout HTML, other bindings, injected services, etc)


==== Input, Output Con't ====
==== Input, Output Con't ====
* Examples
* Examples
** app/dashboard/dashboard-hero.component.spec.ts
** app/dashboard/dashboard-hero.component.spec.ts
Line 1,619: Line 1,680:
-->
-->


==== Overriding providers ====
==== Overriding providers ====
* '''Component''' is created with its '''own''' injector - '''child''' of the '''fixture''' injector
* '''Component''' is created with its '''own''' injector - '''child''' of the '''fixture''' injector
* Component's '''own providers'''
* Component's '''own providers'''
Line 1,628: Line 1,689:
* Other angular classes are supported too - ''overrideDirective, overrideModule, overridePipe''
* Other angular classes are supported too - ''overrideDirective, overrideModule, overridePipe''


==== Overriding providers Con't ====
==== Overriding providers Con't ====
* Example
* Example
** app/hero/hero-detail.component.spec.ts
** app/hero/hero-detail.component.spec.ts
Line 1,634: Line 1,695:
** Move validators in '''pesel''' component to it's own custom service
** Move validators in '''pesel''' component to it's own custom service


=== Testing Services ===
=== Testing Services ===
* The '''easiest''' Angular construct to unit test
* The '''easiest''' Angular construct to unit test
* With '''dependencies'''
* With '''dependencies'''
Line 1,649: Line 1,710:
** with '''HttpClientTestingModule'''
** with '''HttpClientTestingModule'''


=== Testing Services Con't ===
=== Testing Services Con't ===
* Examples
* Examples
** app/demo/demo.spec.ts
** app/demo/demo.spec.ts
Line 1,671: Line 1,732:
-->
-->


=== Testing Attribute Directives ===
=== Testing Attribute Directives ===
* '''Attribute Directive'''
* '''Attribute Directive'''
** Modifies the '''behavior''' of an element, component or another directive
** Modifies the '''behavior''' of an element, component or another directive
Line 1,683: Line 1,744:
** ''DebugElement''.'''properties''' - access to the artificial '''custom property''' that is set by the directive
** ''DebugElement''.'''properties''' - access to the artificial '''custom property''' that is set by the directive


=== Testing Attribute Directives Con't ===
=== Testing Attribute Directives Con't ===
* Examples
* Examples
** app\shared\highlight.directive.spec.ts
** app\shared\highlight.directive.spec.ts
Line 1,690: Line 1,751:
<!--
<!--
TODO
TODO
=== Testing Structural Directives ===
=== Testing Structural Directives ===
-->
-->


=== Testing Pipes ===
=== Testing Pipes ===


* '''No need''' to use Angular '''utilities'''  
* '''No need''' to use Angular '''utilities'''  
Line 1,700: Line 1,761:
** Can't tell if it's working properly as applied in the application components
** Can't tell if it's working properly as applied in the application components


=== Testing Pipes Con't ===
=== Testing Pipes Con't ===
* Examples
* Examples
** app/shared/title-case.pipe.spec.ts
** app/shared/title-case.pipe.spec.ts
Line 1,713: Line 1,774:
** Test ''jewels-list-detail'' component with ''jewel'' pipe
** Test ''jewels-list-detail'' component with ''jewel'' pipe


=== Testing Router ===
=== Testing Router ===
* Routed Components
* Routed Components
<!-- TODO
<!-- TODO
Line 1,720: Line 1,781:
* ''RouterTestingModule''
* ''RouterTestingModule''


=== Testing Router Con't ===
=== Testing Router Con't ===
* Examples
* Examples
** app/dashboard/dashboard.component.spec.ts
** app/dashboard/dashboard.component.spec.ts
Line 1,730: Line 1,791:
**** When we navigate to jewel it should display that jewel
**** When we navigate to jewel it should display that jewel


== e2e Testing ==
== e2e Testing ==


* When it comes to testing the '''flow''' and feature of any component, module, or '''full application'''
* When it comes to testing the '''flow''' and feature of any component, module, or '''full application'''
Line 1,740: Line 1,801:
** <small>https://www.protractortest.org/</small>
** <small>https://www.protractortest.org/</small>
** <small>https://github.com/angular/protractor/blob/master/docs/faq.md</small>
** <small>https://github.com/angular/protractor/blob/master/docs/faq.md</small>
* '''Cypress''' (no webdriver involved) - <small>https://www.cypress.io/</small>
* '''Webdriver.io''' - <small>https://webdriver.io/</small>


=== Protractor ===
=== Protractor ===
[[File:ProtractorJS.jpg|480px]]
[[File:ProtractorJS.jpg|480px]]


Ugly name, but full of power (-;
Ugly name, but full of power (-;


=== Protractor Con't ===
=== Protractor Con't ===
* End-to-end '''testing tool'''
* End-to-end '''testing tool'''
** Runs using '''Node.js''' and is available as an '''npm package'''
** Runs using '''Node.js''' and is available as an '''npm package'''
Line 1,756: Line 1,819:
** Allows '''expectations''' to be set based on what the user would expect
** Allows '''expectations''' to be set based on what the user would expect


=== Protractor Global Functions ===
=== Protractor Global Functions ===
* '''browser()''' - global object from '''WebDriver''', used to '''interact''' with the application '''browser'''
* '''browser()''' - global object from '''WebDriver''', used to '''interact''' with the application '''browser'''
** provides useful methods - ''get()'', ''getTitle()'', etc
** provides useful methods - ''get()'', ''getTitle()'', etc
Line 1,769: Line 1,832:
<!-- by.model - select element by ng-model -->
<!-- by.model - select element by ng-model -->


==== Example ====
==== Example ====
* A basic search feature
* A basic search feature
** We input 'pro' into the search box
** We input 'pro' into the search box
Line 1,779: Line 1,842:
** Protractor is a '''wrapper''' around Jasmine, with added '''features''' to support e2e testing
** Protractor is a '''wrapper''' around Jasmine, with added '''features''' to support e2e testing


==== Search Example Code (generic) ====
==== Search Example Code (generic) ====
<syntaxhighlight lang="javascript">
<syntaxhighlight lang="javascript">
describe("Giving value 'abc' for input into the search box",function(){  
describe("Giving value 'abc' for input into the search box",function(){  
Line 1,794: Line 1,857:
</syntaxhighlight>
</syntaxhighlight>


=== Protractor Show ===
=== Protractor Show ===
* It is about '''testing''' through the '''eyes of the user'''
* It is about '''testing''' through the '''eyes of the user'''
* Designed to test '''all''' the '''layers''' of an '''application'''  
* Designed to test '''all''' the '''layers''' of an '''application'''  
Line 1,802: Line 1,865:
* '''Best''' choice for '''Angular applications'''
* '''Best''' choice for '''Angular applications'''


==== Protractor Show Con't ====
==== Protractor Show Con't ====
* '''Documented''' throughout the '''Angular''' tutorials and examples
* '''Documented''' throughout the '''Angular''' tutorials and examples
* Can be written using '''multiple''' testing '''frameworks''' - ''Jasmine'', ''Mocha'', etc
* Can be written using '''multiple''' testing '''frameworks''' - ''Jasmine'', ''Mocha'', etc
Line 1,811: Line 1,874:
* Same people to 'blame' for - guys behind '''Angular''' and '''Google''' (--;
* Same people to 'blame' for - guys behind '''Angular''' and '''Google''' (--;


=== With Angular ===
=== With Angular ===
* Examples (testing_f, reactive-forms, dynamic-forms, lifecycle-hooks, form-validation)
* Examples (testing_f, reactive-forms, dynamic-forms, lifecycle-hooks, form-validation)
** e2e\src\app.e2e-spec.ts
** e2e\src\app.e2e-spec.ts
Line 1,821: Line 1,884:
*** Test the whole '''pesel''' part
*** Test the whole '''pesel''' part


== Debugging, API helpers, Practices ==
== Debugging, API helpers, Practices ==
* Debug '''same''' way as the '''app''' (console)
* Debug '''same''' way as the '''app''' (console)
* Utilities
* Utilities
Line 1,832: Line 1,895:
** <small>https://testing-angular.com/example-applications/</small>
** <small>https://testing-angular.com/example-applications/</small>


=== Practices ===
=== Practices ===
* Keep '''source''' and '''spec''' file in the '''same folder'''
* Keep '''source''' and '''spec''' file in the '''same folder'''
<!-- easy to find
<!-- easy to find

Latest revision as of 06:57, 14 August 2024


title
Angular Fundamentals Training Course
author
Lukasz Sokolowski


Angular Fundamentals

Angular Fundamentals Training Materials

Introduction

  • Angular is a completely new framework
    • Very few AngularJS core concepts are still in Angular
  • AngularJS got old
    • Predates much of what has developed around mobile technology
    • ES2015 - no serious support for new version of JavaScript
    • Angular1 was not designed for use with new web techis (like Web Components, etc)
  • Have been engineered to
    • Maximize performance
    • Emphasize mobile-first development

Intro Con't

  • Angular
    • Has a mobile-first design
    • Engineered for a small footprint
      • Data that flows from the server to the browser is minimized as much as possible
    • Has been broken into a collection of modules
      • Only the code needed to run the application is loaded
    • Syntax has been simplified and made more coherent
      • Easier to learn
      • Better support for tooling and automation

Intro Con't 1

To accomplish these goals

  • Web Components
    • Apps are built out of reusable building blocks hat encapsulate their internal logic
  • ES2015 provides
    • Classes and a solid system for loading Angular modules
  • TypeScript
    • Brings types that enable a simpler and more robust syntax
    • Abillity to build large-scale applications

Intro Con't 2

  • Angular2, along with Typescript
    • Allows to write code in both ES5 and ES2015
    • It's own team uses TypeScript to build the framework
    • Code written in TypeScript is far terser than the alternatives
    • IDEs can provide better IntelliSense and code completion support

Bootstrap Angular app

  • Loading the modules
  • App initialization process (bootstrapping)

Loading the modules

ES2015 introduced a new syntax for module loading

  • It allows for modules to be loaded selectively and asynchronously
  • TypeScript supports the ES2015 module loading syntax
  • SystemJS allows to add module loading to applications that run ES5
  • Combination of the two above gives
    • TypeScript will transpile (compile) the application's components to ES5
    • SystemJS will load them as ES5 modules
    • Each of TypeScript files will be compiled to a SystemJS module
    • SystemJS will then load all the related dependencies and when requested the module itself
  • Example
<script src="https://unpkg.com/systemjs@0.19.27/dist/system.js"></script>
<script src="systemjs.config.js"></script>

Loading the modules con't


Bootstrapping

Bootstrapping process:

  • Angular scans component definition
    • Imports the modules identified in the import statement of the component along with its related dependencies
  • It compiles the HTML view
    • Starts from where the my-app tag is declared.
    • Traverses the template
      • Looks for all interpolations
      • Sets up the binding between the view and the class
  • Post compilation links the view and the component class
    • changes are synced across the model and viewed in real time as we interact with the app
  • Example

Components in Angular

  • Component Pattern instead of MVC
  • Constructs
    • Interpolation, expressions, data binding syntax
  • Change detection

Component pattern

  • In general, involves combining smaller, discrete building blocks into larger finished products
  • In software development, logical units that can be combined into larger applications
    • Tend to have internal logic and properties that are shielded or hidden from the larger application
    • The larger application consumes these building-blocks through specific gateways (interfaces)
      • That expose only what is needed to make use of the component
    • Component's internal logic can be modified without affecting the larger application
      • As long as the interfaces are not changed

Component pattern Con't

In web applications

  • No messes of spaghetti code in applications
  • Reasoning about specific parts of the application in isolation from the other parts
  • Maintenance costs are less
    • Each component's internal logic can be managed separately
    • No affecting the other parts of the application
  • Self-describing components
    • Makes the application easier to understand at a higher level of abstraction

Constructs

  • Usually they live in the HTML template that is inside the @Component decorator
  • They are linking the view HTML and component code
  • Similar to standard HTML with new symbols:
    [ ] property bindings
    ( ) event bindings
    {{ }} interpolation
    

Interpolation

  • Replaces the content of the markup with the value of the expression (howManyTries)
    • The value of the component property will be displayed as the contents inside the interpolation tags
  • Declaration syntax:{{expression}}
    • Similar to a JavaScript expression
    • Is always evaluated in the context of the component
    • Interpolation tags read the value of the property directly from the component without any need for additional code
  • Changes made to component properties are automatically synchronized with the view
    • Easier debugging when we need to see the state of the model
    • No need to put a breakpoint in code for the value of a component property or method call
  • Example
<p class="text-info">No of tries :
  <span class="badge">{{howManyTries}}</span>
</p>

Expressions

  • Pieces of plain JavaScript code
    • Evaluated in the context of the component instance
    • Associated with the template instance in which they are used
  • Should be kept simple (readability)
    • When become complex, should be moved into a method in the component
  • New meanings for operators | and ?.

Expressions Con't

Restrictions

  • Assignment is prohibited, except in event bindings
  • The new operator is prohibited
  • ++, -- and bitwise operators are not supported
  • No referring to anything in the global namespace
  • No referring to a window or document
  • No calling console.log
Safe navigation operator

?. checks for null values in lengthy property paths

  • Example {{ invoice?.item }}
    • If null value (invoice), it stops processing the path
    • But lets the application continue running
  • Good for data loaded asynchronously
    • Because it might not be immediately available to the view
  • Prevents the application from crashing
    • Loads the data when it is available

Binding data

Binding data in Angular

  • Property binding
  • Event binding
  • Example
Property binding

[ ]

  • Binding works by linking the value of the property in component class to the value of some element in the view
  • The binding is dynamic
    • As the value of the property changes
    • The value of the element will be synchronized to the same value
    • No need to write any code to do that
<input type="text" [value]="findJewel" (input)="findJewel = $event.target.value" />
Event binding

( )

  • Event of element is bound to an expression
  • The expression sets the property in component class to $event.target.value
    • The value being entered by the user
  • Angular sets up an event handler for the event that we are binding to
<button (click)="verifyTheTry()" class="btn btn-primary btn-sm">Verify</button>
<button (click)="initializeGame()" class="btn btn-warning btn-sm">Restart</button>

Change detection

  • State maintenance
  • Change detection
State maintenance
  • Angular apps are dynamic
  • Dynamic values are kept up-to-date as the data in an application gets updated
  • Component is the container for the state
    • all the properties in the component instance and their values are available for the template instance that is referenced in the component
    • we can use these values directly in expressions and bindings in the template without having to write any plumbing code to wire them up
Change detection

Angular keeps track of changes in the component as it runs

  • Is reacting to events in the application
  • Uses change detectors
    • Go through every component to determine whether anything has changed that affects the view
    • Event triggers the change detection cycle
      • Identifies that the property that is being used in the view has changed
    • Updates the element in the view that is bound to property with the new value of it
Change detection Con't
  • Multi-step process where Angular
    • First updates the components and domain objects in response to an event
    • Then runs change detection
    • Lastly re-renders elements in the view that have changed
  • This is done on every browser event
    • Also other asynchronous events (XHR requests, timers, etc)
  • one-way data binding
    • Change detection in Angular is reactive and one way
    • Makes just one pass through the change detection graph
    • It vastly improves the performance of Angular

Web components

Standards for web browsers

  • Custom elements
  • Shadow DOM
  • Templates
  • HTML imports

Custom elements

Enable new types of element to be created

  • Other than the standard HTML tag names (div, p, etc)
  • Custom tags provides a location on the screen that can be reserved for binding a component
  • Separation of component from the rest of the page
    • Makes it possible to become truly self-contained

Shadow DOM

Provides a hidden area on the page for scripts, CSS, and HTML

  • Markup and styles in it
    • Will not affect the rest of the page
    • Will not be affected by the markup and styles on other parts of the page
  • Component can use it to render its display

Templates

Repeatable chunks of HTML

  • Have tags that can be replaced with dynamic content at runtime using JavaScript
  • Web Components standardize templating
    • Provide direct support for it in the browser
  • Can be used to make the HTML and CSS inside the Shadow DOM used by the component dynamic

HTML imports

Provide a way to load resources (HTML, CSS, JS) in a single bundle

  • Angular does not use HTML imports
    • It relies on JavaScript module loading instead

Module loading

Module provides a way for JavaScript files to be encapsulated

  • Do not pollute the global namespace
  • Can interact with other modules in a controlled manner
  • Have to be loaded into application for execution
    • ES2015
      • export (or default export)
      • import to consume them elsewhere in application
    • SystemJS, etc

Transpilation

Convertion from ES2015 to ES5

  • Similar to compilation
    • Transfers one source code into the other
  • Transpilers
    • Traceur, Babel, TypeScript

Angular modules

Organise components into complete apps

  • Combine the components into reusable groups of functionality
    • Can be exported and imported throughout the application
    • Modules can do: authentication, common utilities, external service calls, etc
  • lazy loading
    • Can be loaded on demand
  • NgModule
    • Conveniently specify the components that make up a module
  • Every Angular app must have at least one root module

Angular modules con't

An Angular module defines:

  • The components/directives/pipes it owns
  • The components/directives/pipes it makes public for other modules to consume
  • Other modules that it depends on
  • Services that the module wants to make available application wide

Angular CLI, etc

  • Tools
  • Resources

Tools

  • Browser's developer console - a lot of errors with code can be detected just by looking at it
    • put in breakpoints, add a watch, etc
  • Angular Devtools (https://angular.io/devtools)
    • Helps visualise the application through component trees, and visual debugging tools
    • Gives insight into application structure, change detection and performance characteristics
    • New feature, similar to Augury
  • Augury (https://augury.angular.io/)
    • Depricated
  • IDE extensions: Visual Studio Code, JetBrains WebStorm, Sublime Text, Atom, etc
  • JSFiddle and Plunker
  • Angular CLI - from the initial project setup all the way to the final deployment

Resources

Introduction to TypeScript

Created by Microsoft as a superset of JavaScript

  • Contains the features of ES2015 (classes, module loading, etc)
  • Provides more
    • Types
    • Decorators
  • For more, reference Typescript

Types, Functions, Lambdas

Types

  • Allow to mark variables, properties, and parameters in classes as
    • numbers, strings, Booleans, or various structures (arrays, objects, etc)
  • Thanks to that we can perform type checking at design time
    • to make sure that the proper types are being used in the application

Classes, Interfaces, Decorators, Modules

Decorators are simple annotations

  • Can be added to classes using the @ symbol along with a function
  • Provide instructions (metadata) for the use of classes
  • Allow us to identify classes as Angular components
  • Can specify
    • Modules to be used with a component
    • How to implement
      • Various bindings and directives
      • Attaching an HTML view to the component

Component Based Development

To develop applications in Angular

  1. Create components
  2. Bundle them into modules
  3. Bootstrap the application

Develop custom components

Building user interface (UI)

  • Don't follow the usual top-down practice
  • Instead with component-based design
    • We start by looking at the UI and expected behavior
    • Then we encapsulate all of this into a building block (component)
    • This component is then hosted on our page
    • Within the component
      • We separate the UI into a view
      • We separate the behavior into a class
        • Provide the appropriate properties and methods needed to support the behavior

Example 1

Build game "Find the jewel".
The objective of the game is to find a random computer-chosen jewel in as few tries as possible.

Approach:
* First think about the functionality we want to offer
* Second think about the data and behavior that can support the functionality
* Third think about how to build a user interface for it

Test it with 'live-server' (if not yet installed in the training machine, run 'sudo npm i -g live-server')
* in command line go to application's root folder and execute the command 'live-server'
  1. Designing the component
    • Detailing the features that we want the app to support
      • Choosing random jewel (origJewel)
      • Providing input for a user to find the jewel (findJewel)
      • Tracking the number of tries already made (howManyTries)
      • Giving the user hints to improve their try based on their input (hinting)
      • Giving a success message if the user found the proper jewel (hinting)
    • Determining what we need to display to the user and what data we need to track
      • Names in round brackets are the properties that will support those features
      • We need to include these properties in our component
  2. Make app folders ~/angu/examples/findthejewel
    1. Prepare main html file index.html
      <!DOCTYPE html>
      <html>
        <head>
          <title>Find the Jewel</title>
          <link href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
            <!-- Provides ES2015 features for older browsers -->
          <script src="https://unpkg.com/core-js/client/shim.min.js"></script>
            <!-- Adds decorator support -->
          <script src="https://unpkg.com/reflect-metadata@0.1.3"></script>
            <!-- Manages change detection -->
          <script src="https://unpkg.com/zone.js@0.6.23?main=browser"></script>
            <!-- Current version of TypeScript -->
          <script src="https://unpkg.com/typescript@2.0/lib/typescript.js"></script>
            <!-- Loads modules -->
          <script src="https://unpkg.com/systemjs@0.19.27/dist/system.js"></script>
            <!--
              Provides instructions to SystemJS as to which modules it should load
              and directs it to dynamically transpile TypeScript files to ES5 at runtime
            -->
          <script src="systemjs.config.js"></script>
          <script>
            // Calls SystemJS to import component directory (app)
            // Implements the module loading for our app
            System.import('app').catch(function(err){ console.error(err); });
          </script>
        </head>
        <body>
            <!-- Tag "custom element", instructs Angular where to inject the component -->
          <my-app>Our own main element for our app</my-app>
        </body>
      </html>
      
    2. Create components folder and file ~/angu/examples/findthejewel/app/find-the-jewel.component.ts
      /*
      Import statement tells what modules will be loaded and used in component.
      We load only what we need.
      No path or directory, but identifier prefixed with '@angular'.
      */
      import { Component }from '@angular/core';
      
      /*
      Decorator for component, placed directly above the class definition, identified by '@'.
      Property called 'selector', tells Angular to inject this component into '<my-app>' tag.
      Property called 'template', identifies the HTML markup for component.
      Back ticks render the template string over multiple lines.
      Also 'templateUrl' property can point to a separate file.
      */
      @Component({
        selector: 'my-app',
        template: `
          <div class="container">
            <h2>Find the Jewel</h2>
            <p class="well lead">Find which Jewel computer likes more than the others.</p>
            <label>Your Try: </label>
            <input type="text" [value]="findJewel" (input)="findJewel = $event.target.value" />
            <button (click)="verifyTheTry()" class="btn btn-primary btn-sm">Verify</button>
            <button (click)="initGame()" class="btn btn-warning btn-sm">Restart</button>
            <div>
      <!--
      The asterisk * in front of 'structural directive' ngIf is a simplified syntax.
      Angular expands into an HTML5 <template> tag.
      -->
              <p *ngIf="hinting===false" class="alert alert-warning">Nope, not that one.. )-:</p>
              <p *ngIf="hinting===true" class="alert alert-success">Yes! That's it.</p>
            </div>
            <p class="text-info">No of tries :
              <span class="badge">{{howManyTries}}</span>
            </p>
          </div>
          `
      })
      /*
      The class run our component.
      Properties reflect values from the design part, typed in TypeScript.
      Methods contain the operations that component will support.
      'constructor()' will run when an instance of component is first created.
      'verifyTheTry()' updates the 'hinting' and 'howManyTries' properties, called from 'view'.
      */
      export class FindTheJewelComponent {
        hinting: boolean;
        howManyTries: number;
        origJewel: string;
        findJewel: string;
        jewels: string[];
      
        constructor() {
          this.initGame();
        }
        initGame() {
          this.jewels = ['ruby','diamond','agate','amber','aquamarine','amethyst','opal','tourmaline', 'emerald','onyx','pearl','sapphire'];
          this.howManyTries = 0;
          
          // EXERCISE 1:
          // - What if we add new jewel to jewels? fix it!
          //
          // EXERCISE 2:
          // - Move the component's view template into a separated file and link it to the component
      
          let jewelIndex = Math.floor((Math.random() * 12) + 1);
          this.origJewel = this.jewels[jewelIndex];
            // console.log(this.origJewel);
          this.findJewel = null;
          this.hinting = null;
        }
        verifyTheTry() {
          this.hinting = (this.origJewel === this.findJewel);
          this.howManyTries = this.howManyTries + 1;
        }
      }
      
    3. Do module file ~/angu/examples/findthejewel/app/app.module.ts
      /*
      Every Angular component must be contained within an Angular module.
      'root module' - minimum one Angular module file has to be in the root of application.
      
      @ngModule - decorator to configure the module in our application
      - 'imports' property
      --- functionality needed to run our application in a browser
      - 'declarations' property
      --- we provide an array of the components that will be used in our application
      - 'bootstrap' property
      --- indicates the first component that will be loaded when our application starts up
      
      */
      import { NgModule }      from '@angular/core';
      import { BrowserModule } from '@angular/platform-browser';
      
      import { FindTheJewelComponent }   from './find-the-jewel.component';
      
      @NgModule({
          imports:      [ BrowserModule ],
          declarations: [ FindTheJewelComponent ],
          bootstrap:    [ FindTheJewelComponent ]
      })
      
      export class AppModule { }
      
    4. Bootstrap file ~/angu/examples/findthejewel/app/main.ts
      /*
      Code that bootstraps the component.
      Creates an instance of the component.
      
      'bootstrapModule' method creates a new instance of AppModule component
       - 'AppModule' component initializes 'FindTheJewelComponent'
       - It does that by calling the component's constructor method and setting the starting values
      */
      import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
      import { AppModule } from './app.module';
      
      const platform = platformBrowserDynamic();
      platform.bootstrapModule(AppModule);
      
    5. Config file ~/angu/examples/findthejewel/systemjs.config.js
      /*
       * Sets the configuration for SystemJS
       */
      System.config({
      
            // Mappings that indicate where SystemJS should look for the files
          map : {
              'app': 'app',    // our own application source
                  // RxJS is a library for reactive programming using Observables, to make it easier to compose asynchronous or callback-based code
              'rxjs': 'https://unpkg.com/rxjs@5.0.0-beta.12',
                  // several Angular modules from CDN from NodeJS package manager (NPM scoped packages)
              '@angular/common': 'https://unpkg.com/@angular/common@2.0.0',
              '@angular/compiler': 'https://unpkg.com/@angular/compiler@2.0.0',
              '@angular/core': 'https://unpkg.com/@angular/core@2.0.0',
              '@angular/platform-browser': 'https://unpkg.com/@angular/platform-browser@2.0.0',
              '@angular/platform-browser-dynamic': 'https://unpkg.com/@angular/platform-browser-dynamic@2.0.0'
          },
      /*
      Packages that will be imported and their main entry points.
      
      We select just the packages that we need
      -- helps to minimise the size of the download and provides better performance
      */
          packages:{
              'app':  { main: 'main.ts',  defaultExtension: 'ts' },
              '@angular/common': { main: 'bundles/common.umd.js', defaultExtension: 'js' },
              '@angular/compiler': { main: 'bundles/compiler.umd.js', defaultExtension: 'js' },
              '@angular/core': { main: 'bundles/core.umd.js', defaultExtension: 'js' },
              '@angular/platform-browser': { main: 'bundles/platform-browser.umd.js', defaultExtension: 'js' },
              '@angular/platform-browser-dynamic': { main: 'bundles/platform-browser-dynamic.umd.js', defaultExtension: 'js' },
          },
          // WE SHOULD NOT TRANSPILE CODE IN THE BROWSER ON PRODUCTION SITES! IT'S JUST SIMPLIFICATION FOR THE SAKE OF THE EXAMPLE
          transpiler: 'typescript',
          typescriptOptions: {
              emitDecoratorMetadata: true
          }
      });
      

Component Tree

Example

  • ngtraining
    • popup-window

Exercise

1. Rewrite find-the-jewel app into 'angular-cli' starter
2. Allow to add from UI a new jewel to the list
3. Make the wrongly guessed gem disappear from the list of available gems

Advanced Components

Examples

  • ngtraining

Exercises

  1. Separate addition of jewels into a new component ( use @Input() )
  2. Separate showing the list of jewels into a new component ( use @Output() )

Directives and Pipes

Angular directives

  • Custom tag directives - component directives - components
    • Come with their own view
  • Property and event binding - [ ], ( )
  • Attribute directives
  • Structural directives
  • Directives (custom or platform), like components, have to be registered on a module before they can be used

Attributes directives and Structural directives

  • Do not have their own view
  • Binding data
    • Attribute binding
      • For HTML attributes that do not have a backing DOM property (colspan, aria, etc)
      • Syntax [attr.attribute-name]="expression"
    • Class binding
      • Set and remove a specific class based on the component state
      • Syntax [class.class-name]="expression"
    • Style binding
      • Set inline styles based on the component state
      • Syntax [style.style-name]="expression"

Attributes directives

  • Enhance the appearance and/or behavior of existing elements/components
  • Examples ngStyle, ngClass, ngValue, ngModel, ngSelectOptions, ngControl, ngFormControl, etc
  • ngStyle
    <div [ngStyle]="{
    'width':componentWidth,
    'height':componentHeight,
    'font-size': 'larger',
    'font-weight': ifRequired ? 'bold': 'normal' }"></div>
    
  • ngClass<div [ngClass]="{'required':inputRequired, 'email':whenEmail}"></div>
Exercises
1. Change font size to 48px for text in the container, which shows hints.
2. Make input field required. Use property in component with default value 'true'.
3. Custom directive. Use highlight directive example from 'ngtraining' - for ftj main title ("Find The Jewel").

Structural directives

  • Looks similar to data binding
  • Allow to manipulate the structure of DOM elements
  • Example ngIf
    • Removes or adds DOM elements based on the result of an expression that is assigned to it
      <p *ngIf="hinting===false" class="alert alert-warning">Nope, not that one.. )-:</p>
      <p *ngIf="hinting===true" class="alert alert-success">Yes! That's it.</p>
      
  • Example ngFor
    <ul>
      <li *ngFor="let elementOnTheList of theList"> {{ elementOnTheList }} </li>
    </ul>
    
Exercises
1. Show all jewels in a nice lavender bootstrap grid, as the last element in the "no body, no crime"  (=
2. Make better hinting (same starting letter, same ending letter) 
3. Move all the hinting logic into your own custom directive

Pipes

  • Provide a mechanism to format view content
    • Format the value of an expression displayed in the view
  • Predefined pipes - date, currency, lowercase, uppercase, json, etc
  • Syntax (in a view): {{expression | pipeName:inputParam1}}
  • Can be chained
    • Output from one pipe can serve as the input to another pipe
  • Examples
    {{ someString | slice:0:10 }} //renders first 10 characters
    {{ dateObj | date:'shortTime' }} // output is '9:43 PM'
    {{ 22.15 | currency:"USD" }} <!-Renders USD 22.15 -->
    {{ fullName | slice:0:10 | uppercase }}
    

Exercise

1. Change jewel names in your grid to uppercase characters
2. Custom pipe: show the number of characters in jewel's name, like this "EMERALD 7"
(example in ngtraining)

Building blocks

An Angular application consists of the following parts:

  • Modules
  • Components
  • Templates
  • Metadata
  • Data binding
  • Directives
  • Services
  • Dependency injection

Immutable.js

  • Immutable objects/collections are objects that cannot be changed once created
  • Any property change results in a new object being created
  • Advantages
    • much simpler application development
    • no defensive copying
    • enabling advanced memoization and change detection techniques with simple logic

Observables

  • Core Reactive Extensions features
  • Enhance the application so that users can
    • edit existing objects in the model
    • create new ones
  • Distribute updates throughout an application
    • immediate updates to the application state
  • Observer - the mechanism by which updates are created
  • Observable - represents an observable sequence of events
    • An object such as a component can subscribe to an Observable
      • will receive a notification each time an event occurs
      • allowing it to respond only when the event has been observed
      • instead of every time there is a change anywhere in the application
  • Subject - class, which implements both the Observer and Observable functionality
    • Easy to create a service that allows events to be produced and consumed with a single object
  • Examples
    • ngtraining

Dependency Injection

Allows components and other building blocks

  • To receive services by declaring dependencies on them using constructor parameters
  • To share objects throughout an application
  • To avoid the need to distribute shared objects manually

Examples

  • ngtraining

Exercise

  1. Create a custom service, which will allow to
    • get all the jewels list
    • add new jewel
    • remove jewel

Routing

Routing uses the browser’s URL to manage the content displayed to the user

  • Allows the structure of an application to be kept apart from the components and templates in the application
  • Changes to the structure of the application are made in the routing configuration
    • moved from individual components and directives
  • The routing configuration
    • Set of fragments
      • Match the browser’s URL
      • Select a component whose template is displayed as the content of an HTML element called router-outlet

Routes in Angular

  • Navigation
  • Wildcards and redirection
  • Child routes
  • Guarding routes
    • Delaying Navigation with a Resolver
    • Preventing Navigation with Guards
  • Lazy loading - loading feature modules dynamically
  • Targeting named outlets
  • Examples
    • ngtraining
    • exampleApp

Exercise

  1. Routes with children
    • Route for adding new gems
    • Route for home page
    • Route for 404 page not found
    • Route for listing all jewels
    • Route for finding jewel
    • Route for showing one jewel (as a child component of listing-jewels)
  2. Route for module with gems
  3. Improve gems module with lazy loading

Forms

Angular provides set of constructs that make standard form-based operations easier

  • Allowing user inputs
    • two-way bindings between the form input elements and the underlying model
      • hence avoiding any boilerplate code that we may have to write for model input synchronization
  • Validating those inputs against business rules
  • Submitting the data to the back-end server

Template Driven Forms

Template-driven forms develops a form within an HTML template

  • handling most of the logic for the form-inputs, data validation, saving, and updating-in form directives placed within the template
  • very little form-related code is required in the component class that is associated with the form's template
  • make heavy use of the ngModel form directive
    • It provides two-way data binding for form controls
    • It allows us to write much less boilerplate code to implement a form
    • It also helps us to manage the state of the form
      • whether the form controls have changed and whether these changes have been saved
    • Ability to easily construct messages that display if the validation requirements for a form control have not been met
      • examples - a required field not provided, e-mail not in the right format, etc
  • Exercise
    • Make your ftj engine component's form template-driven and validate it (required)

Form Builder

Model-driven forms

  • start with a model that is constructed in a component class
  • form builder API creates a form in code and associate it with a model
  • to create form controls dynamically based on data we are retrieving from the server
  • for complicated validation, it is often easier to handle it in code
    • keep complicated logic out of the HTML template, making the template syntax simpler
  • make unit-testing the form possible
  • New form directives - FormGroup, FormControl, and FormArray
    • allow the form object that is constructed in code to be tied directly to the HTML markup in the template
    • cleaner and less cluttered template with more focus on the code that drives the form

Examples

Exercises

  • Improve ngtraining example with custom zip validator
  • Provide custom validation for ftj add-new-jewel component
    • ban adding 'salt' and 'sugar'
      • show error message to user "Too much salt and sugar can kill you!"
      • disable submit button, unless the form is valid
    • do not allow to add already existing jewel
  • Pesel app

REST and State Management

HTTP API

Asynchronous HTTP requests

  • HTTP requests sent by the browser on behalf of the application
  • asynchronous - the application continues to operate while the browser is waiting for the server to respond
  • allow Angular to interact with web services
    • persistent data can be loaded into the application and changes can be sent to the server and saved
  • Requests are made using the Http class, delivered as a service through dependency injection
    • This class provides an Angular-friendly wrapper around the browser’s XMLHttpRequest feature
  • Examples
    • ngtraining
    • exampleApp
  • Exercises
    • Move gems list to model and use json-server to get them asynchronously into our grid
    • meanCRUD
    • Rewrite 'ftj', connect with backend 'jewel-db'

Redux and Ngrx

  • NgRx - framework for building reactive applications in Angular
    • inspired by the Redux pattern
    • unifies the events in our application and derives state using RxJS
    • stores a single state and uses actions to express state changes
    • manages complex states (apps with a lot of user interactions and multiple data sources)
    • ngrx.io
  • Alternatives
    • NGXS - ngxs.io
    • Akita - github.com/salesforce/akita
    • Elf - ngneat.github.io/elf


Migrating from Angular 1.x to Angular

Migration Steps

Choosing a path

ng-forward and ng-upgrade

Migrating from Angular 2.x to Angular x.x

More examples

Angular docs

Cheat-sheet

News

What's new in Angular ...

  • 16
  • 17
  • ...next

News Con't - 16

16 - https://blog.angular.io/angular-v16-is-here-4d7a28ec680d

Router inputs

// To enable it:
//// use 'withComponentInputBinding' as part of the 'provideRouter'
const routes = [
  {
    path: 'about',
    loadComponent: import('./about'),
    resolve: { contact: () => getContact() }
  }
];

@Component(...)
export class About {
  // The value of "contact" is passed to the contact input
  @Input() contact?: string;
}

Ex with 16

Exercises:

  1. Make our jewels-list component working as a standalone one
  2. Create new app - bootstrapping one component only
  3. Migrate the whole ftj into standalone mode
  4. Add ssr to ftj app and enable hydration
  5. Turn on esbuild in our ftj

News Con't - 17

17 - https://blog.angular.dev/introducing-angular-v17-4d7033312e4b

  • Built-in control flow
    • new block template syntax - @if, @switch, @for
    • ng generate @angular/core:control-flow
  • Deferrable views - better lazy loading
    • @defer, @loading, @error, @placeholder, @defer
    • triggers - idle, on immediate, on timer, on viewport, on interaction, on hover, when
    • more here - https://v17.angular.io/guide/defer
  • Improvements to server-side rendering (SSR) and static-site generation (SSG or prerendering)
    • new lifecycle hooks - afterRender, afterNextRender
  • Vite and esbuild - are default now for new projects
  • Dependency injection debugging in DevTools
  • Standalone APIs - by default in ng generate
    • ng generate @angular/core:standalone
  • New docs - angular.dev

Testing JS

TestingJS.jpeg

Yes, it's possible.

Hard to believe, but true (-8

Testing tools and frameworks

  • Karma - the test runner for JavaScript
  • Protractor - the end-to-end testing framework (deprecated)
  • Jasmine - the behavior-driven JavaScript testing framework
  • Mocha - the JavaScript testing framework (for nodejs)
  • QUnit - the unit testing framework (for jQuery)
  • Selenium - the tool that automates the web browsers
  • PhantomJS - the headless webkit browser

Test-Driven Development

TddJS.jpg

Test-Driven Development Con't

  • An approach to development
    • We write a test before we write just enough production code to fulfill that test and its refactoring
  • Benefits
    • No change is small
    • Specifically identify the tasks
    • Confidence in refactoring
    • Upfront investment, benefits in the future
    • QA resources might be limited
    • Documentation

Practical TDD with JavaScript

TDD life cycle

  • Test first
    • Write the test, watch it fail
  • Make it run
    • Write just enough code to pass the test
  • Make it better
    • Improve the code without changing its behavior

Unit VS E2E Testing

UnitVSe2eJS.png

Unit Testing

  • Jasmine and Karma
  • Unit Testing in Angular
  • Testing Components
  • Testing Services
  • Testing Attribute Directives
  • Testing Pipes
  • Testing Router

Unit Testing Con't

  • Jasmine test framework
  • Karma test runner

Jasmine

JasmineJS.jpg

The beloved One for Aladyn or..

..intriguingly scenting flower.

Well, the choice is yours (-;

Jasmine Con't

  • Fast - core has no external dependencies
  • Full out of the box set of everything we need to test our code
  • Supports browser tests and Node.js tests
  • https://jasmine.github.io/

Example

Function to test Jasmine test spec
function helloWorld() {
  return 'Hello world!';
}
describe('Hello world', () => { 
  it('says hello', () => { 
    expect(helloWorld()) 
        .toEqual('Hello world!'); 
  });
});
  • describe(string, function) - defines a Test Suite
    • A collection of individual Test Specs
  • it(string, function) - defines an individual Test Spec
    • This contains one or more Test Expectations
  • expect(actual) - is an Expectation
    • With a Matcher it describes an expected piece of behavior in the application
  • matcher(expected) - a boolean comparison between the actual value and the expected value
    • Reports to Jasmine if the expectation is true or false - then the spec will be passed or failed

Jasmine Built-in Matchers

expect(array).toContain(member);
expect(fn).toThrow(string);
expect(fn).toThrowError(string);
expect(instance).toBe(instance);
expect(mixed).toBeDefined();
expect(mixed).toBeFalsy();
expect(mixed).toBeNull();
expect(mixed).toBeTruthy();
expect(mixed).toBeUndefined();
expect(mixed).toEqual(mixed);
expect(mixed).toMatch(pattern);
expect(number).toBeCloseTo(number, decimalPlaces);
expect(number).toBeGreaterThan(number);
expect(number).toBeLessThan(number);
expect(number).toBeNaN();
expect(spy).toHaveBeenCalled();
expect(spy).toHaveBeenCalledTimes(number);
expect(spy).toHaveBeenCalledWith(...arguments);

Jasmine Custom Matchers

  • Example
    • testing_f\src\testing\jasmine-matchers.ts

A few useful global functions

To avoid any duplicated setup and to teardown code

  • beforeAll(), afterAll()
    • Called once before/after all the specs in describe test suite are run.
  • beforeEach(), afterEach()
    • Called before/after each test specification, it() function, has been run.

Example

describe('Hello world', () => {

  let expected = "";

  beforeEach(() => {
    expected = "Hello World";
  });

  afterEach(() => {
    expected = "";
  });

  it('says hello', () => {
    expect(helloWorld())
        .toEqual(expected);
  });
});

Exercise

  • testing-angular/just-jasmine (live coding)

Karma

KarmaJS.jpg

In Polish language it means: food for animal (-;

Let's feed our NG App properly! (-8

Karma Con't

  • Manually running Jasmine tests by refreshing a browser tab repeatedly in different browsers every-time we edit some code can become tiresome.
  • Karma is a tool which lets us spawn browsers and run jasmine tests inside of them all from the command line. The results of the tests are also displayed on the command line.
  • Karma can also watch your development files for changes and re-run the tests automatically.
  • Karma lets us run jasmine tests as part of a development tool chain which requires tests to be runnable and results inspectable via the command line.
  • It’s not necessary to know the internals of how Karma works.
    • When using the Angular CLI it handles the configuration for us and for the rest of this section we are going to run the tests using only Jasmine.
  • https://karma-runner.github.io/latest/index.html

x and f

  • x: disables tests
  • f: focused tests
xdescribe('Hello world', () => { 
  xit('says hello', () => { 
    expect(helloWorld())
        .toEqual('Hello world!');
  });
});
fdescribe('Hello world', () => { 
  fit('says hello', () => { 
    expect(helloWorld())
        .toEqual('Hello world!');
  });
});

Unit Testing in Angular

  • Angular components and directives require special support for testing
    • their interactions with other parts of the application infrastructure can be isolated and inspected
  • Isolated unit tests
    • are able to assess the basic logic provided by the class that implements a component or directive
    • but do not capture the interactions with host elements, services, templates, and other important Angular features
  • TDD (Test, Execute, Refactor) with Assemble, Act, and Assert
  • Example - Create new angular app called ftjt, with router and simple CSS - ng new ftjt
    • Add new selling-jewels component - ng g c selling-jewels --skip-tests --inline-template
    • It should have the title "Buying can be fun, but selling too!", kept as component's property
  • Let's do it together by following the steps below

Test it first

  • Add new file - ftjt/src/app/selling-jewels/selling-jewels.component.spec.ts
  • Start with the base Jasmine template format
describe('Test suite title', () => { 
    beforeEach(() => { 
        // .... 
    }); 
    it('Test spec title', () => { 
        // .... 
    }); 
});

Assemble Part 1

  • initialize the component, execute the contractor of the class, setup Angular testing APIs
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SellingJewelsComponent } from './selling-jewels.component';

// ...
beforeEach(async(() => {
  TestBed.configureTestingModule({
    declarations: [ SellingJewelsComponent ]
  })
  .compileComponents();
}));
// ...

Assemble Part 2

  • define the component object, wrap it into fixture - Angular's utility
// ...
// comp will hold the component object  
let comp: SellingJewelsComponent; 
let fixture: ComponentFixture<SellingJewelsComponent>; 
// ...
beforeEach(() => { 
  fixture = TestBed.createComponent(SellingJewelsComponent); 
  comp = fixture.componentInstance; 
});

Act Part

  • Nothing to do here yet
  • We will see that part later with bigger example

Assert Part

it('Should define a title', () => { 
  expect(comp.title).toBeDefined(); 
});

it('Title should be as expected', () => {
  expect(comp.title).toEqual("Buying can be fun, but selling too!");
});

Make it run

  • Run command ng test (keep it running in the separate terminal, rerun it after adding new c, s, etc)
  • Add the title property - title: string;
  • Tests failed, so we're happy (-:
  • Add the title value - title: string = '';
  • One test fine, 2nd no
  • Correct the title value - title: string = 'Buying can be fun, but selling too!';
  • Both tests passed (-:

Make it better

  • Interpolate the title in component's template
  • We'll use the same module and controller, so the same test file
// ...
  describe('Testing interpolated title', () => { 
    beforeEach(() => { 
        // .... 
    });    
    it('Title of the test spec', () => { 
        // .... 
    }); 
  });

Assemble, Act Parts

  • Assemble - we will explain this setup later
// ...
let p: HTMLElement;
// ...
beforeEach(() => {
  p = fixture.nativeElement.querySelector('p');
  fixture.detectChanges();
});
  • Act
    • No custom action yet (interpolation is from angular core)

Assert Part

it('Title should be interpolated', () => {
  expect(p.textContent).toContain(comp.title);
});

Make it run

  • Tests failed
  • Interpolate the title property in the template - template: `<p>{{title}}</p>`,
  • Test passed (-:

Testing Components

  • Component combines an HTML template and a TypeScript class
  • We test that they work together as intended, by
    • creating the component's host element in the browser DOM, as Angular does
    • investigating the component class's interaction with the DOM as described by its template
  • Class alone
  • TestBed (with DOM)

Nested Components

  • Stubbing
  • NO_ERRORS_SCHEMA
  • Examples
    • app/app.component.spec.ts
    • app/about/about.component.spec.ts
  • Exercise
    • Add new jewel game engine component (name it shortly - engine) - ng g c engine
    • Call components selling-jewels and engine from app component's template
    • Fix ftjt's main app component test

Class alone

  • Examples
    • app/demo/demo.ts (LightswitchComp)
    • app/dashboard/dashboard-hero.component.ts
    • app/welcome/welcome.component.ts
    • ngtraining
      • app/jasmine-test/jasmine-test.component.spec.ts
  • Exercise
    • Move game description in to a new component ("Find which Jewel computer likes more than the others.")
      • cd src/app/engine
      • ng g c descr
      • It should show text 'Game Description' by default and when clicked - 'Find which Jewel computer likes more than the others.'

With DOM

  • TestBed.configureTestingModule() - to setup the TestBed
  • createComponent() - creates an instance, adds a corresponding element to the test-runner DOM, and returns a ComponentFixture
  • ComponentFixture - interacting with the created component
  • beforeEach() - for duplicated TestBed configuration
  • nativeElement - usually with HTMLElement.querySelector()
  • DebugElement - non-browser platform
  • By.css() - run on a different platform

With DOM con't

  • Examples
    • app/banner/banner.component.ts
    • app/welcome/welcome.component.ts
  • Exercise
    • In H2 tag replace "Find The Jewel" text with new interpolated gameTitle property
      • Assert that engine component provides the default value for gameTitle ("Find The Jewel")
      • Test if it works also for different value of gameTitle
    • Assert that engine component changes the value of howManyTries

Testing binding

  • fixture.nativeElement.querySelector() - querying for html
  • createComponent() with fixture.detectChanges() - manual change detection
  • ComponentFixtureAutoDetect - automatic change detection
  • dispatchEvent() - changing an input value

Testing binding Con't

  • Examples
    • app/banner/banner.component.spec.ts
    • app/banner/banner.component.detect-changes.spec.ts
    • app/hero/hero-detail.component.spec.ts
  • Exercise
    • Simulate user input in engine
    • Improve it with <p id="providedJewel">Your gues is {{findJewel}}</p>

External file, dependencies

  • Service test doubles - stubs, fakes, spies, mocks
  • injector.get(service_name) - from the injector of the component-under-test
  • TestBed.inject() - from the root injector (if it's the only provider)
  • Async service

External file, dependencies Con't

  • Examples
    • app/banner/banner-external.component.spec.ts (vs banner.component.spec.ts)
    • app/welcome/welcome.component.spec.ts
    • app/twain/twain.component.spec.ts
  • Exercise
    • Create new service jewel
      • ng g s jewel
      • provide it from app.module.ts
    • Test engine component with injected jewel service
      • mock, stub, test-double, injected

Testing a list of items

  • Examples
    • app/hero/hero-list.component.spec.ts
  • Exercise
    • Create new component jewels-list
      • cd src/app/engine
      • ng g c jewels-list
    • Test jewels-list component

Input, Output

  • Appears inside the view template of a host component
    • host uses a property binding to set the input property
    • listens to events raised by the output property (event binding)
  • We test if such binding works as expected
    • set input values and listen for output events
  • Possible methods - as used by, stand-alone component, used by a substitute (test-host)
    • as used by - usually to much effort and it's over-complicated
    • stand-alone - triggerEventHandler or element's own click() method
    • test-host - disables distractions (*ngFor, components, layout HTML, other bindings, injected services, etc)

Input, Output Con't

  • Examples
    • app/dashboard/dashboard-hero.component.spec.ts
  • Exercise
    • Test communication between engine and add-jewel components (stand-alone)
      • cd src/app/engine
      • ng g c add-jewel
    • Test jewel-detail according to jewels-list-detail (test-host)
      • cd src/app/engine
      • ng g c jewels-list-detail
        • make it with a routerLink
      • ng g c jewel-detail
      • any conclusion? (0;

Testing forms

  • Exercise
    • Add pesel component
      • Source is here
      • Make it as angular's reactive form

Overriding providers

  • Component is created with its own injector - child of the fixture injector
  • Component's own providers
    • Can't be stubbed or configured in TestBed.configureTestingModule
      • For example test cannot get to child injector services from the fixture injector
  • Solution - TestBed.configureTestingModule().overrideComponent() method
    • Replaces the component's providers with test doubles (spy stub)
  • Other angular classes are supported too - overrideDirective, overrideModule, overridePipe

Overriding providers Con't

  • Example
    • app/hero/hero-detail.component.spec.ts
  • Exercise
    • Move validators in pesel component to it's own custom service

Testing Services

  • The easiest Angular construct to unit test
  • With dependencies
    • Do not inject the real services - usually they are difficult to create and control
    • Mock the dependency, use a dummy value, or create a spy
  • TestBed - lets Angular DI do the service creation and deal with constructor argument order
    • Creates a dynamically-constructed Angular test module that emulates an Angular @NgModule.
    • TestBed.configureTestingModule() - metadata object that can have most of the properties of an @NgModule
    • with TestBed.inject()
    • inside the beforeEach()
    • via providers array
  • HTTP services
    • with just HttpClient
    • with HttpClientTestingModule

Testing Services Con't

  • Examples
    • app/demo/demo.spec.ts
    • app/demo/demo.testbed.spec.ts
    • app/model/hero.service.spec.ts
  • Exercise
    • Test jewel service
      • With and without testbed
      • Improve it by adding new jewel
        • cd src/app/engine
        • ng g c add-jewel-service
        • Test it from service perspective
      • Make it as an observable
        • cd src/app
        • ng g s jewel-obs
        • Test it from component perspective
          • Which one: add-jewel-service or jewels-list-detail ?

Testing Attribute Directives

  • Attribute Directive
    • Modifies the behavior of an element, component or another directive
    • Is applied as an attribute on a host element
  • Some noteworthy techniques
    • By.directive - gets the elements that have specific directive when their element types are unknown
    • :not pseudo-class - helps find elements that do not have the directive
    • DebugElement.styles- access to element styles without a real browser (an alternative to nativeElement)
    • Use the Angular's default element injector in tests to get defaults from custom directives
    • DebugElement.properties - access to the artificial custom property that is set by the directive

Testing Attribute Directives Con't

  • Examples
    • app\shared\highlight.directive.spec.ts
  • Exercise
    • Test favorite-jewel directive

Testing Pipes

  • No need to use Angular utilities
    • Manipulates the input value into a transformed output value
  • Isolated tests only
    • Can't tell if it's working properly as applied in the application components

Testing Pipes Con't

  • Examples
    • app/shared/title-case.pipe.spec.ts
    • app/hero/hero-detail.component.spec.ts
    • ngtraining
      • app/pipe-solution/even-uppercase.pipe.spec.ts (live coding)
  • Exercise
    • Test jewel pipe
      • cd src/app
      • ng g p jewel
      • It should count the number of characters in jewel's name
    • Test jewels-list-detail component with jewel pipe

Testing Router

  • Routed Components
  • RouterTestingModule

Testing Router Con't

  • Examples
    • app/dashboard/dashboard.component.spec.ts
    • app/hero/hero-detail.component.spec.ts
    • testing/activated-route-stub.ts
  • Exercise
    • Test the ftjt app router
      • From routed component - jewel-detail
        • When we navigate to jewel it should display that jewel

e2e Testing

Protractor

ProtractorJS.jpg

Ugly name, but full of power (-;

Protractor Con't

  • End-to-end testing tool
    • Runs using Node.js and is available as an npm package
    • Allows the e2e testing by interacting the DOM elements of application
  • Selects a specific DOM element
    • Shares the data with that element
    • Simulates the click of a button, etc
    • Interacts with an application in the same way as a user would
    • Allows expectations to be set based on what the user would expect

Protractor Global Functions

  • browser() - global object from WebDriver, used to interact with the application browser
    • provides useful methods - get(), getTitle(), etc
  • element() - used to find a single element based on the locator
    • supports multiple element selection - element.all (also takes Locator and returns ElementFinderArray)
    • methods - element.all(Locator).get(position), element.all(Locator).count(), etc
  • action - to interact with a DOM
    • methods - getText(), click(), clear(), etc
  • Locator - how to find a certain element in the DOM
    • exported as a global factory function, which will be used with a global by object
    • examples - element(by.css(cssSelector)), element(by.id(id)), element.(by.model), etc

Example

  • A basic search feature
    • We input 'pro' into the search box
      • The search button should be clicked on
      • At least one result should be received
    • No describing a controller, directive, or service
      • Only the expected application behavior
  • The structure and syntax of Protractor mirrors that of Jasmine
    • Protractor is a wrapper around Jasmine, with added features to support e2e testing

Search Example Code (generic)

describe("Giving value 'abc' for input into the search box",function(){ 
  browser.get('/');                                           // 1 - Point browser to website 
  var inputField = element.all(by.css('input'));              // 2 - Select input field 
  inputField.setText('pro');                                  // 3 - Type 'pro' into input field 
  var searchButton = element.all(by.css('#searchButton');     // 4 - Push search button 
  searchButton.click(); 
  it('should display search results',function(){ 
    var searchResults = element.all(by.css('#searchResult');  // 5 - Find the search result details 
    expect(searchResults).count() >= 1);                      // 6 - Assert 
  }); 
});

Protractor Show

  • It is about testing through the eyes of the user
  • Designed to test all the layers of an application
    • Web UI, backend services, persistence layer, etc
  • Focused on how the complete system works when interconnected
  • Allows the developer to focus on the complete behavior of a feature or module
  • Best choice for Angular applications

Protractor Show Con't

  • Documented throughout the Angular tutorials and examples
  • Can be written using multiple testing frameworks - Jasmine, Mocha, etc
  • Provides convenience methods for Angular components
    • waiting for a page to load, expectations on promises, etc
  • Wraps Selenium methods that automatically wait for promises to be fulfilled
  • Supported by SaaS (Software as a Service) providers - Sauce Labs, etc
  • Same people to 'blame' for - guys behind Angular and Google (--;

With Angular

  • Examples (testing_f, reactive-forms, dynamic-forms, lifecycle-hooks, form-validation)
    • e2e\src\app.e2e-spec.ts
    • e2e\protractor.conf.js
  • Exercise - testing forms
    • Run ng e2e in ftjt - fix the default test (-:
    • Write e2e test for ftj game
      • Rewrite the engine and add-new-jewel into reactive forms
      • Test the whole pesel part

Debugging, API helpers, Practices

Practices

  • Keep source and spec file in the same folder
  • Separated test folder - for integration specs
  • KISS - keep it simple stupid
  • e2e tests - in angular avoid them AMAP, instead go for unit tests techniques
    • no comprehensive test coverage
    • difficult to write and perform poorly, break easily
    • no telling if missing or bad data, lost connectivity, and remote service failures
    • when update db, credit card gates - tricky and risky