Friday, January 11, 2019

48 answers on StackOverflow to the most popular Angular questions



I gathered the most common questions and answers from Stackoverflow. These questions were chosen by the highest score received. Whether you are an expert or a beginner, you can learn from others’ experiences.

Table of Contents

Enjoy!

Angular — Promise vs Observable

551+ points 🔹 150,497+ viewed
Rohit asked,
Can someone please explain the difference between Promise and Observable in Angular?
An example on each would be helpful in understanding both the cases. In what scenario can we use each case?
Günter Zöchbauer answered, (686+ points)
Promise
A Promise handles a single event when an async operation completes or fails.
Note: There are Promise libraries out there that support cancellation, but ES6 Promise doesn't so far.
Observable
An Observable is like a Stream (in many languages) and allows to pass zero or more events where the callback is called for each event.
Often Observable is preferred over Promise because it provides the features of Promise and more. With Observable it doesn't matter if you want to handle 0, 1, or multiple events. You can utilize the same API in each case.
Observable also has the advantage over Promise to be cancelable. If the result of an HTTP request to a server or some other expensive async operation isn't needed anymore, the Subscription of an Observable allows to cancel the subscription, while a Promise will eventually call the success or failed callback even when you don't need the notification or the result it provides anymore.
Observable provides operators like map, forEach, reduce, ... similar to an array
There are also powerful operators like retry(), or replay(), ... that are often quite handy.

Difference between Constructor and ngOnInit

444+ points 🔹 190,166+ viewed
Haseena P A asked,
Angular provides lifecycle hook ngOnInit by default.
Why should ngOnInit be used, if we already have a constructor?
Pardeep Jain answered, (512+ points)
The Constructor is a default method of the class that is executed when the class is instantiated and ensures proper initialization of fields in the class and its subclasses. Angular or better Dependency Injector (DI) analyzes the constructor parameters and when it creates a new instance by calling new MyClass() it tries to find providers that match the types of the constructor parameters, resolves them and passes them to the constructor like
new MyClass(someArg);
ngOnInit is a lifecycle hook called by Angular2 to indicate that Angular is done creating the component.
We have to import OnInit in order to use like this (actually implementing OnInit is not mandatory but considered good practice):
import {Component, OnInit} from '@angular/core';
then to use the method of OnInit we have to implement in the class like this.
export class App implements OnInit{
  constructor(){
     //called first time before the ngOnInit()
  }
  ngOnInit(){
     //called after the constructor and called  after the first ngOnChanges() 
  }
}
Implement this interface to execute custom initialization logic after your directive's data-bound properties have been initialized. ngOnInit is called right after the directive's data-bound properties have been checked for the first time, and before any of its children have been checked. It is invoked only once when the directive is instantiated.
Mostly we use ngOnInit for all the initialization/declaration and avoid stuff to work in the constructor. The constructor should only be used to initialize class members but shouldn't do actual "work".
So you should use constructor() to setup Dependency Injection and not much else. ngOnInit() is better place to "start" - it's where/when components' bindings are resolved.
For more information refer here:

Can’t bind to ‘ngModel’ since it isn’t a known property of ‘input’

442+ points 🔹 246,901+ viewed
abreneliere asked,
I’ve got the following error when launching my Angular app, even if the component is not displayed.
I have to comment out the so that my app works.
zone.js:461 Unhandled Promise rejection: Template parse errors:
Can't bind to 'ngModel' since it isn't a known property of 'input'. ("
    <div>
        <label>Created:</label>
        <input  type="text" [ERROR ->][(ngModel)]="test" placeholder="foo" />
    </div>
</div>"): InterventionDetails@4:28 ; Zone: <root> ; Task: Promise.then ; Value:
I’m looking at the Hero plucker but I don’t see any difference.
Here is the component file:
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Intervention } from '../../model/intervention';
@Component({
    selector: 'intervention-details',
    templateUrl: 'app/intervention/details/intervention.details.html',
    styleUrls: ['app/intervention/details/intervention.details.css']
})
export class InterventionDetails
{
    @Input() intervention: Intervention;
    public test : string = "toto";
}
abreneliere answered, (674+ points)
Yes that’s it, in the app.module.ts, I just added :
import { FormsModule } from '@angular/forms';
[...]
@NgModule({
  imports: [
    [...]
    FormsModule
  ],
  [...]
})

Angular HTML binding

385+ points 🔹 227,115+ viewed
Aviad P. asked,
I am writing an Angular application, and I have an HTML response I want to display. How do I do that? If I simply use the binding syntax {{myVal}} it encodes all HTML characters (of course).
I need somehow to bind the inner html of a div to the variable value.
prolink007 answered, (691+ points)
The correct syntax is now the following:
<div [innerHTML]="theHtmlString"></div>
Working in 5.2.6

Angular/RxJs When should I unsubscribe from `Subscription`

