This week I ran into an issue regarding a plugin we use at work called DataTables.  This plugin starts throwing errors when it is initialized more than once, and I could not figure out how this was happening.  It was random, and hard to reproduce.  I think I have the solution...

The DataTables are inside of a div that has an ngIf directive on it.  I figured that it would initialize the controller once the ngIf was true, and not re-initialize it, but I was wrong.

I created an example on CodePen to demonstrate the difference of how a controller is instantiated in both ngIf and ngShow.  There are two divs that contain a controller and an ngInit function.  The ngInit is called when the controller starts up and sends a message to the controller that is then displayed in an alert box.  Not the fanciest thing, but it gets us the information we need.

Interestingly, even though the first div (with ngShow) is hidden, it alerts immediately.  Toggling the button to show and hide it does not cause it to alert again.  However, the second div (with ngIf) does not alert until the toggle is clicked, and then again each time it is re-added to the DOM.

I ran into an issue the other day on our test environment at work.  It was a bug that I couldn't reproduce locally, and it drove me crazy.  Here is the link that angular was giving when the error occurred:

https://docs.angularjs.org/error/$injector/unpr?p0=eProvider{28a1f9697f743f8d6d06f2207fadc4e6d27e2b8eeb1f2d9901b57215d544ad7a}20{28a1f9697f743f8d6d06f2207fadc4e6d27e2b8eeb1f2d9901b57215d544ad7a}3C-{28a1f9697f743f8d6d06f2207fadc4e6d27e2b8eeb1f2d9901b57215d544ad7a}20e

Here is the code in question:

Again, it works fine locally, yet angular is telling me that the $injector could not resolve dependencies.  The dependencies it is referring to are the widgets and chartName, which are both set up in the resolve object, and passed into the controller function.


The Simple Fix

The solution: change the controller declaration to be an array, passing in the items to be injected, and having the last item be the function declaration:

Yep, just like that.  It appears that this is only an issue in minified code, and works fine otherwise.  It's a silly bug, but easy to fix once you know what the issue actually is.  Happy bug hunting, friends!

Angular uses dependency injection to new up objects when a component is generated.  This feature can also be used to re-use a service that is already instantiated.  This can help with performance if, for instance, the service being injected is used to fetch data from a server.  If this service is used in multiple components, we don't need to call the server for each component, we only want to do it once, and persist the data to all of the components.

Angular's providers attribute allows us to do this.  It also allows us to use a service as a singleton across components, but allow a specific component (and all of its children) to have its own instance of the service.


Project Setup

I have created a project to demonstrate this.  The code can be found here.

In this project, we have a service for storing a name.  It can be found in app/services/name.service.ts.  It comes with a default name (I used 'Chris'), and will be used by our components.  In order to use it, we need to provide it somewhere.  Since we will want singletons in our app, let's just put it at the very top of the component tree, in app/app.module.ts.  You will find the providers attribute here, providing this service to the rest of the app.  It is not instantiated at this time.


Components Using the Singleton

Let's create a component that will use the service as a singleton.  This can be found in the example code at app/components/shared-singleton/shared-singleton.component.ts.  You will notice that the constructor takes the NameService, and is using dependency injection.  The first time one of these components is added to the page, the NameService will be instantiated.  This shared-singleton component has very little code.  It is just here to show that we can see and edit the name from the service.

We can add multiple of these to the page and see how they behave.  You can see that we have added two of them in app/app.component.html.  Viewing this in the browser, you can see that editing the name in either of these two components will update the other.  This is proof that the two components are utilizing the same instance of the service, our singleton.o


I Don't Want to Share!

I want to re-use this amazing service in another component, but I don't want it to be linked anywhere else.  We should able to change the name somewhere on the app, but not have it update the other components.  This is where we can utilize the providers attribute again, to create a new instance of the service, and have it separate from the singleton we already were using.

The not-shared components (app/components/not-shared/not-shared.component.ts) is essentially the same component as our shared-singleton component.  The only difference here, is that we are adding the providers attribute, and giving it our NameService.

Now that we are providing the service at this component level, the dependency injection in the constructor of our not-shared component will create a new instance of the NameService.

We've added the not-shared component into the app along side the shared-singleton components.  Editing the name in one of these not-shared components will only update the data within that component.  The others have no knowledge of the service being used here.


Providers Are Amazing

In short, the providers attribute allows you to quickly and easily manage what components get what services, and when they should be instantiated.  Remember, any child components of a component or module with a provider given, will have access to the service if injected into the constructor.

