Author Archives: admin

Angular2 – Unit testing – introduction

Introduction

This Section only provides an Introduction to Unit Testing an Angular 2 App.

It’s NOT a detailed Guide about that neither is it a detailed Introduction to Unit Testing in general. Check out the “Further Resources” (last Lecture in this Module) to dive deeper into these topics.

Why units tests?

Captura de pantalla 2017-06-02 a las 11.48.17

Captura de pantalla 2017-06-02 a las 11.48.34

Analyzing the testing setup (as created by the CLI)

  • In order to simulate we need to run the app in the testing environment as it would be in the browser.
  • Our test runner will mount the testing environment and will read spec files to execute the tests.
  • Lets see one example
  • app/app.component.spec.ts
import { TestBed, async } from '@angular/core/testing';

import { AppComponent } from './app.component';

describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent
      ],
    }).compileComponents();
  }));

  it('should create the app', async(() => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app).toBeTruthy();
  }));

  it(`should have as title 'app'`, async(() => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app.title).toEqual('app');
  }));

  it('should render title in a h1 tag', async(() => {
    const fixture = TestBed.createComponent(AppComponent);
    fixture.detectChanges();
    const compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!!');
  }));
});
  • In the beforeEach we configure the module for our testing with the TestBed utility.
  • toBeTruthy check there is an existing app
  • In the third test we check one value in the template. For that we need to

Running Tests (with the CLI)

ng test

Adding a Component and some fitting tests

  • Lets create a new user component: ng g c user
  • Lets create a basic test for this component: check if instance is existing

app/user/user.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-user',
  templateUrl: './user.component.html',
  styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {

  user: {name: string};
  isLoggedIn = false;

  constructor() { }

  ngOnInit() {
  }

}

app/user/user.component.spec.ts

import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { UserComponent } from './user.component';

describe('UserComponent', () => {
  let component: UserComponent;
  let fixture: ComponentFixture<UserComponent>;

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

  beforeEach(() => {
    fixture = TestBed.createComponent(UserComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should be created', () => {
    expect(component).toBeTruthy();
  });
});

app/user/user.component.html

<div *ngIf="isLoggedIn">
  <h1>User logged in</h1>
  <p>User is {{ user.name }}</p>
</div>
<div *ngIf="!isLoggedIn">
  <h1>User not logged in</h1>
  <p>Please log in first</p>
</div>

 

Testing Dependencies: Components and Services

  • Lets add a service component

user.service.ts

export class UserService {
  user = {
    name: 'Max'
  };
}

user.component.spec.ts

  it('should use the user name from the service', ()=> {
    let userService = fixture.debugElement.injector.get(UserService);
    fixture.detectChanges();
    expect(userService.user.name).toEqual(component.user.name);
  });
  it('should display the user name if user is logged in', ()=> {
    component.isLoggedIn = true;
    fixture.detectChanges();
    let compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('p').textContent).toContain(component.user.name);
  });
  it('should not display the user name if user is not logged in', ()=> {
    fixture.detectChanges();
    let compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('p').textContent).not.toContain(component.user.name);
  });

 

Simulating Asyncs Tasks

  • We create a data service to simulate a remote service
  • In the test we wrap tests with async method and we use fixture.whenStable to check the assertions.

/shared/data.service.ts

export class DataService {
  getDetails() {
    const resultPromise = new Promise((resolve,reject)=>{
      setTimeout(()=> {
        resolve('Data');
      },1500);
    });
    return resultPromise;
  }
}

/user/user.component.ts

import { Component, OnInit } from '@angular/core';

import {UserService} from './user.service';
import {DataService} from '../shared/data.service';

@Component({
  selector: 'app-user',
  templateUrl: './user.component.html',
  styleUrls: ['./user.component.css'],
  providers: [UserService, DataService]
})
export class UserComponent implements OnInit {

  user: {name: string};
  isLoggedIn = false;
  data:string;

  constructor(private userService: UserService, private dataService: DataService) { }

  ngOnInit() {
    this.user = this.userService.user;
    this.dataService.getDetails().then((data:string)=> this.data = data);
  }

}

/user/user.component.spec.ts

  it('shouldnt fetch data successfully if not called asynchronously', ()=> {
    let dataService = fixture.debugElement.injector.get(DataService);
    let spy = spyOn(dataService, 'getDetails').and.returnValue(Promise.resolve('Data'));
    fixture.detectChanges();
    expect(component.data).toBe(undefined);
  });

  it('should fetch data successfully if called asynchronously', async(()=> {
    let dataService = fixture.debugElement.injector.get(DataService);
    let spy = spyOn(dataService, 'getDetails').and.returnValue(Promise.resolve('Data'));
    fixture.detectChanges();
    fixture.whenStable().then(()=> {
      expect(component.data).toBe('Data');
    });
  }));

 

Using “fakeAsync” and “tick”

  • It is another way for testing and async feature:
  it('should fetch data successfully if called asynchronously', async(()=> {
    let dataService = fixture.debugElement.injector.get(DataService);
    let spy = spyOn(dataService, 'getDetails').and.returnValue(Promise.resolve('Data'));
    fixture.detectChanges();
    fixture.whenStable().then(()=> {
      expect(component.data).toBe('Data');
    });
  }));

 

Isolated vs Non-isolated tests

  • There can be some services or pipes with functions not related to Angular. We just make simple tests for those
  • For example a reverse pipe and its test

/shared/reverse.pipe.ts

import { Pipe } from '@angular/core';
@Pipe({
  name: 'reverse'
})
export class ReversePipe {
  transform(value: string) {
    return value.split("").reverse().join("");
  }
}

/shared/reverse.pipe.spec.ts

import { ReversePipe } from './reverse.pipe';

describe('ReversePipe', () => {

  it('should be created', () => {
    let reversePipe = new ReversePipe();
    expect(reversePipe.transform('hello')).toEqual('olleh');
  });
});

 

Further resources & Where to go next

This Module only provides a brief and basic Introduction to Angular 2 Unit Tests and the Angular 2 Testing Suite. This Course isn’t focused on Testing.

If you want to dive deeper, the official Docs actually are a great place to start. There you’ll also find a Non-CLI Setup!

Official Docshttps://angular.io/docs/ts/latest/guide/testing.html

I can also recommend the following Article: https://semaphoreci.com/community/tutorials/testing-components-in-angular-2-with-jasmine

For more Information on how to run Tests with the CLI have a look at their official Docs:

=> Unit Tests: https://github.com/angular/angular-cli#running-unit-tests

=> E2E Tests: https://github.com/angular/angular-cli#running-end-to-end-tests

 

NPM as a build tool