320+ points 🔹 69,606+ viewed
Sergey Tihon asked,
When should I store the Subscription instances and invoke unsubscribe() during the NgOnDestroy lifecycle and when can I simply ignore them?
Saving all subscriptions introduces a lot of mess into component code.
HTTP Client Guide ignore subscriptions like this:
getHeroes() {
  this.heroService.getHeroes()
                   .subscribe(
                     heroes => this.heroes = heroes,
                     error =>  this.errorMessage = <any>error);
}
In the same time Route & Navigation Guide says that:
Eventually, we'll navigate somewhere else. The router will remove this component from the DOM and destroy it. We need to clean up after ourselves before that happens. Specifically, we must unsubscribe before Angular destroys the component. Failure to do so could create a memory leak.
We unsubscribe from our Observable in the ngOnDestroy method.
private sub: any;
ngOnInit() {
  this.sub = this.route.params.subscribe(params => {
     let id = +params['id']; // (+) converts string 'id' to a number
     this.service.getHero(id).then(hero => this.hero = hero);
   });
}
ngOnDestroy() {
  this.sub.unsubscribe();
}
seangwright answered, (508+ points)

— — Edit 3 — The ‘Official’ Solution (2017/04/09)

I spoke with Ward Bell about this question at NGConf (I even showed him this answer which he said was correct) but he told me the docs team for Angular had a solution to this question that is unpublished (though they are working on getting it approved). He also told me I could update my SO answer with the forthcoming official recommendation.
The solution we should all use going forward is to add a private ngUnsubscribe: Subject = new Subject(); field to all components that have .subscribe() calls to Observables within their class code.
We then call this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); in our ngOnDestroy() methods.
The secret sauce (as noted already by @metamaker) is to call .takeUntil(this.ngUnsubscribe) before each of our .subscribe() calls which will guarantee all subscriptions will be cleaned up when the component is destroyed.
Example:
import { Component, OnDestroy, OnInit } from '@angular/core';
import 'rxjs/add/operator/takeUntil';
// import { takeUntil } from 'rxjs/operators'; // for rxjs ^5.5.0 lettable operators
import { Subject } from 'rxjs/Subject';
import { MyThingService } from '../my-thing.service';
@Component({
    selector: 'my-thing',
    templateUrl: './my-thing.component.html'
})
export class MyThingComponent implements OnDestroy, OnInit {
    private ngUnsubscribe: Subject = new Subject();
    constructor(
        private myThingService: MyThingService,
    ) { }
    ngOnInit() {
        this.myThingService.getThings()
            .takeUntil(this.ngUnsubscribe)
            .subscribe(thing

What about Promises?

  1. Promises are often cited as the best way to handle asynchronicity, and have their own built-in object methods.
  2.  The problem with Promises is that they are neither cancellable nor are they a good way to manage collections of data.
  3. uses the Event Loop in a different way.

Objerable ?

You can think of an observable as an array whose items arrive asynchronously over time. Observables help you manage asynchronous data, such as data coming from a backend service. Observables are used within Angular itself, including Angular’s event system and its http client service. To use observables, Angular uses a third-party library called Reactive Extensions (RxJS). Observables are a proposed feature for ES 2016, the next version of JavaScript.

Why your Angular App is not Working: 11 common Mistakes

Resolving problems of your angular application can be very challenging. When your angular app is not working and all it gives you are some cryptic red lines in a console. Especially when you are a beginner, these problems can turn the development process with angular into a real pain.

But trust me, it does not have to be painful.
In fact, I think angular development is one of the smoothest development experience I had so far. All you need to do is avoid some very common mistakes, almost every angular developer had to deal with.

At least I did.

By reading this article, those mistakes will not slow you down on your angular journey.

We will take a closer look at each of 11 mistakes. I will explain to you, why it is actually a mistake and point you in the right direction with at least one possible solution.

So let's get started!

1. Import required Angular Modules


Probably the most common mistakes, beginners make, is not to import the required modules. Why? Because they don't even know about them. Of course, they don't. Getting an overview of the angular framework takes some time. Unfortunately, that often results in mysteriously not working angular applications. The errors you get look something like this:

Can't bind to 'ngModel' since it isn't a known property of 'input'
This error indicates, that the angular Forms Module has not been imported into your module.

Unhandled Promise rejection: No provider for HttpClient!
This error tells you, that you have not imported the angular HttpClient Module into your (root) module.

The Solution


To resolve the problem, you need to import the missing module into your module. Most of the cases, that module would be the AppModule in your app directory.




@NgModule({
    declarations: [AppComponent],
    imports: [
        BrowserModule,
        FormsModule,
        HttpClientModule
    ],
    bootstrap: [AppComponent]
})
export class AppModule {}
Note: Only import the modules you really need! Importing unnecessarily module bloats your application size significantly.

This advice does not only apply to framework angular modules. It also applies to any angular module you might want to use, including third-party ones.

Here are some common modules you might need to import:

BrowserModule
FormsModule // required to use ngModel directive
HttpClientModule // formerly HttpModule 
RouterModule
BrowserAnimationsModule / NoopAnimationsModule
It is good practice for third party libraries to split up in as many modules as possible, to keep the size of your application small. With Angular Material, for example, you have to import a module, for every component you use.

For example:
MatMenuModule
MatSidenavModule
MatCheckboxModule
MatDatepickerModule
MatInputModule
...

2. Don't use DOM references before they exist (@ViewChild)


With the help of the @ViewChild decorator, angular makes it very easy to reference specific child elements (html nodes or components) of your component.
All you need to do is to add a reference identifier to the node or component inside of your template. Just add a # followed by a name beside the nodes attributes.



<div #myDiv></div> 
We can now reference that element from our component. If it is a component, we can call its public methods and access its properties. If it is a plain HTML element, we can change its style, its attributes or its children.

Angular automatically assigns the reference to a property of our component, if we decorate that property with the @ViewChild() decorator. Make sure, to pass the reference name to the decorator. For example @ViewChild('myDiv').



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

@Component({})
export class ExampleComponent {
    @ViewChild('myDiv') divReference;
}

The Problem


The @ViewChild() directive is very useful. But you have to keep this in mind:

You can only use the reference to the element if the element actually exists!

Why shouldn't it? There are multiple reasons why the element you are referencing could not actually exist.

The most common reason, is, that the browser has not finished creating it and has not yet added it to the DOM. If you try to use it before it has been added, it will not work and crash your app. If you are familiar with JavaScript in general, you have probably stumbled across that problem, as it is not specific to angular.

One example of accessing the DOM when it does not exist is in the constructor of the component. Another one would be in the ngOnInit lifecycle callback.

THIS DOES NOT WORK:


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

@Component({})
export class ExampleComponent implements OnInit{
    @ViewChild('myDiv') divReference;

    constructor(){
        let  ex = this.divReference.nativeElement; // divReference is undefined
    }

    ngOnInit(){
        let  ex = this.divReference.nativeElement; // divReference is undefined
    }
}

The Solution


Just like the DOMContentLoaded event or the $(document).ready() callback in JQuery, angular has a similar mechanism, to notify you, that all HTML elements have been created. It is called the ngAfterViewInit lifecycle hook. It is a callback you should implement, that gets triggered when all of the components views and child-views got initialized. That way you, it is (almost) safe to access the viewChild reference inside of that callback.



import { ViewChild, AfterViewInit } from '@angular/core';

@Component({})
export class ExampleComponent implements AfterViewInit {
    @ViewChild('myDiv') divReference;

    ngAfterViewInit(){
        let  ex = this.divReference.nativeElement; // divReference is NOT undefined
    }
}
Great, it is working now. But wait. There is one more trap to step into.

As I mentioned before, you can only access elements, that actually exists. As we will discuss in a later chapter, elements that have a false *ngIf directive, are completely removed from the DOM. Hence we can not access them in that case.

To prevent your application from crashing, you should check your references for null before using them. By the way, that advice does not only apply to components or angular, but for any programming language in general.



import { ViewChild, AfterViewInit } from '@angular/core';

@Component({})
export class ExampleComponent implements AfterViewInit {
    @ViewChild('myDiv') divReference;

    ngAfterViewInit(){
        let  ex;
        if( this.divReference ){
            ex = this.divReference.nativeElement; // divReference is NOT undefined
        }
    }
}

3. Don't manipulate the DOM directly - Angular Universal


Manipulating the DOM in angular directly is not only considered bad practice. It can also result in your angular app not working in a different environment other than the browser. The most popular example is the so-called Angular Universal project, which enables your angular application to be rendered server-side. But why should you even care? Read all about Angular universal and server-side rendering in this step-by-step guide.

THIS EXAMPLE DOES NOT WORK


import { ViewChild, AfterViewInit } from '@angular/core';

@Component({})
export class ExampleComponent implements AfterViewInit {
    @ViewChild('myDiv') divReference;

    ngAfterViewInit(){
        let  ex = this.divReference.nativeElement;
        ex.style.color = 'red'; // does not work on the server
    }
}

The Solution


Instead of changing the elements directly, you should manipulate them indirectly. For that, angular offers a rendering API in the form of the Renderer2 class. Yes, that '2' is intentional and yes there was a Renderer (1). Not the best name, but it is what it is.

With the help of that renderer, we can still do everything we are used to when working with the DOM. But by using the renderer, we are making sure, that our code works as well on the server, as it does on the client.

Here is how it should be done:

1. Obtain an instance of the Renderer2 by requesting it via Dependency Injection in the constructor


import { ViewChild, Renderer2 } from '@angular/core';

@Component({})
export class ExampleComponent{
    @ViewChild('myDiv') divReference;

    constructor(private renderer: Renderer2){
    }
}
2. Use the renderer to manipulate the DOM indirectly. Again, make sure that the reference to the element actually exists.


import { ViewChild, Renderer2, AfterViewInit } from '@angular/core';

@Component({})
export class ExampleComponent implements AfterViewInit{
    @ViewChild('myDiv') divReference;

    constructor(private renderer: Renderer2){
    }

    ngAfterViewInit(){
        if(this.divReference)
            this.renderer.setStyle(this.divReference.nativeElement, 'color', 'red');
    }
}
The Renderer2 has many different methods to alter an element. Many of them look very familiar to the JavaScript DOM API. Guessing what they do, should not be the issue. You can find the full list of methods at the official documentation.

4. Avoid duplicate providers overriding each other


You may have heard that angular utilizes a concept called dependency injection. With the help of dependency injection, you can request instances of your services in your constructor.

For that to work, services or broader injectables have to be registered in the provider section of your component or module decorator. The most common method is to provide it at a module level.

dependency-injection


The problem here, is, that angular uses a hierarchical dependency injection system. That means, that services/injectables provided in the root module (AppModule) are available to all components in that module. And because that module should contain all other components and modules, the services provided here are available in the whole application.

hierarchical-dependency-injection


If you provide a service to a sub-module, it is only available to that sub-module. That also means, that if you provide the services in both modules, the components in the sub-module get a different instance of the service than any other component. That can lead to all kinds of errors if you assume your service to be the only instance in your application (singleton).

The solution is simple. Do only provide your services once. Generally in the AppModule. Unless you know what you are doing, you should stick to that, especially in the beginning. In 99% of the cases, you should be fine doing so.

5. Angular Guards are not a Security Feature


Angular Guards are a great way to artificially restrict access to certain routes. For example, to check if a user is logged in, before actually showing him the page.
Here is a quick example of such a guard:



import { Injectable } from '@angular/core';
import { AuthenticationService } from './authentication.service';
import { CanActivate } from '@angular/router';

@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private authService: AuthenticationService) {}

  canActivate() {
    return this.authService.isAuthenticated();
  }
}

Of course, because the guard is an observable, it has to be provided, as well.



@NgModule({
  providers: [
    AuthGuard,
    AuthenticationService
  ]
})
export class AppModule {}
Finally, we have to tell it, which routes to protect:



@NgModule({
  imports: [
   RouterModule.forRoot([
    { 
    path: '',
    component: SomeComponent,
    canActivate: [AuthGuard] 
    ])
  ],
  providers: [
    AuthGuard,
    AuthenticationService
  ]
})
export class AppModule {}

The Problem


So why are guards a problem? The truth is, they are not!

But many people seem to be confused about the topic. They become a problem if people get a false sense of security because of them. Fact is, nothing you do on the client is 'secure'. Because you deliver the full source code to the potential harmful users, the application can be altered in any way. That means, that our guard can be easily circumvented by commenting some lines.

No, especially with AOT compilation, it is not THAT easy, but with enough criminal energy, definitely possible in a few hours.

That way, data that is only protected by a route guard client side, can be accessed without too much effort. You definitively do not want that!

The Solution


Because of that, if you need to protect any sensitive data, you also need to have an actually secure, server-based, solution. For example with signed JavaScript Web Tokens.

6. Declare your Components only once


For components to work in angular, they have to be declared in a module. So as long as we have only one module (AppModule) and we register our components inside of it, there is no problem.

But when we start to bundle our application in modules, what you definitely should, by the way, you probably will encounter a common problem.

A component can only be declared in one module!

That is pretty much all there is to it. But what can we do, if we want to use our component in multiple modules?

The solution is simple. Just wrap your component into a module. Maybe a module per component is a bit too much, so why don't we create a components module? That module can then be imported into other modules and you can use your components there.
When you do so, make sure that you don't only declare your components in that components module, but to also export them. Otherwise, they can only be accessed from within the module itself.



@NgModule({
  imports: [
    CommonModule
  ],
  declarations: [
    LoginComponent,
    RegisterComponent,
    HelpComponent
  ],
  exports:[
    LoginComponent,
    RegisterComponent,
    HelpComponent
  ]
})
export class AuthenticationModule { }

7. Speed up your application by using *ngIf instead of the [hidden] attribute


Another common mistake is to confuse *ngIf and [hidden]. Choosing the right one of these can make quite a difference performance wise. So let's take a closer look at both techniques.

The [hidden] Attribute


The hidden attribute toggles the visibility of an element. Just as we would expect. right?
That means, that if we set [hidden] to true, the css property "display" is set to "none". Afterward, the element is no longer visible, but still present on the DOM.



<div [hidden]="isHidden"></div>
One problem with using the hidden attribute, that the toggled css property can be easily overridden by other css properties by accident. If you, for example, define that elements "display" property to be "block" in your stylesheet, it will always override the display: none property. This results in your element being always visible.

Kudos to Kara Erickson for her findings of that. You can learn more about the topic in her great article here!

Another, although very theoretical, problem is, that all these elements stay on the DOM, although they are not visible. If we talk about hundreds or thousands of elements, these elements can slow down the browser quite a bit. So why don't we remove them, if we don't need them?

The *ngIf directive


The main difference of the *ngIf directive is exactly that. Instead of just hiding the marked elements, it completely removed them from the DOM. Aside from the possible performance improvement, this solution also appears a lot cleaner to me. But that is my opinion. It just seems like the standard way of hiding things in angular to me. Because of that, I almost exclusively use *ngIf.



<div *ngIf="!isHidden"></div>
When it comes to downsides of the *ngIf directive, it is sometimes hard to debug these sections, because the removed elements can no longer be inspected with the browsers DOM explorer.

8. Avoid maintainability issues by wrapping in Services


As you may have noticed, we transitioned from critical errors to recommendations on how to improve your application. The last points will right about that: Making your applications faster, smaller, and better to maintain.

As a general advice, it is always good practice, to extract your core business logic into services. That way, it becomes much easier to maintain, as it can be swapped out and replaced by a new implementation in just a few seconds.
The same goes for testing. Often times you need services that fetch external data, to fake the results in a test environment. If you fetch your data in services, that is an easy one. If not, good luck changing all the lines that need to be changed for that.

That advice is certainly true when using angular HttpClient. It should always be wrapped inside of a centralized service. That way, it not only stays testable, it is also easy to make changes to it. Imagine, your backend requires a new header to be passed with every request after a recent update. Without a centralized service, you now have to find all the lines across your whole app that are affected. Needless to say, that this would be far from optimal.

Instead, you should always wrap your HTTP-requests into services. In the worst case, that does not hurt you anyway. In the best case, it saves you (and your team) hours for the simplest tasks.

9. Gain performance and shrink size by using AOT in production


Starting an angular cli application via

ng serve
or

ng build
results in your application being build in regular mode. That means, that your application is shipped to the browsers, as it is. The browser has then to execute the angular compiler to convert your components and templates into executable JavaScript code. That process does not only take a lot of time. It also requires the whole angular compiler to be shipped with your application. In the current version of angular, the compiler has a size of ~1 MB (167kb gzipped). That's huge!

angular-bundle-size-default


Don't believe me? You can analyze the angular bundle itself, by using a tool called webpack-bundle-analyzer. All you need to do is to create your bundle with the stats-json parameter.

ng build --stats-json
Afterward, start the bundle analyzer like so:

webpack-bundle-analyzer dist/stats.json
The tool will automatically open a browser window, showing you a similar result to the one above.

The Solution


The solution to that is to use the so-called AOT (Ahead of Time) compilation. When using the AOT mode, your angular application is compiled at build time. That way, the browser does not has to do that work. Instead, we do it once and for all. This results in your application starting much faster.

Even more important, the bundle size, every user has to download, decreases dramatically, because the angular compiler no longer has to be included in the bundle.

In older versions of the angular-cli, you had to enable AOT manually in production builds. Fortunately, AOT has been the default of production builds for some time now. All you have to do is to add the production flag to your build command. That does not only give you AOT compilation, but also other benefits that reduce the bundle size, like excluded source-maps.

Older Versions

ng build --prod --aot
Newer Versions

ng build --prod
Here is how the production bundle looks like. The gzipped size of the vendor bundle is now around 55 KB. From 330 KB to 55 KB. That is what I call an improvement!
You will also notice, that the compiler is no longer included.

angular-bundle-size-production



10. Keep your Application Size small by only importing what you need


The next point is directly related to the previous. Again, we will take a look at the bundle size.

This time, the advice I give you is, to be careful what you import.

Every import statement you use increases the size of your bundle. Makes sense right? We are adding more code, so the size goes up.

The problem here is, that some libraries are quite huge. When using the wrong import statement, you can end up with the whole library in your application. Here is a much to popular mistake, that imports the whole RxJs library in your application.



import 'rxjs';
This tiny statement almost doubles the size of our application.

angular-rxjs-import-mistake


The point here is that this additional size is completely unnecessary. If you compare this bundle to the previous ones, you will notice, that these included RxJs as well. The difference is, that the previous bundle only contained the modules that are actually required. Using this import statement, we have imported just everything.

The Solution


Well, there are multiple solutions to this. The most general solutions would be, to evaluate every library you add to your project. Do you really need that shiny button at a cost of additional 100 KB? Does the library offer sub-modules, so you can only import the stuff you need? If not, it is probably not worth it.

If your library offers sub-modules, make sure to only import the stuff you need. Regularly check the resulting bundle using the bundle-analyzer. 

So how do you only import the stuff you need? Let's take a look at out RxJs example.
RxJs has split up almost everything into its own module. That requires you to eventually write a lot of imports, but it helps you to keep your application size small.

For example, you need to import every operator you want to use:



import 'rxjs/add/observable/of';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';
NEVER USE THIS:


import 'rxjs';
Unfortunately, not all libraries do a great job at splitting up their code. And they all do it differently. So taking a closer look at your libraries is crucial for small and fast applications.


11. Do not leak Memory - Unsubscribe your Subscriptions


When dealing with RxJs Observables and Subscriptions, it can easily happen, that you leak some memory. That is because your component is destroyed, but the function you registered inside of the observable is not. That way, you not only leak memory but probably also encounter some odd behavior.

The Solution



To prevent that, make sure to unsubscribe from your subscriptions, when the component is destroyed. One good place to do so, would be the ngOnDestroy lifecycle hook. Here is an example:



@Component({})
export class ExampleComponent implements OnDestroy{

    private subscriptions = []; 

    constructor(){
        this.subscriptions.push(this.anyObservable.subscribe());
    }
    
    ngOnDestroy(){
        for(let subscription of this.subscriptions){
            subscriptions.unsubscribe();
        }
    }
}

Conclusion


In this article, we went through all the mistakes that are very commonly made in the beginning. Well, at least I did most of them...

I hope by sharing my mistakes, I could help you avoid them completely and provide you a better angular development experience that way.

Saturday, January 5, 2019

Angular 2 form fundamentals: reactive forms

Angular 2 form fundamentals: reactive forms


High-level terminology

Before we begin, let’s clarify what “reactive” forms mean from a high level.

Reactive

When we talk about “reactive” forms (also known as model-driven), we’ll be avoiding directives such as ngModel, required and friends. The idea is that instead of declaring that we want Angular to power things for us, we can actually use the underlying APIs to do them for us. In a sense, instead of binding Object models to directives like template-driven forms, we in fact boot up our own instances inside a component class and construct our own JavaScript models. This has much more power and is extremely productive to work with as it allows us to write expressive code, that is very testable and keeps all logic in the same place, instead of scattering it around different form templates.

Template-driven forms

If you’re yet to dive into “template-driven” forms, check out my previous post on it.

Form base and interface

The base form structure that we’ll be using to implement our reactive form:
<form novalidate>
  <label>
    <span>Full name</span>
    <input
      type="text"
      name="name"
      placeholder="Your full name">
  </label>
  <div>
    <label>
      <span>Email address</span>
      <input
        type="email"
        name="email"
        placeholder="Your email address">
    </label>
    <label>
      <span>Confirm address</span>
      <input
        type="email"
        name="confirm"
        placeholder="Confirm your email address">
    </label>
  </div>
  <button type="submit">Sign up</button>
</form>
We have three inputs, the first, the user’s name, followed by a grouped set of inputs that take the user’s email address.
Things we’ll implement:
  • Bind to the user’s name, email, and confirm inputs
  • Required validation on all inputs
  • Show required validation errors
  • Disabling submit until valid
  • Submit function
Secondly, we’ll be implementing this interface:
// signup.interface.ts
export interface User {
  name: string;
  account: {
    email: string;
    confirm: string;
  }
}

ngModule and reactive forms

Before we even dive into reactive forms, we need to tell our @NgModule to use the ReactiveFormsModule from @angular/forms:
import { ReactiveFormsModule } from '@angular/forms';

@NgModule({
  imports: [
    ...,
    ReactiveFormsModule
  ],
  declarations: [...],
  bootstrap: [...]
})
export class AppModule {}
You will obviously need to wire up all your other dependencies in the correct @NgModule definitions.
Tip: use ReactiveFormsModule for reactive forms, and FormsModule for template-driven forms.

Reactive approach

Let’s begin with a base SignupFormComponent and add our above template:
// signup-form.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'signup-form',
  template: `
    <form novalidate>...</form>
  `
})
export class SignupFormComponent {
  constructor() {}
}
So, this is a typical component base that we need to get going. So what now? Well, to begin with, we don’t need to actually create any initial “data”, however, we do need to start understanding FormControl, FormGroup, and finally move onto the amazing FormBuilder.

