angular basics

Karine Samyn, Benjamin Vertonghen, Thomas Aelbrecht, Pieter Van Der Helst

"The strength of JavaScript is that you can do anything.
The weakness is that you will." - Reg Braithwaite

overview.

  1. intro
    what is an SPA?
  2. typescript
    typescript, not javascript
  3. app
    creating a new angular application
  4. components
    it's all about components
  5. pipes
    how to format data
  6. @Input()
    pass data between two components
  7. debugging
    how to debug angular application in VSCode

what is an SPA?

  • exactly one html file (SINGLE page application), which is only used as an entry point for your application
  • and a lot of javascript, which builds the page through DOM manipulation
  • requesting JSON data from the server as it's needed to build the page
  • think what you did in Web2 with insertHTML and such, but every single letter on your page is created this way, nothing is inside html pages
    and don't worry, this is nothing like web2, the framework takes care of the nasty parts

'classic' vs SPA site

typescript.

  • angular programs are written using typescript
  • typescript is an extension of javascript (every valid javascript program is a valid typescript program)
  • created in 2012 at Microsoft by Anders Hejlsberg (who also created Turbo Pascal and C#)
  • as tools mature to take advantage of typescript (and since everyone's transpiling their javascript anyway because of angular/react/...) it's becoming more and more popular
  • it was #2 on stackoverflow's "most loved languages" in 2019

typescript? what's wrong with javascript?

  • browsers were (are?) always slow to adopt new javascript versions and features, and old javascript was weird and bad
  • so people desperately wanted to use the new javascript features, and started relying on transpiling: writing ES6+ and using e.g. babel to 'transpile' this to ES5 the browser understands
  • but as a compile step was added, why not go all the way and extend javascript?
  • hence typescript was born: modern javascript with all class/type related stuff added

why do we need types?

airbnb bugs

why do we need types?

  • I'd love to argue static vs dynamic types in programming languages, because I love arguing when I know I'm right
  • but we don't really have the time; very simply put: if you're not perfect, you're better of with static typing
  • (for the same reasons unit testing is good: you catch bugs early)
  • while we're at it: tabs! spaces are for suckers, and vim forever, losers use emacs!

typescript.


let name: string;
const age: number = 15;

let cities: string[];
let postalcodes: Array<number>;
let recipe: Recipe;
let notThatUseful: any;
          
the most important difference is that everything has a type types can be implicit if you assign a value (which always has a type) to a variable, the variable gets that type as well you can define array's both with [] as with the Array keyword you can have both basic types as types you define yourself with class when the type is not specified, it's considered of type any, you should avoid using any whenever you can sometimes you have to though, for javascript compatibility, or when processing generic json data

typescript.

  
         interface LabelledValue {
           label: string;
         }
         class Recipe {
          private _name: string;
          private _ingredients: string[];
          readonly maximumSteps = 12;
           
          constructor() {
             this._name = 'spaghetti';
             this._ingredients = ['tomato', 'minced meat'];
          }

          get name(): string { return this._name; }
          set name(name: string) { this._name = name; }
           
          public printDirections(labeled?: LabelledValue) {
           console.log(labeled.label);
          }
       } 

       let rec = new Recipe();
       rec.name = 'risotto';
          
another set of differences (or extras) are available when dealing with classes Javascript 6 introduced the class keyword, making it easier to work with javascript class definitions for people who come from other OO backgrounds (prototype mechanism could be confusing) typescript takes this further, you can now define properties inside your class definition and not just using this.propertyname in the constructor you can also specify access modifiers if nothing is specified, everything in a class is public, and everything in a module is private except what's exported by convention, the names of private properties usually start with an underscore there's also the readonly modifier for properties readonly properties must be initialized when declared or in the constructor, and can't change afterwards you can declare computed properties using get and set which can be used just like public properties outside your class the interface keyword is also available, behaves (and is used) just as you know it from java you can explictly specify that arguments are optional by using a ?

typescript.

  
          class Recipe {
            private _name: string;
            
            constructor(private _name: string) {
              this._name = name;
            }
          }
          
one last thing I want to explicitly point out is the initialize-and-declare-simultaneously typescript idiom when creating classes you often want to specify a private member variable, pass an initial value in the constructor parameter list, and initialize it in the constructor since this is a very common pattern, typescript has a shorthand for it simply add an access modifier before the parameter of the constructor, no separate property, nothing in the body of the constructor this is the exact same thing as before, with a more convenient syntax

typescript

our example app

  • a recipe webapp
  • user / recipes
  • list of recipes / live search
  • form to add recipes
~$ ng new recipeapp
			
  • angular cli
  • the command angular cli should execute
  • the name of the app we're creating
filelist

I chose 'No' routing and plain CSS
(routing comes in a few weeks, and nobody likes css so we don't do that here)

Now we can run this

					
~$ cd recipeapp
~/recipeapp$ ng serve
					
				
  • first run will take a while (everything needs to be compiled)
  • app is available @ http://localhost:4200/
  • you get a default 'hello world' angular app, with some links to get you started
  • changes are tracked and app updates automatically (Webpack)
  • everything is done in memory (by default no files are written)

problems?

webpack problems?

  • open the Chrome console, click on errors
  • if obvious (typo...) fix it
  • if the error is very weird and doesn't make sense...
  • quit 'ng serve', quit your browser, hide your kids, hide your wife
  • restart everything, still errors: start googling

it's all about components

  • we create our webapp by defining new tags, and build our pages using those
  • such a new tag is created through a Component
  • components consists of some html, css and (typescript) code
  • ... and tests, in an ideal world

creating our first component

~/recipeapp$ ng generate component recipe
installing component
  CREATE src/app/recipe/recipe.component.css (0 bytes)
  CREATE src/app/recipe/recipe.component.html (25 bytes)
  CREATE src/app/recipe/recipe.component.spec.ts (628 bytes)
  CREATE src/app/recipe/recipe.component.ts (269 bytes)
  UPDATE src/app/app.module.ts (396 bytes)
				
  • generates html/css/ts/unit test
  • makes sure the component gets included at the right place so you can use it (more on that later)

creating our first component

src/app/recipe/recipe.component.ts
                          import { Component, OnInit } from '@angular/core';
          
                          @Component({
                            selector: 'app-recipe',
                            templateUrl: './recipe.component.html',
                            styleUrls: ['./recipe.component.css']
                          })
                          export class RecipeComponent implements OnInit {
                            constructor() { }
                          
                            ngOnInit() {
                            }
                          }
                      
let's take a quick look at the code generated for a new component we need to import dependencies (more about this when we talk about modules) components are defined in a decorator of a class selector is the name of the tag you'll use in the html, so
<app-recipe></app-recipe>
will show this component
templateUrl and styleUrls contain the html (one) and css (multiple) of this component styles defined in this css are scoped! they will only apply on the html of this template (styles never 'leak')

loading our first component.

 src/app/app.component.html 
              <div style="text-align:center">
                <h1>
                  Welcome to {{title}}
                </h1>
              </div>
              <div>
                <app-recipe></app-recipe>
              </div>            
             bb3d916 
now we can use this tag in our other html files to see this at work, we add it to the html of our 'Main' component http://localhost:4200/

github.

all the code from the example app can be found on my github repo , using small incremental commits when a slide corresponds with a commit, the commit hash is at the bottom of the code window
~$ git clone https://github.com/Pieter-hogent/recipeapp.git
~$ cd recipeapp
~$recipeapp/ npm install
~$recipeapp/ git checkout -b mybranch bb3d916
              

ng doc

  • we've done ng new, ng serve, ng generate, but there are more ng commands available
  • one of the more useful one's is probably ng doc
  • typing ng doc <something> will open the official documentation about <something> in your browser (must be used inside an angular project)

adding data to the component

 src/app/recipe/recipe.component.ts 
                export class RecipeComponent implements OnInit {
                  name: string;
                
                  constructor() { 
                    this.name = 'spaghetti';
                  }
                
                  ngOnInit() {
                  }
                }	            
            
declare a variable inside the class to hold the name it has a type! (this makes it typescript) accessible inside all member function using this.

adding data to the component

 src/app/recipe/recipe.component.html 
<p>
  recipe for {{name}}
<p>           
            7ff11fa
the property can be bound by enclosing it in {{}} inside the html (no this. here, simply the name of the property) let's try it

using a collection of data

 src/app/recipe/recipe.component.ts 
                export class RecipeComponent implements OnInit {
                  name: string;
                  ingredients: string[];

                  constructor() {
                    this.name = 'spaghetti';
                    this.ingredients =  ['tomato', 'onion', 'celery', 
                      'carrot', 'minced meat'];
                  }

                  ngOnInit() {}
                }		            
            
what if we want to use a collection of data we declare the variable as an array of strings and obviously initialize it with an array too

using a collection of data

 src/app/recipe/recipe.component.html 
<p>
  recipe for {{name}}
</p>
Ingredients:
<ul>
  <li *ngFor="let ingredient of ingredients">
    {{ingredient}}
  </li>
</ul>           
            f9fdd84
the *ngFor keyword loops over an array (notice the '*') declare a new loopvariable (the loop 'code' is passed as a string parameter to *ngFor) then simply use this new loop variable as you would any other component property let's try it

formatting data

  • sometimes you don't want to show a property as it's stored, but formatted (dates, currency, floating point numbers, ...)
  • you could add a separate (display) property for each property you want to format (date, dateStr), but this is cumbersome and error prone
  • and what if you want to format differently, based on the locale? (US dates vs EU dates)
  • the Angular solution is to use Pipes, when a property is shown it is 'piped' through a function first

pipe

 src/app/recipe/recipe.component.ts 
                export class RecipeComponent implements OnInit {
                  name: string;
                  ingredients: string[];
                  dateAdded: Date;
                
                  constructor() {
                    this.name = 'spaghetti';
                    this.ingredients = ['tomato', 'minced meat'];
                    this.dateAdded = new Date();
                  }
                }            
            
to illustrate this, let's look at the built in date pipe, imagine we want to show the date a recipe is added first we need to add a date property to our typescript class, and initialize it in our constructor

pipe

 src/app/recipe/recipe.component.html 
              <p>
                recipe for {{name}}, 
                added on {{dateAdded | date: 'dd/MM/yyyy': 'longDate'}} 
                // Wed Jan 03 2018 21:46:00 GMT+0100 (CET)
                // Jan 3, 2018
                // 03/01/2018
                // January 3, 2018
              </p>            
            540f065
simply outputting this date object in our html would result in a not very friendly user output by adding the default built in date pipe, the date gets formatted in a more user friendly way but you can specify how the formatting needs to happen, using typical date format strings or you can use one of the predefined formatters, which take the current locale into account next to date pipes, there are many other built in pipes (currency, numbers, lowercase, json, ...) or you can (rather easily) write your own custom pipes (we'll do that later) let's try this

built in pipes examples

TitleCasePipe Transforms Text To Title Case
CurrencyPipe € 45.10 / USD 1000 / $15
PercentPipe 26%
DecimalPipe 3.14 / 03.14000
JsonPipe { properly formatted json object }
... and many more

pass data between components

  • when creating more complex components, you typically use a bunch of other smaller components
  • often called dumb (or presentational) components, only used to show info to the user; as opposed to smart components, which contain functionality as well
  • for this to be useful, you need to be able to pass data to these embedded components, which they'll render
  • this is done using the @Input() decorator on properties

ingredient component

as an example, take our recipe's ingredients.
ingredients need more than a name, so lets create a separate component
~/recipeapp$ ng generate component ingredient

// which can be written shorter as well

~/recipeapp$ ng g c ingredient
when rendering the name of the ingredient, we want to pass it from the recipe
 src/app/ingredient/ingredient.component.ts 
                import { Component, OnInit, Input} from '@angular/core';

                @Component({
                  selector: 'app-ingredient',
                  templateUrl: './ingredient.component.html',
                  styleUrls: ['./ingredient.component.css']
                })
                export class IngredientComponent implements OnInit {
                  @Input() name : string = '' ;
                
                  constructor() {}
                
                  ngOnInit() {
                  }
                }	           
            
we added the Input import and added an @Input annotation to our variable note that we still initialize our variable, since typescript 2.7's --strictPropertyInitialization all variables must always be initialized when declared (or the latest in the constructor) I'm sure there are still spots in the course I need to update for this, if you bump into a "Property has no initializer and is not definitely assigned in the constructor.ts" error, notify me pls (or make a pull request of course ;))
first lets change the default html to show this name property
src/app/ingredient/ingredient.html
<p>
  ingredient works!
</p>
	
should become
src/app/ingredient/ingredient.html
{{name}}
	

now use this component in the recipe html.

 src/app/recipe/recipe.component.html 
              <p>
                recipe for {{name}}
              </p>
              Ingredients:
              <ul>
                <li *ngFor="let ingredient of ingredients">
                  <app-ingredient [name]="ingredient"></app-ingredient>
                </li>
              </ul>	
            2bbff86
we add the component with its tag and we set a property using square brackets [ ] note that the content of the loop variable ingredient is passed, not the string 'ingredient' let's try it out

debugging

visual studio code has a lot of (third party) extensions, we'll use the Debugger for Chrome to help us debug our Angular application

debugger extension

debugging

 .vscode/launch.json 
                {
                  "version": "0.2.0",
                  "configurations": [
                    {
                      "type": "chrome",
                      "request": "launch",
                      "name": "Launch Chrome against localhost",
                      "url": "http://localhost:8080",
                      "url": "http://localhost:4200",
                      "webRoot": "${workspaceFolder}"
                    }
                  ]
                }
            aa93285
you need a launch.json file to specify how to run the debugger, choose Debug > Start Debugging (F5) from the menu, and select the Chrome option to create a default one change the url to localhost:4200, where angular serves the app

debugging

make sure the app is running, add a breakpoint and start your debug proces

running debugger

other plugins

  • there are many other plugins, some of which are really convenient
  • I have a page where I keep a list of the plugins I use (these change, as I find new ones, or as vscode adds functionality
  • don't mindlessly install all these (if you're not a Vim user and you install the Vim plugin you're going to have a Bad Time)

summary

  • use angular-cli to create everything (ng XXX)
  • pages are built using components
  • everything is typescript, which is sort of an enhanced javascript
  • pass properties from the code to the html using {{}}
  • format data in your html using pipes
  • pass data from the html to code using [ ]
  • *ngFor to loop over a list inside your html