Happy coding!

Previous: Part 1

Setup Firebase Authentication

Let's get started.  In the last post, we got our project setup locally and started a new Firebase app.  It currently has no data or anything setup.  Let's go ahead and get the authentication ready on Firebase's end so that we can move forward with coding.  Go ahead and log into your Firebase account and open up the new project you setup last time.  You should see an Authentication card.  Click 'Get Started'.

 

You should have an empty table.  Click the large 'Set Up Sign-In Method' button.  You will be prompted with a variety of options.  For this example, we will only be using Email/Password.  I may later come back and add social media logins, but for now, let's keep it simple.

 


Setup Authentication in Angular

Now that we have Firebase setup, let's get busy on our app.

Go ahead and run npm start to get your app running!

 

I like to give it the path to a directory called components.  This just helps me organize my code. Next we will need to install the firebase and angularfire2 modules.  We can use the shortcut 'i' for 'install'.

npm i firebase angularfire2 --save

We now need to import our AngularFire module as well as the authentication providers and methods into our app.  Inside of app.module.ts, add the following import:

import { AngularFireModule, AuthProviders, AuthMethods } from 'angularfire2';

You will also need to setup your configuration:

const myFirebaseAuthConfig = { provider: AuthProviders.Password, method: AuthMethods.Password };

Next, we will want to set the import for the module itself.

Uh-oh, we have the angry red squiggly line under myFirebaseConfig.  If you read the firebase docs, it shows how to setup the config right here in the module code.  I do not like this, especially if you are going to have this as a public repository on GitHub.  Let's make a new class that we won't check into the repo.

ng generate class firebaseConfig

Now, inside the newly created class, add the configuration:

static myFirebaseConfig = {
  apiKey: '<your-key>',
  authDomain: '<your-project-authdomain>',
  databaseURL: '<your-database-URL>',
  storageBucket: '<your-storage-bucket>',
  messagingSenderId: '<your-messaging-sender-id>'
};

Here's a trick to find the data you need.  In the Authentication page on Firebase, click the Web Setup link on the top-right.  You will be given a snippet of javascript that will contain everything you need.  I won't post an image since it contains my personal key.  Now, go ahead and open up your .gitignore, and add firebase-config.ts in here.  This will prevent your configuration code from accidentally being committed to the repository.

Now, go back to app.module.ts and update the import for the AngularFireModule

AngularFireModule.initializeApp(FirebaseConfig.myFirebaseConfig, myFirebaseAuthConfig)


Creating the Authentication Component in Angular

Let's create a new component to keep our authentication separate from anything else.  This will allow us to easily move it or reuse it later.  We will use the Angular CLI for this.  We can use the shortcut 'g' for 'generate'.

ng g component components/authentication

Let's put the new fancy component into our app.  Looking at authentication.component.ts, there is a selector in the @Component decorator.  I'm going to rename mine to just authentication.  Now, over in the main root component, app.component.html, we can add the authentication component.

<h1>{{title}}</h1>

<authentication></authentication>

Your app should now display the authentication component:

Well, that's not exactly what we want...  Let's fix it.  We will worry about styling the component later, right now we want to focus on just getting it to work.

<div class="form form-authentication">
 <div class="form-group">
   <label for="inputEmail">Email: </label>
   <input id="inputEmail" type="email" [(ngModel)]="email" />
 </div>

 <div class="form-group">
   <label for="inputPassword">Password: </label>
   <input id="inputPassword" type="password" [(ngModel)]="password" />
 </div>

 <button (click)="register()">Register</button>
</div>

We now have a basic form, and a button wired up to a function that we need to write, and the two input fields are set with two-way data-binding to fields with the same name.  Let's setup our component class.

export class AuthenticationComponent implements OnInit {

 email: String;
 password: String;

 constructor() { }

 ngOnInit() { }

 register() { }

}

Now we have everything wired up to our component, we can handle everything in typescript.  First, we need to import AngularFire2 into our component.  Add this to the top of the file:

import { AngularFire } from 'angularfire2';

Set the constructor to inject it into the component:

constructor(public angularFire: AngularFire) { }

We can now access it by using this.angularFire anywhere in our component.  Let's wire up the register function:

register() {
 this.angularFire.auth.createUser({
   email: this.email.toString(),
   password: this.password.toString()
 });
}

Now, users can register on the site!  If you look at your firebase authentication page and refresh it, you can now register your email address and password.  After doing so, you will see your email address listed as a user!  We still need to style the component, handle exceptions (such as if the user is already registered), and create a login form.  We will continue that next post!