FormControl and FormGroup

Before digging into these APIs, I would strongly recommend checking out my previous article on template-driven forms to gain a better understanding of what’s happening.
Let’s define what FormControl and FormGroup are:
  • FormControl is a class that powers an individual form control, tracks the value and validation status, whilst offering a wide set of public API methods.
Basic example:
ngOnInit() {
  this.myControl = new FormControl('Todd Motto');
}
  • FormGroup is a group of FormControl instances, also keeps track of the value and validation status for the said group, also offers public APIs.
Basic example:
ngOnInit() {
  this.myGroup = new FormGroup({
    name: new FormControl('Todd Motto'),
    location: new FormControl('England, UK')
  });
}
Right, so we have an example of invoking new instances of FormControl and FormGroup, now how do we use them? It’s actually much easier than you’d think. Let’s assume we’ll bind our FormGroup to a fresh code example before we continue with our signup form, so hopefully things click and you can follow easier:
<form novalidate [formGroup]="myGroup">
  Name: <input type="text" formControlName="name">
  Location: <input type="text" formControlName="location">
</form>
Note: you’ll notice ngModel and name="" attributes have been toasted, this is good thing as it makes our markup less declarative (which can become complex, quickly, with forms)
That’s it! On the form, we must declare [formGroup] as a binding, and formControlName as a directive with the corresponding Object key name. This is what we have:
FormGroup -> 'myGroup'
    FormControl -> 'name'
    FormControl -> 'location'