[Fuente: https://www.keithcirkel.co.uk/how-to-use-npm-as-a-build-tool/]

Detailed info in https://docs.npmjs.com/misc/scripts

npm a great package manager, but npm has a great subset of functionality dedicated to running tasks to facilitate in a packages lifecycle – in other words, it is a great tool for build scripts.

npm Scripts

Firstly, we need to figure out how npm can manage our build scripts. As part of npm’s core, it has the

npm run-script

command (npm run for short). This command dives into your package.json and pulls out the scripts Object. The first argument passed to npm run refers to a property in the scripts objectit will execute the property’s value as a command in the operating systems default shell (usually Bash, except on Windows – but we’ll get to that later). So let’s say you have a package.json config that looks like this:

{
  "name": "myproject",
  "devDependencies": {
    "jshint": "latest",
    "browserify": "latest",
    "mocha": "latest"
  },
  "scripts": {
    "lint": "jshint **.js",
    "test": "mocha test/"
  }
}

If you run npm run lint – npm will spawn a shell and run jshint **.js. If you run npm run test, npm will spawn a shell and run mocha test/. The shell environment has your node_modules/.bin folder added to the PATH which means any of the dependencies you have that install binaries will be runnable directly – in other words, no need to put "./node_modules/.bin/jshint **.js" or "$(npm bin)/jshint **.js". If you run npm run without any arguments it gives you a list of the available commands to run, like so:

Available scripts in the user-service package:
  lint
     jshint **.js
  test
    mocha test/

Shortcut scripts

npm also provides a few convinient shortcuts. The

npm test, npm start, npm stop

 commands are all shortcuts for their run equivalents, e.g. npm test is just a shortcut for npm run test. These shortcuts are useful for 2 reasons:

  1. These are common tasks that most projects will use, and so it’s nice to not have to type as much each time.
  2. Much more importanlty – it provides a standard interface within npm for testing, starting and stopping a package. Many CI tools, such as Travis, take advantage of this behaviour by making the default command for a Node.js project npm test. It also makes introducing new developers to your project a breeze, if they know they can simply run scripts like npm test without ever having to read any docs.

Pre and Post Hooks

Another cool feature about npm is that any script that can be executed also has a set of pre- and post- hooks, which are simply definable in the scripts object.

For example, if you execute npm run lint, despite npm having no preconceived idea of what the lint task is, it will immediately run npm run prelint, followed by npm run lint, followed by npm run postlint. The same is true for any command, including npm test (npm run pretest, npm run test, npm run posttest). The pre and post scripts are also exit-code-sensitive, meaning if your pretest script exits with a non-zero exit code, then NPM will immediately stop, and not run the test and posttest scripts. You can’t pre- a pre- script though, so prepretest gets ignored.

npm also runs the pre- and post- hooks for a few internal commands:

install , uninstall, publish, update

. You can’t override the behaviours for the internal commands – but you can affect their behaviour with pre- and post- scripts. This means you can do cool stuff like:

  "scripts": {
    "lint": "jshint **.js",
    "build": "browserify index.js > myproject.min.js",
    "test": "mocha test/",

    "prepublish": "npm run build # also runs npm run prebuild",    
    "prebuild": "npm run test # also runs npm run pretest",
    "pretest": "npm run lint"
  }

Passing Arguments

Another cool feature with npm (since npm 2.0.0, at least) is passing argument sets through to the underlying tools. This can be a little complex, but here’s an example:

  "scripts": {
    "test": "mocha test/",
    "test:xunit": "npm run test -- --reporter xunit"
  }

With this config, we can simply run

npm run test

– which runs mocha test/, but we can extend it with custom parameters with a -- prefix. For example npm

run test -- anothertest.js

 will run mocha test/ anothertest.js, or more usefully

npm run test -- --grep parser

will expand to mocha test/ --grep parser (which runs only the tests with “parser” in the title). In the package.json we have test:xunit which effectively runs mocha test --reporter xunit. This setup can be incredibly useful for composing commands together for some advanced configurations.

NPM Config Variables

One last thing that is worth mentioning – npm has a config directive for your package.json. This lets you set arbitrary values which can be picked up as environment variables in your scripts. Here’s an example:

  "name": "fooproject",
  "config": {
    "reporter": "xunit"
  },
  "scripts": {
    "test": "mocha test/ --reporter $npm_package_config_reporter",
    "test:dev": "npm run test --fooproject:reporter=spec"
  }

Here, the config object has reporter property – set to 'xunit'. All config options are exposed as environment variables prefixed with npm_package_config_ (which, admittedly, does make the variable names a mouthful). In the above example, the npm run test command uses the $npm_package_config_reporter variable which gets expanded to mocha test/ --reporter xunit. This can get replaced in two convinient ways:

  1. Just like the test:dev task, you can change the reporter config variable to spec– by using
    --fooproject:reporter

    . You’d replace fooproject with your project’s name, and reporter with the config variable to override.

  2. They can also be overriden as part of a user’s config. By running
    npm config set fooproject:reporter spec

    – an entry in my ~/.npmrc appears (fooproject:reporter=spec) which is read at runtime, and overrides the npm_package_config_reporter variable, meaning that on my local machine, forever more, npm run test gets expanded to mocha test/ --reporter spec. I can remove my personalised setting for this using npm config delete fooproject:mocha_reporter. A good set up for this is to have some sensible defaults in your package.json – but your own customisations can be tucked away inside your own ~/.npmrc.

Now, if I’m totally honest I’m not in love with the way this works. While the setup seems trivial (having a "config" object in your JSON), trying to use them is seems too verbose and complicated. I would also like a simpler way to override package configs, without specifying the package’s name – it’d be awesome if standard conventions could come into place where I could set my favourite mocha reporter for all packages in my ./.npmrc.

The other downside to these configs is that they’re not very Windows friendly – Windows uses % for variable substitution, while bash uses $. They work fine if you use them within a Node.js script, but if anyone knows of a way to get them working in Windows via the shell commands, let me know!

The Windows Problem

Let’s get something out of the way before we progress. Because npm is reliant on the operating systems shell to run scripts commands, they can quickly become unportable. While Linux, Solaris, BSD and Mac OSX come preinstalled with Bash as the default shell, Windows does not. On Windows, npm will resort to using Windows command prompt for these things.

Frankly, this is less of a problem than it seems. A good chunk of syntax that works in Bash will also work in Windows command prompt the same way:

  • && for chaining tasks
  • & for running tasks simaltaneously
  • < for inputting the contents (stdin) of a file to a command
  • > for redirecting output (stdout) of a command and dumping it to a file
  • | for redirecting output (stdout) of a command and sending it to another command

The biggest problems between the two is the availability and naming of commands (e.g. cp is COPY in Windows) and variables (Windows uses % for variables, Bash $). I feel like both of these are completely surmountable problems:

  1. Rather than relying on built in commands, you could simply use alternatives – for example instead of using rm, use the npm rimraf package.
  2. Rather than trying to use syntax that is not cross compatible, stick to just the above ones. You’d be surprised just how much you can get done with just &&, >, |, and <. Variables are for suckers anyway.

Replacing build tools

Ok, lets get to the brass tacks of this post. If we want to replace build tools like Grunt or Gulp, we need like-for-like replacements for plugins and features of these tools. I’ve taken the most popular tasks & paradigms from various projects, and questions from commenters of my last post and demonstrated how to do them in npm:

Using multiple files

I had a few people responding to my last post, saying the benefit of task runners is their ability to handle multiple files in tasks using file “globs” which look like *.js, *.min.css or assets/*/*. This feature was actually inspired from Bash, which in turn was inspired from the glob command from Unix in 1969. The shell will automatically look at a command line arguments such as *.js and expand the stars out as wildcards. Using two stars allows it to search recursively. If you’re on a Mac or Linux machine, try opening up your shell and playing with it (try something like ls *.js).

Now, the problem herein lies that, of course, the Windows command line does not have this functionality. Luckily, when given a command line argument like *.js – Windows passes it verbatum to the application, meaning that tool vendors can install compatibility libraries to give Windows glob like functionality. Many, many tools on npm do; the two most popular glob libraries, minimatch and glob, share 1500 dependents, including JSHint, JSCS, Mocha, Jade, Stylus, Node-Sass… the list goes on.

This means you can just use file globs within npm scripts, like so:

"devDependencies": {
  "jshint": "latest"
},
"scripts": {
  "lint": "jshint *.js"
}

Running multiple tasks

Grunt, Gulp etc all have the capability of tying multiple tasks up together to make one single task – typically useful for building or testing. With npm you have two options here – depending on which one is semantically the right fit. You can either use the pre- or post- hooks – which are a good fit if the task is a prerequisite thing (i.e concating js before minfiying it), or you can use the bash && operator – like so:

"devDependencies": {
  "jshint": "latest",
  "stylus": "latest",
  "browserify": "latest"
},
"scripts": {
  "lint": "jshint **",
  "build:css": "stylus assets/styles/main.styl > dist/main.css",
  "build:js": "browserify assets/scripts/main.js > dist/main.js",
  "build": "npm run build:css && npm run build:js",
  "prebuild:js": "npm run lint"
}

In the above example build will execute both build:css and build:js – but not before running the lint task. Using this pattern you can also run the build:css or build:js tasks separately, and build:js will also run lint beforehand. Tasks can be composed and chained like this as much as you like, and it is all Windows compatible.

Streaming to multiple tasks

One of Gulp’s biggest features is that it streams the output seamlessly from one task to the next (as opposed to Grunt which constantly dips in and out of the filesystem). Bash and the Windows command line have the pipe operator (|), which can stream one command’s output (stdout) and send it to another command’s input (stdin). Let’s say you want to run all of your CSS first through Autoprefixer, then CSSMin, then output to a file (using the > operator, which outputs stdout to a given file):

"devDependencies": {
  "autoprefixer": "latest",
  "cssmin": "latest"
},
"scripts": {
  "build:css": "autoprefixer -b 'last 2 versions' < assets/styles/main.css | cssmin > dist/main.css"
}

As you can see autoprefixer adds the CSS vendor prefixes to our CSS, which is then piped to cssmin which minifies the output – then the whole thing gets dumped into dist/main.css. Most good tools will support stdin and stdout and the above code is fully compatible with Windows, Mac and Linux.

Version Bumping

Version bumping is a popular Grunt or Gulp task. Effectively it increments the version number up by one inside the package.json, makes a git commit, and tags said commit.

This actually comes baked into npm (it is a package manager after all). Simply run

npm version patch

to increment the patch number (e.g. 1.1.1 -> 1.1.2),

npm version minor

to increment the minor version number (e.g. 1.1.1 -> 1.2.0) or

npm version major

(e.g. 1.1.1 -> 2.0.0). It’ll commit and tag up your package for you, all that is left is to git push and npm publish.

This can be fully customised too. For example, if you don’t want it running git tag, simply run it with the --git-tag-version=false flag (or set it to permanently not with npm config set git-tag-version false). Want to configure the commit message? Simply run it with the -m flag, e.g. npm version patch -m "Bumped to %s" (set it permanently with npm config set message "Bumped to %s"). You can even get it to sign the tags for you, by running with the --sign-git-tag=true flag (or, once again, set it permanently with npm config set sign-git-tag true).

Clean

Many build runners come with a clean task. This task usually just removes a bunch of files so you can start with a fresh working copy to start building into. Well, turns out that Bash has a pretty good clean command all by itself: rm. Passing the -r (recursive) flag lets rm remove directories too! It couldn’t be simpler:

"scripts": {
  "clean": "rm -r dist/*"
}

If you really need to have Windows support, it does not support rm – luckily there is rimraf which is a cross-compatible tool to do the same thing:

"devDependencies": {
  "rimraf": "latest"
},
"scripts": {
  "clean": "rimraf dist"
}

Compiling files to unique names

Effectively, trying to replace the functionality of gulp-hash and grunt-hashtake an input of JS and name it with the hash of its contents. This one turned out to be really complex to do using existing command line tools, so I had a look on npm to see if anything fit the bill, and it didn’t – so I wrote one (I can hear the Grunt/Gulp proponents telling me I cheated already). I have two points about this – firstly, it’s pretty disappointing to see lots of siloed efforts from various plugin authors – and no generic solutions that work with any build tool. Secondly – if you can’t find something that fits, write your own! My hashmark library clocks in around the same lines of code as the grunt/gulp versions, and has a similar or better featureset, depending on the plugin – mine even supports streaming! Going back to the autoprefixer example, we can output a file with a specific hash using pipes:

"devDependencies": {
  "autoprefixer": "latest",
  "cssmin": "latest"
},
"scripts": {
  "build:css": "autoprefixer -b '> 5%' < assets/styles/main.css | cssmin | hashmark -l 8 'dist/main.#.css'"
}

Now the ouput of build:css will ouput a file in dist named with a hash, such as dist/main.3ecfca12.css.

Watch

This is definitely the most popular reason why people using Grunt/Gulp, and by far the most requested example from comments around my previous post. A lot of these build tools come with commands for watching a filesystem, detecting changes to files (e.g. from saving), and then reloading the server/recompiling assets/rerunning tests. Very useful for rapid development. It seems like most developers replying to my last post simply assumed that this wasn’t an option outside of Grunt/Gulp (or perhaps thought it was something too difficult to do without them).

Well, most tools facilitate this option themselves – and usually are much more in tune with the intricacies of the files that should be listened for. For example Mocha has the -w option, as does Stylus, Node-Sass, Jade, Karma, and others. You could use these options like so;

"devDependencies": {
  "mocha": "latest",
  "stylus": "latest"
},
"scripts": {
  "test": "mocha test/",
  "test:watch": "npm run test -- -w",

  "css": "stylus assets/styles/main.styl > dist/main.css",
  "css:watch": "npm run css -- -w"
}

Of course, not all tools support this, and even when they do – you might want to compose multiple compile targets into one task which watches for changes and runs the whole set. There are tools that watch files and execute commands when files change, for example watch, onchange, dirwatch, or even nodemon:

"devDependencies": {
  "stylus": "latest",
  "jade": "latest",
  "browserify": "latest",
  "watch": "latest",
},
"scripts": {
  "build:js": "browserify assets/scripts/main.js > dist/main.js",
  "build:css": "stylus assets/styles/main.styl > dist/main.css",
  "build:html": "jade assets/html/index.jade > dist/index.html",
  "build": "npm run build:js && npm run build:css && npm run build:html",
  "build:watch": "watch 'npm run build' .",
}

There you go – pretty painless. This 13 lines of JSON will watch our whole project directory, and build HTML, CSS and JS assets every time any file changes. Just run npm run build:watch and start developing! You could even optimise this further, with a little tool I wrote (once again, while writing this post): Parallelshell, which will keep multiple processes running at one time – a little like this:

"devDependencies": {
  "stylus": "latest",
  "jade": "latest",
  "browserify": "latest",
  "watch": "latest",
  "parallelshell": "latest"
},
"scripts": {
  "build:js": "browserify assets/scripts/main.js > dist/main.js",
  "watch:js": "watch 'npm run build:js' assets/scripts/",
  "build:css": "stylus assets/styles/main.styl > dist/main.css",
  "watch:css": "watch 'npm run build:css' assets/styles/",
  "build:html": "jade index.jade > dist/index.html",
  "watch:html": "watch 'npm run build:html' assets/html",
  "build": "npm run build:js && npm run build:css && npm run build:html",
  "build:watch": "parallelshell 'npm run watch:js' 'npm run watch:css' 'npm run watch:html'",
}

Now running npm run build:watch will run the individual watchers all through Parallelshell and if, for example, you only change the CSS, then only the CSS will recompile. If you change the JS then only the JS will recompile and so on. Parallelshell combines the outputs (stdout and stderr) of each of the tasks, and will listen to the exit code to ensure logs and failed builds propagate out (unlike the Bash/Windows & operator).

LiveReload

LiveReload was another popular one. If you don’t know what LiveReload is – its a combination of command line tool and browser extension (or custom server) – as files change, LiveReload triggers the page you’re looking at to reload meaning you never have to press refresh. The npm package live-reload is a pretty suitable command line client for this – it runs a server which only serves a JS file, which if you include on your page will notify the page of changes. Simple, yet effective. Here’s an example of how to get it working:

"devDependencies": {
  "live-reload": "latest",
},
"scripts": {
  "livereload": "live-reload --port 9091 dist/",
}
<!-- In your HTML file -->
<script src="//localhost:9091"></script>

Now running npm run livereload – when you visit the HTML page it’ll start listening to the livereload server. Any changes to files in the dist/ directory will notifiy clients, and the page will be reloaded.

Running tasks that don’t come with binaries

It was pointed out to me that there are libs that don’t come with binaries – such as favicon – and so Grunt/Gulp plugins can be useful because they wrap the tools so they can be used within the task runners. If you find a package that you want to use, but it doesn’t have a binary then simply write some JavaScript! You would have to if using Grunt or Gulp, so don’t be afraid to just chuck a bit of JavaScript somewhere that wires it all up (or even better, submit a PR to the maintainers convincing them to support a command line interface!):

// scripts/favicon.js
var favicons = require('favicons');
var path = require('path');
favicons({
    source: path.resolve('../assets/images/logo.png'),
    dest: path.resolve('../dist/'),
});
"devDependencies": {
  "favicons": "latest",
},
"scripts": {
  "build:favicon": "node scripts/favicon.js",
}

A fairly complex config

In my previous post many were telling me I was missing the point about task runners – they’re for wiring up complex sets of tasks, not just running odd tasks. So I thought I’d wrap up this piece with a complex set of tasks typical of a multi-hundred-line Gruntfile. For this example I want to do the following:

  • Take my JS and lint, test & compile it into 1 versioned file (with a separate sourcemap) and upload it to S3
  • Compile Stylus into CSS, down to a single, versioned file (with separate sourcemap), upload it to S3
  • Add watchers for testing and compilation
  • Add a static file server to see my single page app in a web browser
  • Add livereload for CSS and JS
  • Have a task that combines all these files so I can type one command and spin up an environment
  • For bonus points, open a browser window automagically pointing to my website

I’ve chucked up a simple repository on GitHub called npm-scripts-example. It contains the layout for a basic website, and a package.json to fit the above tasks. The lines you’re probably interested in are:

  "scripts": {
    "clean": "rimraf dist/*",

    "prebuild": "npm run clean -s",
    "build": "npm run build:scripts -s && npm run build:styles -s && npm run build:markup -s",
    "build:scripts": "browserify -d assets/scripts/main.js -p [minifyify --compressPath . --map main.js.map --output dist/main.js.map] | hashmark -n dist/main.js -s -l 8 -m assets.json 'dist/{name}{hash}{ext}'",
    "build:styles": "stylus assets/styles/main.styl -m -o dist/ && hashmark -s -l 8 -m assets.json dist/main.css 'dist/{name}{hash}{ext}'",
    "build:markup": "jade assets/markup/index.jade --obj assets.json -o dist",

    "test": "karma start --singleRun",

    "watch": "parallelshell 'npm run watch:test -s' 'npm run watch:build -s'",
    "watch:test": "karma start",
    "watch:build": "nodemon -q -w assets/ --ext '.' --exec 'npm run build'",

    "open:prod": "opener http://example.com",
    "open:stage": "opener http://staging.example.internal",
    "open:dev": "opener http://localhost:9090",

    "deploy:prod": "s3-cli sync ./dist/ s3://example-com/prod-site/",
    "deploy:stage": "s3-cli sync ./dist/ s3://example-com/stage-site/",

    "serve": "http-server -p 9090 dist/",
    "live-reload": "live-reload --port 9091 dist/",

    "dev": "npm run open:dev -s & parallelshell 'npm run live-reload -s' 'npm run serve -s' 'npm run watch -s'"
  }

(If you’re wondering what the -s flag is, it just silences output from npm on those tasks, cleaning up the log output, try disabling them to see the difference)

To do the equivalent in Grunt, it’d take a Gruntfile of a few hundred lines, plus (my finger in the air estimate) around 10 extra dependencies. It’s certainly subjective as to which version would be more readable – and while npm is certainly not the holy grail of readability, I personally think the npm scripts directive is easier to reason about (i.e. I can see all tasks and what they do, at a glance).

Conclusion

Hopefully this article shows you how capable npm can be as a build tool. Hopefully it has demonstrated to you that tools like Gulp and Grunt should not always be the first thing to jump to in a project, and that tools you probably already have on your system are worth investigating.

As always, feel free to discuss this with me on The Twitter, I’m @keithamus, you can “Follow” me there too, apparently.

Angular2 – Angular animations

Making Animations work with Angular 4

With the release of Angular 4, the general syntax of Angular Animations didn’t change.

However, the animation functions were moved into their own package and you now also need to add a special module to your imports[]  array in the AppModule.

Specifically, the following adjustments are required:

  • You probably need to install the new animations package (running the command never hurts):
    npm install --save @angular/animations
  • Add the BrowserAnimationsModule  to your imports[]  array in AppModule
  • This Module needs to be imported from @angular/platform-browser/animations'  =>
    import { BrowserAnimationsModule } from '@angular/platform-browser/animations'

    (in the AppModule!)

  • You then import trigger , state , style  etc from @angular/animations  instead of @angular/core

That’s all!

Introduction

  • Angular comes with a built-in animation module.
  • It is hard to handle transition animation with only CSS in cases of render items in a list or conditional display of DOM elements . Thats why Angular provides this module.

Setting up the starting project

  • We will start putting animation on a box below the buttons
  • We will define states from where the transition will start and will end to.

Animations triggers and state and switching between states

  • We will change the box from red to blue and move it to the right on the click of Animate button.

app.component.html

    <div class="col-xs-12">
      <h1>Animations</h1>
      <button class="btn btn-primary" (click)="onAnimate()">Animate!</button>
      <button class="btn btn-primary" (click)="onShrink()">Shrink!</button>
      <hr>
      <div [@divState]="state" style="width:100px; height: 100px;"></div>
    </div>

app.component.ts

import { Component } from '@angular/core';

import { trigger, state, style} from '@angular/animations';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  animations: [
    trigger('divState',[
      state('normal', style({
        'background-color':'red',
        transform: 'translateX(0)'
      })),
      state('highlighted', style({
        'background-color':'blue',
        transform: 'translateX(100px)'
      }))
    ]
  )
  ]
})
export class AppComponent {
  state = 'normal';

	list = ['Milk', 'Sugar', 'Bread'];

	onAnimate() {
    this.state === 'normal' ? this.state = 'highlighted' : this.state = 'normal';
  }
	onAdd(item) {
		this.list.push(item);
	}

	onDelete(item) {
		this.list.splice(this.list.indexOf(item), 1);
	}
}

Transitions

  • We will add now a transition between the two states, in one direction and in the reverse.
  • Wit transition we define animation from one state to other and the duration as an argument of animate method (in milliseconds).

app.component.ts

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  animations: [
    trigger('divState',[
      state('normal', style({
        'background-color':'red',
        transform: 'translateX(0)'
      })),
      state('highlighted', style({
        'background-color':'blue',
        transform: 'translateX(100px)'
      })),
      transition('normal => highlighted', animate(300)),
      transition('highlighted => normal', animate(800)),
    ]
  )
  ]
})

Advanced transitions

  • One simple way to define animation in both directions is with double arrow:
      transition('normal <=> highlighted', animate(300)),
  • We can use wildcards(*) to define transition from one state to any other:
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  animations: [
    trigger('divState',[
      state('normal', style({
        'background-color':'red',
        transform: 'translateX(0)'
      })),
      state('highlighted', style({
        'background-color':'blue',
        transform: 'translateX(100px)  scale(1)'
      })),
      state('chiquito', style({
        'background-color':'green',
        transform: 'translateX(0) scale(0.5)'
      })),
      transition('normal <=> highlighted', animate(300)),
      transition('chiquito <=> *', animate(300)),
    ]
  )
  ]
})

Transition Phases

  • Now we can insert in-between animations. For instance , lets put an animation to border-radius 50px. To avoid hard step at the end we will add another animate method to transition to the end state.
  • We will pass an array of animations to the transition method
  • The first element of the array will be a starting style (background orange)
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  animations: [
    trigger('divState',[
      state('normal', style({
        'background-color':'red',
        borderRadius: '0px',
        transform: 'translateX(0)'
      })),
      state('highlighted', style({
        'background-color':'blue',
        borderRadius: '0px',
        transform: 'translateX(100px)  scale(1)'
      })),
      state('chiquito', style({
        'background-color':'green',
        transform: 'translateX(0) scale(0.5)'
      })),
      transition('normal <=> highlighted', animate(300)),
      transition('chiquito <=> *', [
        style({
          'background-color':'orange'
        }),
        animate(1000, style({
          borderRadius: '50px'
        })),
        animate(500)
      ]),
    ]
  )
  ]
})

 

The “void” state

  • Regarding the cases of adding / deleting items in a list we can animate those actions with the void state.
  • Let animate adding items by making the new items coming from the left and setting opacity from 0 to 1.
  • And lets animate deleting items by making deleted items disappearing to the right
  • We define a new trigger ‘list1’ to add into the <li> elements
  • We don’t bind any variable of state in this case to the trigger. We will use predefined internal states for lists.
  • ‘in’ state is a predefined state for elements inserted to the list
  • ‘void’ state is the state for elements to be added.

app.component.ts

    trigger('list1', [
      state('in', style({
        opacity: 1,
        transform: 'translateX(0)'
      })),
      transition('void => *', [
        style({
          opacity:0,
          transform: 'translateX(-100px)'
        }),
        animate(300)
      ]),
      transition('* => void', [
        animate(300, style({
          opacity:0,
          transform: 'translateX(100px)'
        }))
      ])
    ]),

app.component.html

      <ul class="list-group">
        <li
          class="list-group-item"
          [@list1]
          (click)="onDelete(item)"
          *ngFor="let item of list">
          {{ item }}
        </li>
      </ul>

 

Using Keyframes for animations

  • In case we want to set animations during the transition in specific steps we use keyframes.
  • In this example we will define adding item action to the list with keyframes
  • Lets define a new trigger ‘list2’ for that:
    trigger('list2', [
      state('in', style({
        opacity: 1,
        transform: 'translateX(0)'
      })),
      transition('void => *', [
        animate(1000, keyframes([
          style({
            transform:'translateX(-100px)',
            opacity:0,
            offset:0
          }),
          style({
            transform:'translateX(-50px)',
            opacity:0.5,
            offset:0.3
          }),
          style({
            transform:'translateX(-20px)',
            opacity:1,
            offset:0.8
          }),
          style({
            transform:'translateX(0px)',
            opacity:1,
            offset:1
          })
        ]))
      ]),

Grouping transitions

  • Defining multiple animate methods in a array will execute sequentially.
      transition('* => void', [
        animate(1000, style({
          color:'red'
        })),
        animate(800, style({
          opacity:0,
          transform: 'translateX(100px)'
        }))
      ])
  • But if we want multiple animate methods occurring at the same time (in parallel) we can use group method:
      transition('* => void', [
        group([
          animate(800, style({
            color:'red'
          })),
          animate(1000, style({
            opacity:0,
            transform: 'translateX(100px)'
          }))
        ])
      ])

Using animation callbacks

app.component.html

      <div
        [@divState]="state"
        (@divState.start)="animationStarted($event)"
        (@divState.done)="animationEnded($event)"
        style="width:100px; height: 100px;"
      ></div>

app.component.ts

  animationStarted(event) {
    console.log(event);
  }
  animationEnded(event) {
    console.log(event);
  }

the result is:

Captura de pantalla 2017-06-01 a las 23.14.25

Angular2 – Deploying an Angular App

Introduction

Deployment preparations and important steps

Captura de pantalla 2017-05-29 a las 11.11.09

  • Build you app for production (—prod  –aot)
  • Set a correct <base> element
  • Redirect all 404 errors to return index.html where Angular launch. Routes defined in Angular (such as /recipes)  are not configured in the server, so it will return a 404 error by default. So you will need to set up your server to redirect to index.html in these cases.

Example: Deploying to AWS S3

  • Create an Amazon account: https://console.aws.amazon.com
  • Create a bucket in S3
  • Set up index and error documents pointing to index.html to make sure Angular app is linked in every route. In S3 bucket this is done in Properties -> Static Web hosting. For deployment in php hosting create an .htaccess file:
ErrorDocument 404 http://jesidea.com/jesites/src/angular2-recipe-book/

RewriteEngine On
RewriteBase /
  • Set up permissions for that bucket for anonymous access: create a bucket policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AddPerm",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::jesidea-ng-recipe-book/*"
        }
    ]
}
  • Build for production:

Taking care of the base folder where it will be deployed (base-href in index.html):

ng build --pro --aot --base-href http://jesidea.com/jesites/src/angular2-recipe-book/

or without taking care:

ng build --pro --aot

 

Angular2 – Using Angular Modules & Optimizing Apps

The idea behind modules

Captura de pantalla 2017-05-24 a las 15.34.54

  • Using Modules in Angular is a way of doing easier to maintain an large app.
  • We will modify our recipe book app to use multiple modules.
  • NOTE: The imports in a component or service are not related to modules, it is a JS stuff to work.

Understanding the App Module

  • Lets see the meaning of the properties of the NgModule in the app.module.ts
@NgModule({
  declarations: [
...
  imports: [
...
  providers: [ShoppingListService,RecipesService, DataStorageService, AuthService, AuthGuard],
  bootstrap: [AppComponent]
})
  • declarations : component , directives or pipes this module use.
  • imports: other modules this module use (some Angular built in modules). When we import another module what that module exports. For example FormsModule export a set of directives , and importing it we can use all of that directives.
  • providers: which service we are using in this module. Inject here will be provided for the whole app.
  • bootstrap: whats our root component.

Understanding Features Modules

  • It is about put all classes relate to a feature inside a Module, in our example Recipes Module can be a candidate.

Creating a Recipe Feature Module

  • We create a recipe/recipe.module.ts to move all recipe related components to the new feature module.
  • We also move DropdownDirective and ReactiveFormsModule into recipe.module.ts because these only been used in the recipes module.
  • BrowserModule needs to be imported only in the app.module. It contains all elements of CommonModule and additional stuff to start the app. But use CommonModule for common directives in the others modules.
  • IMPORTANT: You must not declare the same component in two different modules.

recipe/recipe.module.ts

import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { RecipeListComponent } from './recipe-list/recipe-list.component';
import { RecipeItemComponent } from './recipe-item/recipe-item.component';
import { RecipeDetailComponent } from './recipe-detail/recipe-detail.component';
import { RecipesComponent } from './recipes.component';
import { RecipeStartComponent } from './recipe-start/recipe-start.component';
import { RecipeEditComponent } from './recipe-edit/recipe-edit.component';

@NgModule({
  declarations: [
    RecipesComponent,
    RecipeListComponent,
    RecipeItemComponent,
    RecipeDetailComponent,
    RecipeStartComponent,
    RecipeEditComponent
  ],
  imports: [
    CommonModule,
    ReactiveFormsModule
  ]
})

export class RecipeModule {}

But now we have a problem with the routing.  Recipe module doesn’t have access to the AppRoutingModule, it is in the app.module and don’t travel down to the recipe.module.

We will resolve it in the next point.

NOTE: We don’t move RecipeService from app.module because it is used in others parts of the app

NOTE: Don’t forget to import CommonModule in recipe.module. It gives you access to the common directives: ngClass , ngFor, etc

Registering routes in Features Modules

  • The problem we havce is we are using router-outlet in some of the templates in recipe module but we are declaring AppRoutingModule.
  • The solution is to create recipe/recipe-routing.module.ts for get all root configuration from the parent component and add all recipe relate routes.
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/Router';
import { RecipesComponent } from './recipes.component';
import { RecipeStartComponent } from './recipe-start/recipe-start.component';
import { RecipeDetailComponent } from './recipe-detail/recipe-detail.component';
import { RecipeEditComponent } from './recipe-edit/recipe-edit.component';
import { AuthGuard } from '../auth/auth-guard.service';

const recipeRoutes: Routes = [
  { path: 'recipes', component: RecipesComponent, children: [
    { path: '', component: RecipeStartComponent},
    { path: 'new', component: RecipeEditComponent, canActivate: [AuthGuard]},
    { path: ':id', component: RecipeDetailComponent},
    { path: ':id/edit', component: RecipeEditComponent, canActivate: [AuthGuard]}
  ]},
];

@NgModule({
  imports: [
    RouterModule.forChild(recipeRoutes)
  ],
  exports: [RouterModule]

})
export class RecipeRoutingModule {
}
  • The key thing is to use forChild instead of forRoot (only in the app routing) — IMPORTANT
    RouterModule.forChild(recipeRoutes)

for link the new recipe routes.

And reference it from recipe/recipe.module.ts

...
import {RecipeRoutingModule} from './recipe-routing.module';

...

  ],
  imports: [
    CommonModule,
    ReactiveFormsModule,
    RecipeRoutingModule
  ]
  • Now we have another issue: DropdownDirective is not working in the recipe module
  • IMPORTANT: A Module can only use components in its declaration and items imported in “imports”

Understanding Shared Modules

  • We will need to move DropdowDirective to a Shared Module

Captura de pantalla 2017-05-25 a las 13.58.59

Creating a Shared Module

shared/shared.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { DropdownDirective } from './dropdown.directive';

@NgModule({
  declarations: [
    DropdownDirective
  ],
  exports: [
    CommonModule,
    DropdownDirective
  ]
})

export class SharedModule {}

Creating a Shopping List Feature Module

  • We create two new features modules: auth/auth.module.ts and shopping-list/shopping-list.module.ts.

Loading components via Selectors vs Routing

  • We cannot use app-shopping-list selector in app.component.html because it is not declared as part of the app.module.ts.
  • IMPORTANT: only use selectors declared in the same module as the template.
  • But with routes it is different. We can declare different routing files and use them anywhere if there is any place in the app where has been declared.

A Common Gotcha

  • Make sure to import FormsModule in the modules where you are using forms directives such as ng-submit,etc,

Creating the Auth Feature Module

 

Understanding Lazy Loading

Captura de pantalla 2017-05-25 a las 15.24.23

  • Lazy loading is about don’t load all the features modules at the beginning. It can happen user won’t visit some feature never so all the components related to that feature are not needed.

 

Adding Lazy Loading to the recipes module

  • We want Recipes Module to be lazily loaded.
  • The first thing to do is create a home/home.component to set it up as the entrance point of our app instead of been redirected to the recipes.
  • Then we remove RecipeModule reference from the app.module.
  • Then we modify routes in the app-routing.module.ts:
const appRoutes: Routes = [
  { path: '', component: HomeComponent},
  { path: 'recipes', loadChildren: './recipes/recipe.module#RecipeModule'}
];
  • With the “loadChildren” option we reference what module we want to load as the user visit ‘/recipes’

NOTE: in the recipe-routing.module.ts we now have:

const recipeRoutes: Routes = [
  { path: '', component: RecipesComponent, children: [
    { path: '', component: RecipeStartComponent},
    { path: 'new', component: RecipeEditComponent, canActivate: [AuthGuard]},
    { path: ':id', component: RecipeDetailComponent},
    { path: ':id/edit', component: RecipeEditComponent, canActivate: [AuthGuard]}
  ]},
];
  • We can check in the console how lazy load some “chunks” of our app since we click in recipes:

Captura de pantalla 2017-05-25 a las 16.09.42

Protecting Lazy Load Routes with canLoad

What if you want to use route protection (canActivate  to be precise) on lazily loaded routes?

You can add canActivate to the lazy loaded routes but that of course means, that you might load code which in the end can’t get accessed anyways. It would be better to check that BEFORE loading the code.

You can enforce this behavior by adding the canLoad  guard to the route which points to the lazily loaded module:

{ path: 'recipes', loadChildren: './recipes/recipes.module#RecipesModule', canLoad: [AuthGuard] }

In this example, the AuthGuard  should implement the CanLoad interface.

 

How Modules and Services work together

  • Services (LogService for example) can be injected in the Lazy Loaded modules automatically without need to specify a providers array. This is because lazy modules use the root injector which can be referenced from the whole app.

Captura de pantalla 2017-05-25 a las 16.35.20

  • Let see another use case: the lazy module has its own provider as the other modules. Then the Lazy Load Module will create a child injector and the LogService will have another instance different from the one at app level:

Captura de pantalla 2017-05-25 a las 16.40.27

  • Keep in mind : when providers array it is declared on a lazy module a child injector will be created
  • And if we want to have a LogService only for a module (Module scope) then use the providers array in the component instead of the module
  • A third use case can be use a shared module to give the LogService instance to the lazy module:

Captura de pantalla 2017-05-25 a las 16.44.09

  • But in this case a child injector will be created too, because lazy module is importing a module which has a providers array.

IMPORTANT: Don’t provide services in shared modules. Especially not , if you plan to use them in lazy loaded modules

Understanding the Core Module

  • Now we want to leave the app.module as lean as is possible. So let see what components belong to the core of the app: Header and Home.

Creating a Basic Core Module

  • We create a core folder and we put inside header and home folders.
  • We fix routes
  • We create a core.module.ts:

core/core.module.ts

import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';

import { HomeComponent } from './home/home.component';
import { HeaderComponent } from './header/header.component';

import { SharedModule } from '../shared/shared.module';

import { AppRoutingModule } from '../app-routing.module';

@NgModule({
  declarations: [
    HomeComponent,
    HeaderComponent
  ],
  imports: [
    SharedModule,
    AppRoutingModule
  ],
  exports: [
    HeaderComponent,
    AppRoutingModule
  ]
})

export class CoreModule {}

 NOTE : in the core.module we export Header component because we use the app-header selector in the app.component.html. But we don’t need to export HomeComponent because is only used in routes and in this case is visible from the whole app.

Restructuring services to use the child injector

  • We continue doing leaner the app.module.
  • This time we are moving all the services in the providers array to the core.module. These services will still be available in the whole app because the core module is loaded at the beginning si it is in the Root injector.
  • The AuthGuard service is only used in recipe module so we can define it only in that module.

core/core.module.ts

  providers: [ShoppingListService,RecipesService, DataStorageService, AuthService],

recipe/recipe.module.ts

  providers: [AuthGuard]

NOTE: Adding the AuthGuard service only to the recipe module we are improving the speed of the app in the bootstrap as Recipe module is lazy loaded.

Using Ahead-of-time compilation

  • This is NOT related to compilation from Typescript to Javascript.
  • This is about Angular needs to compile HTML templates to convert them to Js objects
  • This step can be done before the app is released (without loosing the fact of some templates are dynamic).

Captura de pantalla 2017-05-25 a las 17.58.01

  • Using AoT has advantages:
    • Faster startup since Parsing and Compilation doesn’t happen in the browser
    • Templates get checked during development: all the console errors will trigger in the build process not in the user browser.
    • Smaller file size as unused features can be stripped out and the Compiler itself isn’t shipped.

 

How to use AoT compilation with the CLI

  • The CLI command to run on terminal is:
ng build --pro --aot
  • The –pro flag is top minify
  • The –aot flag is for Ahead of Time compilation
  • Some errors can be triggered with this command you should resolve to get it completed.
  • Once the command is completed a dist folder will be generated and you can check size of the app files: main.js and vendor.js and compare it with the sizes in the chrome console on development time.

Preloading Lazy Loading routes

  • We can preload all the lazy loading modules , not at the time the app start but during user is using the app.
  • In the app-routing.module.ts, we need to specify the preloadingStrategy:
@NgModule({
  imports: [
    RouterModule.forRoot(appRoutes, {preloadingStrategy: PreloadAllModules})
  ],
  exports: [RouterModule]
})
  • Then in debug console –> network, we can see how chunk file is loaded after the main and vendor js files

Captura de pantalla 2017-05-26 a las 10.03.15

 

Angular2 – Authentication and Route protection

Module introduction

  • We want allow only logged in users to modify recipes

How Authentication works in SPAs

  • In SPAs authentication is managed by tokens

Captura de pantalla 2017-05-23 a las 19.14.33

More about jWT

Creating a signup page and route

  • We create a Sign up form page and his route “/signup”. All associated to a auth/signup.component

Setting up the firebase SDK

  • In firebase console we need to enable basic authentication in the “Authentication” menu option.
  • We need to install firebase npm package
sudo npm install firebase --save

 NOTE: In a normal backend you can have a available endpoint to retrieve a valid token but firebase hasn’t.

  • Create an auth/auth.service.ts with a signupUser method
  • Initialize firebase credentials in a app level: app.component.ts
...
import * as firebase from 'firebase';
...
export class AppComponent implements OnInit{
...
  ngOnInit() {
    firebase.initializeApp({
      apiKey: "AIzaSyCqHQQ7gtNxkERmJzTBDfkn_Sq_2LeY9qc",
      authDomain: "ng-recipe-book-5333b.firebaseapp.com"
    });
  }

 

Signing Users Up

auth.service.ts

import { Injectable } from '@angular/core';
import * as firebase from 'firebase';


@Injectable()
export class AuthService {

  signupUser(mail: string, password:string) {

    firebase.auth().createUserWithEmailAndPassword(mail, password)
    .catch(
      error => console.log(error)
    );

  }
}

signup.component.ts

  onSignup(form: NgForm) {
    const mail = form.value.email;
    const password = form.value.password;

    this.authService.signupUser(mail, password);

  }

Signing Users In

  • We create /signin route and Signin component calling signinUser method on auth service

auth.service.ts

  signinUser(mail: string, password:string) {

    firebase.auth().signInWithEmailAndPassword(mail, password)
    .then(
      response => console.log(response)
    )
    .catch(
      error => console.log(error)
    );

  }
  • Firebase store i the browser information of logged user. You can check it in console -> Application –> Local Storage
  • There it is the accessToken we will using for make future requests.

Requiring a Token (in the Backend)

  • For requiring authentication in the firebase (our backend) we need to set up database rules in the firebase console:
{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null"
  }
}

Sending the token

  • Now for making Fetch Data and Save Data action we need to append accessToken of logged users in the http requests.

auth.service.ts

  token: string;

  getToken() {
    firebase.auth().currentUser.getToken()
    .then(
      (token:string) => {
        this.token = token;
      }
    );
    return this.token;
  }
  signinUser(mail: string, password:string) {

    firebase.auth().signInWithEmailAndPassword(mail, password)
    .then(
      response => {
        firebase.auth().currentUser.getToken()
        .then(
          (token:string) => {
            this.token = token;
          }
        )
      }
    )
    .catch(
      error => console.log(error)
    );

  }

data-storage.service.ts

  • We append token in the url of the requests
  storeRecipes() {
    const token = this.authService.getToken();
      return this.http.put('https://ng-recipe-book-5333b.firebaseio.com/recipes.json?auth='+token,
    this.recipesService.getRecipes());
  }

Checking and using Authentication status

  • We want to show dropdown menu at the top right only for authenticated users

auth.service.ts

  isAuthenticated() {
      return this.token != null;
  }

header.component.ts

  constructor(private dataStorageService: DataStorageService,
    private recipesService: RecipesService,
    private authService: AuthService) { }

header.component.html

      <ul class="nav navbar-nav navbar-right">
        <ng-template [ngIf]="!authService.isAuthenticated()">
          <li><a href="/signin">Sign in</a></li>
          <li><a href="/signup">Sign up</a></li>
        </ng-template>
        <li class="dropdown" appDropdown *ngIf="authService.isAuthenticated()">
          <a style="cursor: pointer;" class="dropdown-toggle" role="button">Manage <span class="caret"></span></a>
          <ul class="dropdown-menu">
            <li><a style="cursor: pointer;" (click)="onSaveData()">Save Data</a></li>
            <li><a style="cursor: pointer;" (click)="onFetchData()">Fetch Data</a></li>
          </ul>
        </li>

Adding a Logout button

auth.service.ts

  logout() {
    firebase.auth().signOut();
    this.token = null;
  }

Route protection and redirection example

  • Protect some routes for only logged in users

auth/auth-guard.service.ts

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot , RouterStateSnapshot} from '@angular/router';
import { AuthService } from './auth.service';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService) {}
  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    return this.authService.isAuthenticated();
  }

}

app-routing.module.ts

...    
    { path: 'new', component: RecipeEditComponent, canActivate: [AuthGuard]},
    { path: ':id', component: RecipeDetailComponent},
    { path: ':id/edit', component: RecipeEditComponent, canActivate: [AuthGuard]}
...
  • After sign in we redirect to main page:

auth.service.ts

  signinUser(mail: string, password:string) {

    firebase.auth().signInWithEmailAndPassword(mail, password)
    .then(
      response => {
        this.router.navigate(['/']);
        firebase.auth().currentUser.getToken()
        .then(
          (token:string) => {
            this.token = token;
          }
        )
      }
    )
    .catch(
      error => console.log(error)
    );

  }

Wrap up

Possible improvements

 

Angular2 – Making Http Requests

Introduction

  • Traditional Web apps against SPAs

Example App & Backend setup

  • We use firebase to make a test database: https://ng-http-cb90d.firebaseio.com/
  • NOTE: Keep in mind set database rules to get free access:
{
  "rules": {
    ".read": "true",
    ".write": "true"
  }
}

 

Sending requests (example POST)

  • With the Angular Http class we can create Observables.
  • But the request is not sent until someone subscribe to that observable.
  • In the next example, we click on a button to subscribe to the POST method which trigger the POST request.

server.service.ts

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';

@Injectable()
export class ServerService {
  constructor(private http: Http) {}

  storeServers(servers: any[]) {
    return this.http.post('https://ng-http-cb90d.firebaseio.com/data.json', servers);
  }
}

app.module.ts

providers: [ServerService],

app.component.ts

  constructor(private serverService: ServerService) {}
  
  onSave() {
    this.serverService.storeServers(this.servers). subscribe (
      (response)=> {
        console.log(response);
      },
      (error) => console.log(error)

    );
  }

app.component.html

      <button type="button" class="btn btn-primary" (click)="onSave()">Save servers</button>

Adjusting request headers

  • We can add headers with the Headers class:

server.service.ts

  storeServers(servers: any[]) {

    const headers = new Headers({'Content-type': 'application/json'});
    return this.http.post('https://ng-http-cb90d.firebaseio.com/data.json', servers, { headers: headers });
  }

Sending GET requests

app.component.ts

  onGet() {
    this.serverService.getServers().subscribe(
      (response: Response) => {
        const data = response.json();
        console.log(data);
      },
      (error) => console.log(error)
    );
  }

server.service.ts

  getServers() {
    return this.http.get('https://ng-http-cb90d.firebaseio.com/data.json');
  }

 

Sending a PUT requests

  • In Firebase the POST methods add items to the database, but with the PUT method we can update the whole database. (This is depending of the backend database)
  storeServers(servers: any[]) {
    const headers = new Headers({'Content-type': 'application/json'});
    return this.http.put('https://ng-http-cb90d.firebaseio.com/data.json', servers, { headers: headers });
    // return this.http.post('https://ng-http-cb90d.firebaseio.com/data.json', servers, { headers: headers });
  }

Transform response easily with observable operators (map())

  • Lets extract the data from the server response to render it properly.
  • We can use map() (import rxjs/Rx is needed) for transform response from the server. What map method does is transform an observable in another observable.
  • We will make this transformation in the service which is much better (a centralized place):
  getServers() {
    return this.http.get('https://ng-http-cb90d.firebaseio.com/data.json')
    .map(
      (response: Response)=> {
        const data = response.json();
        return data;
      }
    );
  }

Using the returned data

server.service.ts

getServers() {
    return this.http.get('https://ng-http-cb90d.firebaseio.com/data.json')
    .map(
      (response: Response)=> {
        const data = response.json();
        for(let server of data) {
          server.name = 'FETCHED_' + server.name;
        }
        return data;
      }
    );
  }

app.component.ts

    this.serverService.getServers().subscribe(
      (servers: any[]) => {
        // const data = response.json();
        console.log(servers);
        this.servers = servers;
      },
      (error) => console.log(error)
    );

 

Catching Http errors

  • We want to catch request errors and print more friendly messages to the chrome console.
  • We force and error removing .json at the end of the url.

server.service.ts

  return this.http.get('https://ng-http-cb90d.firebaseio.com/data')
...
    .catch(
      (error: Response) => {
        console.log(error);
        return Observable.throw('Something went wrong');
        // return Observable.throw(error);

      }
    );

app.component.ts

  onGet() {
    this.serverService.getServers().subscribe(
      (servers: any[]) => {
        // const data = response.json();
        console.log(servers);
        this.servers = servers;
      },
      (error) => console.log(error)
    );
  }

Using the async pipe with Http requests

  • We explain here how to use async to render values depending of http requests.

server.service.ts

  getAppName() {
    return this.http.get('https://ng-http-cb90d.firebaseio.com/appName.json')
    .map(
      (response: Response) => {
        return response.json();
      }
    );

  }

app.component.ts

appName = this.serverService.getAppName();

app.component.html

<h1>{{appName | async}}</h1>

 

 

Angular2 – Handling forms: Reactive approach

Introduction to the Reactive Approach

  • Form is created programmatically and synchronized with the DOM

Reactive : Setup

  • In the app.module we need to comment out FormsModule and add ReactiveFormsModule

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
// import { FormsModule } from '@angular/forms';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    // FormsModule,
    HttpModule,
    ReactiveFormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts

import { NgForm, FormGroup } from '@angular/forms';
...
  reactiveForm: FormGroup;

Reactive: Creating a form in code

  • Out intention now is bind an HTML form to a Form object created programmatically.

app.component.html

  <h2>Reactive forms</h2>
  <div class="row">
    <div class="col-xs-12 col-sm-10 col-md-8 col-sm-offset-1 col-md-offset-2">
      <form>
        <div class="form-group">
          <label for="candidate">Candidate</label>
          <input type="text" id="candidate" class="form-control">
        </div>
        <div class="form-group">
          <label for="haircolor">Hair color</label>
          <select id="haircolor" type="select" class="form-control">
            <option value="blonde">Blonde</option>
            <option value="brown">Brown</option>
          </select>
        </div>

        <button
        class="btn btn-primary"
        type="submit"
        >Submit</button>
      </form>
    </div>
  </div>

app.component.ts

  //Reactive approach
  reactiveForm: FormGroup;

  ngOnInit() {
    this.reactiveForm = new FormGroup({
      'candidate': new FormControl(null),
      'haircolor': new FormControl('blonde')
    });
  }

Reactive: Syncing HTML and form

  • The first thing is tells Angular not to detect form tags and use my FormGroup created in Typescript. We use formGroup and formControlName built-in directives.

app.component.html

...
      <form [formGroup]="reactiveForm">
...
          <input type="text" id="candidate"
            class="form-control"
            formControlName="candidate">
...
          <select id="haircolor" type="select" class="form-control"
            formControlName="haircolor">

Reactive: Submitting the form

  • Here we can use ngSubmit as in TD forms , but we dont need any local reference to the form. We already have reference to the form in the Form object created.

app.component.html

<form [formGroup]="reactiveForm" (ngSubmit)="onSubmitReactive()">

app.component.ts

  onSubmitReactive() {
      console.log('submitted! reactive', this.reactiveForm);
  }

Reactive: Adding Validation

  • We will use Validators class to pass static references to functions to execute on validation.
  • We will use an array of Validators for making multiple validations.

app.component.ts

  ngOnInit() {
    this.reactiveForm = new FormGroup({
      'candidate': new FormControl(null, Validators.required),
      'email': new FormControl(null, [Validators.required,Validators.email]),
      'haircolor': new FormControl('brown')
    });
  }

Reactive: Getting access to controls

app.component.html

<span class="help-block" 
 *ngIf="!reactiveForm.get('candidate').valid && reactiveForm.get('candidate').touched">Please enter a valid candidate</span>

Reactive: Grouping controls

app.component.ts

  ngOnInit() {
    this.reactiveForm = new FormGroup({
      'userData': new FormGroup({
        'candidate':new FormControl(null, Validators.required),
        'email':new FormControl(null, [Validators.required,Validators.email])
      }),
      'haircolor': new FormControl('brown')
    });
  }

app.component.html

...
      <form [formGroup]="reactiveForm" (ngSubmit)="onSubmitReactive()">
        <div formGroupName="userData">
          <div class="form-group">
            <label for="candidate">Candidate</label>
            <input type="text" id="candidate"
              class="form-control"
              formControlName="candidate">
            <span class="help-block"
              *ngIf="!reactiveForm.get('userData.candidate').valid && reactiveForm.get('userData.candidate').touched">Please enter a valid candidate</span>
          </div>
          <div class="form-group">
            <label for="email-reactive">Email</label>
            <input type="text" id="email-reactive"
              class="form-control"
              formControlName="email">
          </div>

        </div>

Reactive: Array of Form controls (FormArray)

  • We will add hobbies dynamically as an input array.
  • We will use formArray directive in the HTML and FormArray in the typescript.

app.component.html

        <div formArrayName="hobbies">
          <h3>Your hobbies</h3>
          <button class="btn btn-primary" (click)="onAddHobby()">Add Hobby</button>
          <div class="form-group"
            *ngFor="let control of reactiveForm.get('hobbies').controls; let i = index">
            <input type="text" class="form-control" [formControlName]="i">
          </div>
        </div>

app.component.ts

 ngOnInit() {
 this.reactiveForm = new FormGroup({
 'userData': new FormGroup({
 'candidate':new FormControl(null, Validators.required),
 'email':new FormControl(null, [Validators.required,Validators.email])
 }),
 'hobbies': new FormArray([]),
 'haircolor': new FormControl('brown')
 });
 }
onAddHobby() {
    const control = new FormControl(null);
    (<FormArray>this.reactiveForm.get('hobbies')).push(control);

  }

Reactive: Creating Custom validators

  • We will check candidate against a  list of forbidden user names.

app.component.ts

    this.reactiveForm = new FormGroup({
      'userData': new FormGroup({
        'candidate':new FormControl(null, [Validators.required, this.forbiddenNameValidation.bind(this)]),
        'email':new FormControl(null, [Validators.required,Validators.email])
      }),
      'hobbies': new FormArray([]),
      'haircolor': new FormControl('brown')
    });
  }
...
  forbiddenNameValidation(control:FormControl): {[s: string]: boolean} {
      if(this.forbiddenUsernames.indexOf(control.value) !== -1) {
        return {'nameIsForbidden': true};
      }
      return null;  //Valid
  }

NOTE: It is important return null on validator function as it is how Angular recognizes validation is ok.

NOTE: We need to make bind(this) in the validators array for the custom validation for tells Angular to get the correct reference of the form control.

Reactive: Using Error Codes

  • We will customize error messages for custom validations.
  • We will check errors property in the form.controls object.

app.component.html

<div class="form-group">
            <label for="candidate">Candidate</label>
            <input type="text" id="candidate"
              class="form-control"
              formControlName="candidate">
            <span class="help-block"
              *ngIf="!reactiveForm.get('userData.candidate').valid && reactiveForm.get('userData.candidate').touched">
              <span class="help-block" *ngIf="reactiveForm.get('userData.candidate').errors['nameIsForbidden']">
                This name is forbidden
              </span>
              <span class="help-block" *ngIf="reactiveForm.get('userData.candidate').errors['required']">
                Name is required
              </span>
            </span>
          </div>

Reactive: Creating a Custom Async Validator

  • Sometimes we want to reach out a server to make a validation
  • So we need to make an async operation , in this case we can pass to the FormControl constructor a third parameter telling Async Validators.
  • In the example we simulate an async operation with a timeout and check if email is test@test.com
...
'email':new FormControl(null, [Validators.required,Validators.email], this.forbiddenEmails)
...

  forbiddenEmails(control:FormControl): Promise<any> | Observable<any> {
    const promise = new Promise<any>((resolve, reject) => {
    setTimeout(()=> {
      if(control.value === 'test@test.com') {
        resolve({'emailIsForbidden':true});
      } else {
        resolve(null);
      }
    },1500);
   });
   return promise;
 }

Reactive: Reacting to Status or Value Changes

  • We can monitor all the values and status changing in our form in every moment with two observables of the reactive forms: valueChanges and statusChanges.
  • We can hook on them:
  ngOnInit() {
    ...
    this.reactiveForm.valueChanges.subscribe(
      (value) => {console.log(value);}
    );
    this.reactiveForm.statusChanges.subscribe(
      (value) => {console.log(value);}
    );
  }

Reactive: Setting and Patching values

  • As in TD forms we can use setValue, patchValue and reset methods on the reactive form object.

Notes

Shortcut for property binding for strings
  • Usually property binding use single quotes:
    [pattern]="'*(0-9)'"
  • When we re using a string in a property binding we can type a simpler syntax:
    pattern="*(0-9)"
Patterns in template driven forms
  • We can add pattern matching in template driven forms with the HTML5 pattern.
  • For example we want numbers above 0 in this input field:
          <input
            type="text"
            id="amount"
            class="form-control"
            name="amount"
            ngModel
            required
            pattern="^[1-9]+[0-9]*$">

ds

Angular 2 – Using pipes to transform output and making http requests

Pipes are about transforming output

Using Pipes

  • Some pipes are coming built in in Angular:
          {{ server.instanceType | uppercase }} |
          {{ server.started | date}}

Parametrizing pipes

{{ server.started | date:’fullDate’ }}

Where to learn more about pipes

https://angular.io/docs/ts/latest/api/#!?query=pipe

  • For example , for the date pipe we can see all the options we have available:
    • 'medium': equivalent to 'yMMMdjms' (e.g. Sep 3, 2010, 12:05:08 PM for en-US)
    • 'short': equivalent to 'yMdjm' (e.g. 9/3/2010, 12:05 PM for en-US)
    • 'fullDate': equivalent to 'yMMMMEEEEd' (e.g. Friday, September 3, 2010 for en-US)
    • 'longDate': equivalent to 'yMMMMd' (e.g. September 3, 2010 for en-US)
    • 'mediumDate': equivalent to 'yMMMd' (e.g. Sep 3, 2010 for en-US)
    • 'shortDate': equivalent to 'yMd' (e.g. 9/3/2010 for en-US)
    • 'mediumTime': equivalent to 'jms' (e.g. 12:05:08 PM for en-US)
    • 'shortTime': equivalent to 'jm' (e.g. 12:05 PM for en-US)

Chaining multiple pipes

  • {{ server.started | date:'fullDate' | uppercase }}

    The order is important because the types couldnt be the same.

Creating a custom pipe

shorten.pipe.ts

import {Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'shorten'
})
export class ShortenPipe implements PipeTransform{
  transform(value: any) {
    if(value.length > 10) {
      return value.substring(0, 10) + '...';
    } else {
      return value.substring(0, 10);
    }
  }
}

app.component.html

          <strong>{{ server.name | shorten}}</strong> |

app.module.ts

@NgModule({
  declarations: [
    AppComponent,
    ShortenPipe
  ],

Parametrizing a custom pipe

shorten.pipe.ts

import {Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'shorten'
})
export class ShortenPipe implements PipeTransform{
  transform(value: any, limit: number) {
    if(value.length > limit) {
      return value.substring(0, limit) + '...';
    } else {
      return value;
    }
  }
}

app.component.html

          <strong>{{ server.name | shorten:15}}</strong> |

 

Example: creating a filter pipe

  • Lets filter the server listing by status by typing in a status input field.

myfilter.pipe.ts

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'myfilter'
})
export class MyfilterPipe implements PipeTransform {

  transform(value: any, filterString: any, propName: string): any {
    if(value.length === 0 || filterString === '') {
      return value;
    }

    const resultArray = [];
    for(const item of value) {
      if(item[propName] === filterString) {
        resultArray.push(item);
      }
    }
    return resultArray;
  }

}

app.component.html

<li
          class="list-group-item"
          *ngFor="let server of servers | myfilter:filteredStatus:'status'"
          [ngClass]="getStatusClasses(server)">

Pure and impure pipes

  • If we add servers on a filtered list , we won’t see the added servers at the beginning.
  • That is because filters are pure by default.
  • If we want to make filtering to recalculate on adding servers to the list, we need to specify in the annotation the pure attribute:

myfilter.pipe.ts

@Pipe({
  name: 'myfilter',
  pure: false
})

app.component.html

<button type="button" (click)="addServer()">Add Server</button>

app.component.ts

  addServer(){
    this.servers.push({
      instanceType: 'small',
      name: 'JAJAJAJ',
      status: 'stable',
      started: new Date(15, 1, 2017)
    });
  }

NOTE: We can have performance issue by using this option in large lists

Understanding the async pipe

  • Imagine we have values on the template that depends of async services.
  • Then we can tell Angular to convert Promises in strings when promises are resolved with the async pipe.

app.component.ts

  appStatus = new Promise((resolve,reject)=> {
    setTimeout(
      ()=> {
        resolve('stable');
      }
    ,2000);
  });

app.component.html

<h3>App status: {{ appStatus | async}}</h3>

* We see how app status is blank at the beginning , but after two seconds render ‘stable’

 
dd
f
df
dfd
fd
fd

Angular 2 – Handling forms

  • Regarding forms, what Angular does for us is giving us a object representation of the form in order to handle fetching data and form validation.
  • And also we will be able to handle form submit without reloading the page (as it is a SPA).

Captura de pantalla 2017-05-10 a las 9.30.46

Two approaches

  • There are two ways of handling forms with Angular. With Template -driven we will resolve most of the cases, but with the Reactive approach you will have full control of the form and it is useful for advanced cases.
  • In Template-driven approch we code all the logic in the template.

Captura de pantalla 2017-05-10 a las 9.35.14

TD: Creating the form and registering controls

  • We need to import FormModule in the app.module.ts

app.module.ts

import { FormsModule } from '@angular/forms';
...

imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  • Angular will detect form tags in your html to create the javascript representation, but we still need to indicate what controls of the form we want to manage. We get that with ngModel directive and giving a “name” attribute to the control. That name we will use it to fetch the value of that control,
            <input
              type="email"
              id="email"
              class="form-control"
              ngModel
              name="email">
  • Submit of the form managed by Angular is done by adding ngSubmit directive to the form tag and specifying the handler method. Otherwise the default behaviour will take place.
<form (ngSubmit)="onSubmit()">
  • With the ngForm directive we finally get the desired javascript representation of our form
<form (ngSubmit)="onSubmit(f)" #f="ngForm">
import { NgForm } from '@angular/forms';

 ...
 onSubmit(form: NgForm) {
    console.log('submitted!', form);
  }

Captura de pantalla 2017-05-10 a las 10.31.05

  • There are useful properties in NgForm object:
    • controls: what control we registered
    • dirty: flag to indicate of something changed
    • disabled
    • enabled
    • valid
    • invalid
    • touched
  • We also can use ViewChild annotation to get reference of a template reference and get form reference:
<form (ngSubmit)="onSubmit()" #f="ngForm">
@ViewChild('f') signupForm: NgForm;
...
onSubmit() {
 console.log('submitted!', this.signupForm);
 }

TD: Adding validation to check user input

  • In template driven forms we can add some validation in the tags. For example, we will add required to username input and required and email validation to email input:
               <input
              type="text"
              id="username"
              class="form-control"
              ngModel
              name="username"
              required>
...
             <input
              type="email"
              id="email"
              class="form-control"
              ngModel
              name="email"
              required
              email>
  • in the NgForm object submitted we will get the valid flag of the form and of the individuals controls set to true/false as a result of validation. What Angular does behind the scenes is adding some control classes to the DOM inputs, such as ‘ng-valid’ , ‘ng-touched’, etc

Built-in Validators & Using HTML5 Validation

Which Validators do ship with Angular?

Check out the Validators classhttps://angular.io/docs/ts/latest/api/forms/index/Validators-class.html – these are all built-in validators, though that are the methods which actually get executed (and which you later can add when using the reactive approach).

For the template-driven approach, you need the directives. You can find out their names, by searching for “validator” in the official docshttps://angular.io/docs/ts/latest/api/#!?query=validator – everything marked with “D” is a directive and can be added to your template.

Additionally, you might also want to enable HTML5 validation (by default, Angular disables it). You can do so by adding the ngNativeValidate  to a control in your template.

 

TD: Using the Form State

  • Lets disable the submit button on false validation (using the form local reference ‘f’):
<button 
        class="btn btn-primary"
        type="submit"
        [disabled]="!f.valid">Submit</button>
  • Also we would like to render in red invalid inputs on validation checks. We will use Angular classes:

app.component.css

input.ng-invalid.ng-touched {
  border: 1px solid red;
}

TD: Outputting validation error messages

  • Lets add a validation message for the email input using a local reference for the email input:
<input
              type="email"
              id="email"
              class="form-control"
              ngModel
              name="email"
              required
              email
              #email="ngModel">
            <span class="help-block" *ngIf="!email.valid && email.touched">Please select a valid email!</span>

TD: Setting default values with ngModel property binding

  • We want in the elect input set “pet” ad the default value. We use property binding in the ngModel directive.

app.component.ts

  defaultQuestion = "pet";

app.component.html

<select
            id="secret"
            class="form-control"
            [ngModel]="defaultQuestion"
            name="secret">
            <option value="pet">Your first Pet?</option>
            <option value="teacher">Your first teacher?</option>
          </select>

TD: Using ngModel with Two-Way Binding

  • Sometimes you want to react to changes in the input , so you need 2-way binding.
  • It is the case when some element in the template depends on other element. In this example we will print what user input in a textarea:

app.component.html

        <div class="form-group">
          <textarea name="questionAnswer" rows="10"
            class="form-control"
            [(ngModel)] = "answer">
          </textarea>
          <p>Yout reply is {{answer}}</p>
        </div>

app.component.ts

  answer='';
  • We get the value in the NgForm in the typescript and also in the template

TD: Grouping form controls

  • We can group some form controls under a same javascript object and be as another form control for checking engine. For example let group user data :
<form (ngSubmit)="onSubmit()" #f="ngForm">
        <div id="user-data"
        ngModelGroup="userData"
        #userData="ngModelGroup">
...
        <p *ngIf="!userData.valid && userData.touched">User data is not valid!</p>

TD: Handling Radio Buttons

          <div class="radio" *ngFor="let gender of genders">
            <label for="">
              <input type="radio"
                name="gender"
                id="gender_{{gender}}"
                ngModel
                [value]="gender"
                required>
                {{gender}}
            </label>
          </div>

app.component.ts

  genders= ['male', 'female'];

TD: Setting and patching form values

  • We can set the whole form to some value with two methods: setValue and patchValue
  • With setValue we overwrite the whole form. For example let set the form on clicking in the suggested user button:

app.component.html

          <button class="btn btn-default" type="button" (click)="suggestUserName()">Suggest an Username</button>

app.component.ts

  suggestUserName() {
    const suggestedName = 'Superuser';
    this.signupForm.setValue({
        userData: {
          username: suggestedName,
          email:''
        },
        secret:'pet',
        questionAnswer:'',
        gender:'male'
    });
  }
  • With patchValue on the form property of the NgForm object we don’t touch other values in the form , only the specified ones:
    this.signupForm.form.patchValue({
        userData: {
          username: suggestedName,
        },
        gender:'male'
    });

TD: Using form data

  • We will print out the information user input on submission:

app.component.html

  <div class="row" *ngIf="submitted">
    <div class="col-xs-12">
      <h2>Your submitted data is:</h2>
      <ul>
        <li>Username: {{user.username}}</li>
        <li>Email: {{user.email}}</li>
        <li>Gender: {{user.gender}}</li>
        <li>Secret question: {{user.secret}}</li>
      </ul>
    </div>
  </div>

app.component.ts

  user = {
    name: '',
    email: '',
    gender: '',
    secret: '',
  };

...

  onSubmit() {
    console.log('submitted!', this.signupForm);
    this.submitted = true;
    this.user.name = this.signupForm.value.userData.username;
    this.user.email = this.signupForm.value.userData.email;
    this.user.gender = this.signupForm.value.gender;
    this.user.secret = this.signupForm.value.secret;
  }

TD: Resetting forms

this.signupForm.reset();
  • You can also pass values to reset method to reset to specific values.