Handling User Data from Firebase

Let's do one last thing before wrapping up Part 2.  Let's get the current user object and hide the registration form when the user is already logged in.  Add the user object to the authentication.component.ts file:

...
export class AuthenticationComponent implements OnInit {
  email: String;
  password: String;
  user: any;
...
Now, in the ngOnInit function, let's subscribe to the angular fire's auth object, and get the data.  The subscribe we are doing here is basically just waiting until firebase has returned with the auth data, and then we tell it what to do once it is loaded.
this.angularFire.auth.subscribe(auth => this.user = auth);

Great, now we have a property on our component that will hold the user information.  Let's hide the component when the user is logged in.  In your authentication.component.html file:

<div class="form form-authentication" *ngIf="user === null">

We are now saying that we will display everything inside this form when the user is null.  That's it!  I've added my own styling (it's not that pretty) to the github repo, but feel free to style it yourself!

In the next post, we will allow the user to switch between the registration form we have now, and a login form.  We will also add the ability to log out of the app.  After that, it's on to building the address book functionality.

Project Setup

This will be a multi-part post in which we will build an address book app using Angular (well, Angular 2, but they call it 'just Angular' now) and Firebase.  I really like using Firebase as a backend, especially for proof of concepts and demos.  It allows us to focus on the front-end and not have to deal with standing up a RESTful API.  It also comes with cool authentication built in.


The Tools we Need

Firstly, you will need to have nodejs installed.  Node will be used to install NPM packages used in the project.  Next, go ahead and install the super handy Angular CLI (Command Line Interface).  You can do so easily in your command prompt by typing:

npm install -g @angular/cli

If you aren't used to using npm, the -g stands for global, meaning once you run this once on your computer, you won't need to do it for future projects.  Now that we have the Angular CLI installed, we can simply stand up a new project by typing:

ng new angular-address-book --style=scss

Here we are telling the CLI (the ng command) that we want a new project, called angular-address-book.  We are also telling it to build it using SCSS as our CSS pre-processor.  To see our app working, simply type:

npm start

This will run the ng serve command (you can see all of the different scripts registered for this project inside of the package.json file).  Go ahead and go to http://localhost:4200.  You will see your beautiful app running.  Keep this command running in the background as you work on your project.  It will automatically reload the page for you whenever you change a file, and will warn you when you make mistakes.

The last tool that we will be using is VS Code.  This editor is free from Microsoft.  I used to be a hardcore Atom fan, but for Angular2, I prefer VS Code.  It's up to you what editor you use.


Angular Project Overview

Let's go ahead and open our fancy new project in our editor and see what we're working with.  We will be focusing on the files under the src directory.

The project is already setup with our index.html starting point.  If you look inside the file, you will see only one line inside the <body> tags:

<app-root>Loading...</app-root>

This is where we will bootstrap our app into our webpage.  The Loading... text will disappear as soon as it is loaded.  Right now the app is so small, you probably won't even see it when loading it in your browser.

The styles.scss file will be used for our global styles.  This is where we will define global fonts, sizes for common elements such as h1 and p tags, etc.  We really won't do much outside of the app directory.

The App Directory

Ahh, the App directory.  This is where we get to have some fun!  The CLI has already given us a starting component.  We will talk more about components in the future posts.  Go ahead and peek at app.component.ts.

This is where the app gets bootstrapped.  See that selector of 'app-root'?  Yep, that matches what we saw in the index.html file.  The HTML that is rendered can be found in the app.component.html, and any styling we want to apply here will be found in app.component.css.  Easy, right?

Tip!

You may see in the angular.io examples the use of moduleId: module.id inside of the @Component decorator. The Angular CLI's webpack setup already uses relative paths for the other attributes of the @Component decorator, so we do not need it.


Setup Firebase

Firebase is a BaaS (Backend as a Service) that we will use for our real-time database.  If you don't already have an account, go ahead and sign up.  Don't worry, it is free until you start having a lot of users and data using it.  Once in your console, click the Create New Project button, give it a name, and you're done!  In the next lesson, we will work to setup authentication in both Firebase and in our Angular application.

Feel free to follow along with my GitHub repository.  Each part will be separated out so you can check your work and follow along.

Next: Part 2 - Authentication

Copyright © 2019 Chris Perko

linkedin facebook pinterest youtube rss twitter instagram facebook-blank rss-blank linkedin-blank pinterest youtube twitter instagram