Implementing our FormGroup model

So now we’ve learned the basis of FormGroup and FormControl, we can think about implementing our own now. But first, what does our interface say?
// signup.interface.ts
export interface User {
  name: string;
  account: {
    email: string;
    confirm: string;
  }
}
So, we’ll need to implement a similar structure with JavaScript Objects using this composition:
FormGroup -> 'user'
    FormControl -> 'name'
    FormGroup -> 'account'
        FormControl -> 'email'
        FormControl -> 'confirm'
Yes, we can create nested FormGroup collections! Let’s make that come alive, but with no initial data:
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';

@Component({...})
export class SignupFormComponent implements OnInit {
  user: FormGroup;
  ngOnInit() {
    this.user = new FormGroup({
      name: new FormControl(''),
      account: new FormGroup({
        email: new FormControl(''),
        confirm: new FormControl('')
      })
    });
  }
}
If we did want to set initial data, we can do so as per the above examples whereby we pre-populate particular strings with information, which typically are data-driven from a backend API.

Binding our FormGroup model

Now we’ve instantiated the FormGroup model, it’s obviously time to bind it to the DOM. Using what we’ve learned before, let’s go ahead:
<form novalidate [formGroup]="user">
  <label>
    <span>Full name</span>
    <input
      type="text"
      placeholder="Your full name"
      formControlName="name">
  </label>
  <div formGroupName="account">
    <label>
      <span>Email address</span>
      <input
        type="email"
        placeholder="Your email address"
        formControlName="email">
    </label>
    <label>
      <span>Confirm address</span>
      <input
        type="email"
        placeholder="Confirm your email address"
        formControlName="confirm">
    </label>
  </div>
  <button type="submit">Sign up</button>
</form>
Now our FormGroup and FormControl matches with the DOM structure:
// JavaScript APIs
FormGroup -> 'user'
    FormControl -> 'name'
    FormGroup -> 'account'
        FormControl -> 'email'
        FormControl -> 'confirm'

// DOM bindings
formGroup -> 'user'
    formControlName -> 'name'
    formGroupName -> 'account'
        formControlName -> 'email'
        formControlName -> 'confirm'
Unlike template-driven forms, where we would do something like #f="ngForm", and print f.value in the DOM to check our form out, we do the opposite with reactive forms, as the [formGroup] is a directive that we bind to, passing the public user Object in:
// { name: '', account: { email: '', confirm: '' } }
{{ user.value | json }}

Reactive submit

This is actually the exact same as the template-driven approach, however we can optionally reference the form internally to the component, instead of passing it in as a value. First, the ngSubmit value-passing:
<form novalidate (ngSubmit)="onSubmit(user)" [formGroup]="user">
  ...
</form>
Notice how we just passed user into the onSubmit()? This allows us to pull down various pieces of information from our respective method on our component class:
export class SignupFormComponent {
  user: FormGroup;
  onSubmit({ value, valid }: { value: User, valid: boolean }) {
    console.log(value, valid);
  }
}
Here we’re using Object destructuring to fetch the value and valid properties from the user reference we pass into onSubmit. The value is the same reference as printing user.value out in the DOM. That’s literally it, you’re free to pass values to your backend API.
Now, for the more internal approach. Because this.user is technically our model, we can simply reference the model onSubmit internally, and not pass user through as a function argument:
export class SignupFormComponent {
  user: FormGroup;
  onSubmit() {
    console.log(this.user.value, this.user.valid);
  }
}

Reactive error validation

So far, we’ve implemented zero validation! Oh my. Let’s fix this. To add validation, we actually need to import the lovely Validators from @angular/forms and pass them in as a second argument to our FormControl instances:
ngOnInit() {
  this.user = new FormGroup({
    name: new FormControl('', [Validators.required, Validators.minLength(2)]),
    account: new FormGroup({
      email: new FormControl('', Validators.required),
      confirm: new FormControl('', Validators.required)
    })
  });
}
Rule: need multiple Validators per FormControl? Use an array to contain them.
This is now a replacement for adding <input required> to the DOM, which means we never have to touch it. Internally, when using required directives in template-driven forms, Angular will actually create this stuff under-the-hood for us, so that’s the main difference between the two implementations.
However, we are going to create [disabled] binding just like in the template-driven approach to disable the submit when the form is invalid:
<form novalidate (ngSubmit)="onSubmit(user)" [formGroup]="user">
  ...
  <button type="submit" [disabled]="user.invalid">Sign up</button>
</form>
All ready to go, now when we actually have validation errors, we need to now show them. When it comes to referencing the controls powering the errors, we must use the .controls property on the Object. Let’s say we want to show if there are any errors on the name property of our form:
<form novalidate [formGroup]="user">
  {{ user.controls.name?.errors | json }}
</form>
Tip: ?.prop is called the “Safe navigation operator”
We also have a .get() method that will lookup that control (I much prefer this as it’s a nicer API and avoids ?.errors):
<form novalidate [formGroup]="user">
  {{ user.get('name').errors | json }}
</form>
So, onto implementing the validation, we need to add the following to the correct portions of the form:
<!-- name -->
<div
  class="error"
  *ngIf="user.get('name').hasError('required') && user.get('name').touched">
  Name is required
</div>
<div
  class="error"
  *ngIf="user.get('name').hasError('minlength') && user.get('name').touched">
  Minimum of 2 characters
</div>

<!-- account -->
<div
  class="error"
  *ngIf="user.get('account').get('email').hasError('required') && user.get('account').get('email').touched">
  Email is required
</div>
<div
  class="error"
  *ngIf="user.get('account').get('confirm').hasError('required') && user.get('account').get('confirm').touched">
  Confirming email is required
</div>
Tip: The touched property becomes true once the user has blurred the input, which may be a relevant time to show the error if they’ve not filled anything out

Code so far

This is what we’ve achieved up until now:
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { User } from './signup.interface';

@Component({
  selector: 'signup-form',
  template: `
    <form novalidate (ngSubmit)="onSubmit(user)" [formGroup]="user">
      <label>
        <span>Full name</span>
        <input type="text" placeholder="Your full name" formControlName="name">
      </label>
      <div class="error" *ngIf="user.get('name').hasError('required') && user.get('name').touched">
        Name is required
      </div>
      <div class="error" *ngIf="user.get('name').hasError('minlength') && user.get('name').touched">
        Minimum of 2 characters
      </div>
      <div formGroupName="account">
        <label>
          <span>Email address</span>
          <input type="email" placeholder="Your email address" formControlName="email">
        </label>
        <div
          class="error"
          *ngIf="user.get('account').get('email').hasError('required') && user.get('account').get('email').touched">
          Email is required
        </div>
        <label>
          <span>Confirm address</span>
          <input type="email" placeholder="Confirm your email address" formControlName="confirm">
        </label>
        <div
          class="error"
          *ngIf="user.get('account').get('confirm').hasError('required') && user.get('account').get('confirm').touched">
          Confirming email is required
        </div>
      </div>
      <button type="submit" [disabled]="user.invalid">Sign up</button>
    </form>
  `
})
export class SignupFormComponent implements OnInit {
  user: FormGroup;
  constructor() {}
  ngOnInit() {
    this.user = new FormGroup({
      name: new FormControl('', [Validators.required, Validators.minLength(2)]),
      account: new FormGroup({
        email: new FormControl('', Validators.required),
        confirm: new FormControl('', Validators.required)
      })
    });
  }
  onSubmit({ value, valid }: { value: User, valid: boolean }) {
    console.log(value, valid);
  }
}

Simplifying with FormBuilder

This is where things get even smoother! Instead of using FormGroup and FormControl directly, we can use a magical API underneath that does it all for us. Meet FormBuilder!
First up, we’ll need to change our imports from this:
import { FormControl, FormGroup, Validators } from '@angular/forms';

export class SignupFormComponent implements OnInit {
  user: FormGroup;
  constructor() {}
  ...
}
To this (with additional constructor injection to make this.fb available as the FormBuilder):
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

export class SignupFormComponent implements OnInit {
  user: FormGroup;
  constructor(private fb: FormBuilder) {}
  ...
}
This is because user: FormGroup; on our component class is of type FormGroup. So, what is FormBuilder? It’s essentially syntax sugar that creates FormGroup, FormControl and FormArray instances for us (we’ll cover FormArray in another article). It’s just simple sugar, but now you know what it’s for.
Let’s refactor our code to use FormBuilder:
// before
ngOnInit() {
  this.user = new FormGroup({
    name: new FormControl('', [Validators.required, Validators.minLength(2)]),
    account: new FormGroup({
      email: new FormControl('', Validators.required),
      confirm: new FormControl('', Validators.required)
    })
  });
}

// after
ngOnInit() {
  this.user = this.fb.group({
    name: ['', [Validators.required, Validators.minLength(2)]],
    account: this.fb.group({
      email: ['', Validators.required],
      confirm: ['', Validators.required]
    })
  });
}
The refactoring is self-explanatory, but let’s roll over it quickly.
Instead of using new FormGroup() for example, we’re injecting FormBuilder as fb, and creating a new this.fb.group(). The structure of these are identical to creating the controls and groups by themselves, it’s just syntax sugar. Which leaves us with a component class that looks like this:
@Component({...})
export class SignupFormComponent implements OnInit {
  user: FormGroup;
  constructor(private fb: FormBuilder) {}
  ngOnInit() {
    this.user = this.fb.group({
      name: ['', [Validators.required, Validators.minLength(2)]],
      account: this.fb.group({
        email: ['', Validators.required],
        confirm: ['', Validators.required]
      })
    });
  }
  onSubmit({ value, valid }: { value: User, valid: boolean }) {
    console.log(value, valid);
  }
}

48 answers on StackOverflow to the most popular Angular questions I gathered the most common questions and answers from Stac...