Ninja Space Content Tech Blog

Week Six of Learning Angular

April 21, 2021
This week, I am going to be taking notes on reactive forms in Angular. Reactive forms is where you explicitly create the forms that's more complex, like implementing async validation.

Reactive Forms
In order to use formControlName, you'll need to import ReactiveFormsModule to the array on imports in the app.module.ts file. Then, it'll automatically add it as an import to the top of that page like so: 
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 
There was a bug that I kept getting following the first part of Programming with Mosh's Angular tutorial. The error was Object is possibly 'null'. This is related to my newer Angular version. I had to make sure the object is truthy in my && conditionals. Here's my html code that I have to fix it:  

<form [formGroup]="form">
    <div class="form-group">
        <label for="username">Username</label>
        <input 
            formControlName="username"
            id="username" 
            type="text" 
            class="form-control">
            <div *ngIf="username && username.touched && username.invalid" class="alert alert-danger">Username is required.</div>
    </div>
    <div class="form-group">
        <label for="password">Password</label>
        <input 
            formControlName="password"
            id="password" 
            type="text" 
            class="form-control">
    </div>
    <button class="btn btn-primary" type="submit">Sign Up</button>
</form>
 
When creating reactive forms, remember to import Validators at the top of the ts file. Here is my code as an example in the ts file:

import { Component } from '@angular/core';
import {FormGroup, FormControl, Validators} from '@angular/forms';

@Component({
  selector: 'signup-form',
  templateUrl: './signup-form.component.html',
  styleUrls: ['./signup-form.component.css']
})
export class SignupFormComponent {
  form = new FormGroup({
    username: new FormControl('', Validators.required),
    password: new FormControl('', Validators.required)
  });

  get username() {
    return this.form.get('username');
  }
}

Display Loader Image or Message
You can use the pending property to display a message, which is so nifty! Here is an example: 

<div *ngIf="username && username.pending">Checking...</div>
 
Simple Error for a Form Because of Angular Version
There as another thing that was different with my latest Angular version and it had to do with AbstractControl. In Programming with Mosh' Angular Tutorial, he used:

removeTopic(topic: FormControll) { let index = this.topics.controls.indexOf(topic);this.topics.removeAt(index); }
 
and this kept throwing a error in my terminal with the following:  Argument of type 'AbstractControl' is not assignable to parameter of type 'FormControl'.

It was I had to change FormControl to AbstractControl and then that fixed it. So my reactive form has the following code in the ts file:
import { Component} from '@angular/core';
import { FormArray, FormControl, FormGroup, AbstractControl} from '@angular/forms';

@Component({
 selector: 'new-course-form',
 templateUrl: './new-course-form.component.html',
 styleUrls: ['./new-course-form.component.css']
})

export class NewCourseFormComponent {
 form = new FormGroup({
 topics: new FormArray([])
 });

 addTopic(topic: HTMLInputElement) {
 this.topics.push(new FormControl(topic.value));
 topic.value='';
 }

 removeTopic(topic: AbstractControl) {
 let index = this.topics.controls.indexOf(topic);
 this.topics.removeAt(index);
 }

 get topics() {
 return this.form.get('topics') as FormArray;
 }
}
And my html file has the following code:

<form>
    <label>Form Array</label>
    <input 
    type="text" class="form-control"
    (keyup.enter)="addTopic(topic)" #topic
    >
    <ul class="list-group">
        <li 
        *ngFor="let topic of topics.controls"
        (click)="removeTopic(topic)"
        class="list-group item">
        {{topic.value}}
      
        </li>
    </ul>
</form>

FormBuilder
These section seemed a bit redundant to me so I didn't code it along with Mosh for this part. It was only a few minutes long. I will revisit FormBuilder some time again in the future.

Multi-Cursor Editing
The cool thing that learned during this particular session is multi-cursor editing. So I can just select the particular word or variable that I'd like to change, press Command and D as many times needed, edit my word and then it'll change those for me.

Abstract Control Error with Possible Null Value
While I was trying to get my validators working in a ts file, I kept getting an error about the object possibly being null when I used Mosh's code. 

In his code, he had:  

static passwordsShouldMatch(control: AbstractControl) {
        let newPassword = control.get('newPassword');
        let confirmPassword = control.get('confirmPassword');

        if (newPassword.value !== confirmPassword.value)
            return { passwordsShouldMatch: true};
            
            return null;
    }

I had to add two more conditionals to get rid of the compile error. Here's my code to fix the error:

static passwordsShouldMatch(control: AbstractControl) {
        let newPassword = control.get('newPassword');
        let confirmPassword = control.get('confirmPassword');

        if (newPassword && confirmPassword && newPassword.value !== confirmPassword.value)
            return { passwordsShouldMatch: true};
            
            return null;
    }

Then, when I was working with its html,  I also encountered object not null possibility error so here were my adjustments. I had to add a couple more truthy conditionals:  

<div class="form-group">
        <label for=''>Confirm Password</label>
        <input formControlName="confirmPassword" type="password" class="form-control">
        <div 
            *ngIf="confirmPassword && confirmPassword.touched && confirmPassword.invalid"
            class="alert alert-danger">
        <div *ngIf="confirmPassword.errors && confirmPassword.errors.required">New confirm password is required.</div>
        </div>
        <div *ngIf="confirmPassword && confirmPassword.valid && form.invalid && form.errors && form.errors.passwordsShouldMatch" class="alert alert-danger">Passwords do not match.</div>
    </div>

Whew, this form section was really hard for me to plow through but I did it! I'm about 29% of the way through the Programming with Mosh tutorial for Angular and it took me 6 weeks guys. I had another project I'm working on and also accepted a writing pitch so that took some of my hours away from this tutorial. However, I am eager to finish it. Read my list of week-to-week Angular notes.
 

Deploying React App with AWS Amplify

April 15, 2021
I created an AWS account 3 months ago but hadn't deployed anything on any of their services until last evening! A few months ago, I was having trouble with AWS Elastic Beanstalk and decided to put it in the back burner to learn Angular and TypeScript. Now, I'm ready to dig into AWS again. Instead of using AWS Elastic Beanstalk this time, which was what I originally had in mind, I decided to try out AWS Amplify to see if deploying my React app this way would be easier. It definitely is.

Here were my steps to deploy my React app on AWS Amplify: 
  1. I signed into my AWS account, and then clicked on AWS Amplify. Then, I had the choice to develop using their framework or just deploying. I'm only interested in deploying with the Amplify Console so I clicked on that deploy button option and then clicked on the GitHub link from there to authorize AWS to get access to my GitHub.
  2. Then after my GitHub was successfully connected, I selected the GitHub file that I needed to use to deploy and hit the "Next" button. I clicked "Next" again and then continued to deploy. Then, it sent me to a status screen where I patiently waited until all steps: Provision, Build, Deploy and Verify have taken place.
  3. I clicked on Verify and it says Build Duration was 1 minutes and 42 seconds. From there, under Domain, I found the link where my app was deployed at. Whoa, that was it! No CLI needed for just deployment on AWS Amplify. This was way easier than I thought!
Issue:
Initially, the main issue that I had was my Nodemailer under my Contact section was not working. Otherwise, everything is rendering great. It's because AWS Amplify is server-less so for backend stuff like Nodemailer, it will not work. I created the backend and front end in one app for this one. I will need to try to deploy it on AWS Elastic Beanstalk if I want a full stack app to deploy properly. However, I am pretty happy with how easy it is to deploy a front-end app on AWS Amplify. (Update: I found a workaround with this. See this blog entry on how I handled getting Nodemailer to work via AWS Amplify.)

The Best Thing:
The greatest thing about AWS Amplify that I love is when I do my regular "git push" command for my app, I can just simply click on the right angle bracket via AWS Amplify to update the deployment and I can easily watch it go through the Provision, Build, Deploy and Verify steps. This is a huge time saver! I didn't have to do an "npm run build" command  in my terminal.

Here is my working deployed app link via AWS Amplify: https://master.d27cizkq724af1.amplifyapp.com.
 

Week Five of Learning Angular

April 11, 2021
I am now getting into forms using the Programming with Mosh's Angular Tutorial. I'm in my 5th week of learning Angular and focusing this blog entry on the introduction to forms and narrowing in on Template-driven Forms. Click here to see my weekly breakdown of learning Angular.

Forms
In Angular, it is a requirement to write a div with a class of form-group for any forms like so:

 <div class="form-group">
    <label for="firstName"></label>
    <input id="firstName" type="text" class="form-control">
    </div>  

Zen Coding
One of the amazing things that I learned is zen coding from Mosh as I am going through this Angular tutorial. You can type something like the following: 

div.form-group>label[for='comment']+textarea[id='comment'].form-control and click tab, and you'll get the mark ups needed quickly for html. Then, this will generate the following: 

<div class="form-group"><label for="comment"></label><textarea name="" id="comment" cols="30" rows="10" class="form-control"></textarea></div>


Form Control
Angular has a class called FormControl and you can check the following: value, touched, untouched, dirty, pristine, valid and errors.

Form Group
FormGroup is a class that represents a group of controls in a form. All the attributes from the Form Control can also be seen from Form Group, which includes: value, touched, dirty, pristine, valid and errors.

You need 1 Form Group for a form and each input field has a Form Control to keep in track for each state of fields.

There are 2 types of forms: Reactive (previously called model-driven form) and Template-driven (with directives).  Reactive is good for complex forms. Template-driven is great for simple forms and easier to code with less code.

FormsModule
I received this error message when I was working with Forms: No directive found with exportAs 'ngModel'. What I had to do to use the ngModel directive in my html file is to go to my app.module.ts file and import {FormsModule} from '@angular/forms'; and also make sure I added it under the imports: array. That way I was able to implement this form below without compiling errors.

<form>
    <div class="form-group">
        <label for="firstName">First Name</label>
        <input ngModel name="firstName" #firstName="ngModel" (change)="log(firstName)" id="firstName" type="text" class="form-control">
    </div>  

    <div class="form-group">
        <label for="comment">Comment</label>
        <textarea ngModel name="comment" id="comment" cols="30" rows="10" class="form-control"></textarea>
    </div>

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

Adding Validation
You can add input validation alerts like so: <div class="alert alert-danger" *ngIf="firstName.touched && !firstName.valid">First Name is required.</div>

Error for Using *ngIf for Forms
I was following Programming with Mosh's tutorial line by line and for my typescript version, I kept getting this error: Object is possibly 'null'.

After digging into Stack Overflow, it's because with Mosh's typescript version, it wasn't an issue. For me I had to add the following conditional with an && to get the compiling error to go away : 
<div *ngIf="firstName.errors">First Name is required.</div>
<div *ngIf="firstName.errors && firstName.errors.minlength">First name should be minimum 3 characters</div>

After debugging for a little while, here's the working code below: 
  <div class="form-group">
        <label for="firstName">First Name</label>
        <input required minlength="4" maxlength="10" pattern="luis" ngModel name="firstName" #firstName="ngModel" (change)="log(firstName)" id="firstName" type="text" class="form-control">
        <div class="alert alert-danger" *ngIf="firstName.touched && !firstName.valid">
            <div *ngIf="firstName.errors && firstName.errors.required">First Name is required.</div>
            <div *ngIf="firstName.errors && firstName.errors.minlength">First name should be minimum {{firstName.errors.minlength.requiredLength}} characters</div>
            <div *ngIf="firstName.errors && firstName.errors.pattern">First name does not match pattern.</div>
            Error!
        </div>
    </div>
Form Classes 
Built into Angular are other classes like ng-form-control, ng-invalid, ng-touched and ng-dirty. To change the styling on your input when a user's input does not meet the criterias, you can utilized these classes to your advantage. Ex for styling: 

.form-control.ng-touched.ng-invalid { border: 2px solid red; }


ngForm Directive
You can also use ngForm to utilize the values of the properties and test by console logging it.

Template-driven Form Example
Here is a great full form example below starting with ts file under that component folder:

export class NewCourseFormComponent implements OnInit {
  categories = [
    {id: 1, name: 'Development' },
    {id: 2, name: 'Art' },
    {id: 3, name: 'Languages' },
  ]
  constructor() { }

  submit(course: any) {
    console.log(course)
  }

  ngOnInit(): void {
  }

Then, in html file:

<form #f="ngForm" (ngSubmit)="submit(f.value)">  
    <div class="form-group">
        <label for="name">Course Name</label>
        <input required minlength="3" ngModel name="name" #name="ngModel" type="text" id="name" class="form-control">
        <div *ngIf="name.touched && name.invalid"></div>
            <div *ngIf="name.errors && name.errors.required">Name field is required.</div>
            <div *ngIf="name.errors && name.errors.minlength">Min length should be {{name.errors.minlength.requiredLength}} characters</div>
    </div>
    <div class="form-group">
        <label for="category">Category</label>
        <select required ngModel name="category" #category="ngModel" id="category" class="form-control">
            <option value=""></option>
            <option *ngFor="let category of categories" 
            [value]='category.id'
            >{{category.name}}</option>
        </select>
        <div *ngIf="category.touched && category.invalid">
            Category field is required
        </div>
    </div>
    <div class="checkbox">
        <label for="moneyBackGuarantee">
            <input ngModel name="hasMoneyBackGuarantee" type="checkbox" id="moneyBackGuarantee" class="form-control">
            30-day money back guarantee?
        </label>
    </div>
    <p>{{f.value | json }}

    </p>
    <button [disabled]="f.invalid" >Create</button>
</form>
 

Week Four of Learning Angular

April 5, 2021
I'm on week 4 of learning Angular now! To see my weekly break down on learning Angular, go to Angular Blogs' List. What I'm realizing is that because I am note taking and coding along the way, a 5 minute video tutorial could easily have me spending 15 minutes to digest the material before moving onto the next video clip! If there is an unforeseen bug, it would take even longer. So make sure you allot enough time in between if you're studying the same way I am.

Using ng-content property
Use ng-content container as mark-up convenience like so:
<div class="panel-body">
    <ng-content select=".body"></ng-content>
</div>

Directives
Use ngIf for looping depending on a condition. We use directives to modify the DOM. There are two types: structural and attribute. Other directives include: ngSwitchCase and ngFor. One way is:  

<div *ngif="courses.length > 0">
  List of courses
<div *ngif="courses.length == 0">No courses yet

 Another way is:  

<div *ngIf="courses.length > 0; then coursesList else noCourses">
    List of courses
</div>
<ng-template #coursesList>
    List of Courses
</ng-template>
<ng-template #noCourses>No courses yet</ng-template>

You can also use the hidden property as well but this doesn't remove the elements from the DOM; it just hides it. Example of a hidden property:
<div [hidden]="courses.length == 0">List of courses</div>
<div [hidden]="courses.length > 0">No courses yet</div>

ngSwitchCase
I really like the ngSwitchCase directive. It's great for switching things back and forth for viewing. Here is the sample code:  
<div [ngSwitch]="viewMode">
    <div *ngSwitchCase="'map'">Map View Content</div>
    <div *ngSwitchCase="'list'">List View Content</div>
    <div *ngSwitchDefault>Otherwise</div>
</div>

Important Note
One thing that threw me off that's different from React is you need a comma after the last object in your array in Angular or else you get a compile error. This is the correct syntax below:  list = [
    {id: 1,name: "course1"},
    {id: 2,name: "course2"},
    {id: 3,name: "course3"},
   ];

ngFor Directive useful values
ngFor is really ngForOf. You can utilize other things in this loop like:  index as i (local variable), first, last, even and odd.

Ex: 
<ul>
    <li *ngFor="let course of courses; even as isEven">
       {{course.name}} <span *ngIf="isEven">(EVEN)</span>
    </li>
</ul>

Remove and Update on Change Detection
Angular is so intuitive. It can detect change and updates automatically. It is so easily to create a remove button for each element like so: 

Under ts file: 
  onRemove(course: any) {
     let index = this.courses.indexOf(course);
     this.courses.splice(index, 1)
   }

Under html file:
<ul>
    <li *ngFor="let course of courses; even as isEven">
       {{course.name}} 
       <button (click)="onRemove(course)">remove</button>
    </li>
</ul>


TrackBy
TrackBy is such a great tool to use when you want to improve performance with large data. Here's an example.
Under ts file:

  trackCourse(index: number, course: any) {
    return course? course.id : undefined;
   }

Under html file:
<button (click)="loadCourses()">Add</button>
<ul>
    <li *ngFor="let course of courses; trackBy: trackCourse">
       {{course.name}} 
      
    </li>
</ul>

Example of an Attribute Directive using ngClass
There's usually more than one way of making something work. Here an example of using a favorite icon with an attribute directive:
<span
    class="bi"
    [ngClass]="{
        'bi-star-fill': isSelected,
        'bi-star': !isSelected

    }"
    (click)="onClick()"
    >
    Star Test Icon
</span>

Styling with CSS
You can style like so:

<button 
    [style.backgroundColor]="canSave ? 'blue': 'gray'"
    [style.color]="canSave ? 'white' : 'black'"
    [style.fontWeight]="canSave ? 'bold' : 'normal'"
>
Save
</button> 

or use ngStyle directive to make it look cleaner like so:

<button 
    [ngStyle]="{
        'backgroundColor': canSave? 'blue': 'gray',
        'color' : canSave? 'pink' : 'black'
    }"
    >
Save
</button> 

Safe Traversal Operator
This is important to use when using complex data. When a value is null, you don't want the console to throw red errors. By solving this , you can use the safe traversal operator with a question mark like so: 

<div>
<span>{{task.assignee?.name}}</span>
</div>

Custom Directives

To create a new directive, go to terminal and type: ng g d directive-name
 

Week Three of Learning Angular

April 1, 2021
I'm on Week Three of Angular and I'm going to re-do a workshop again because I wanted to figure something out that wasn't working properly for me before. I'm coding along with Programming with Mosh's tutorial on Angular.

My Fixes
In the Section 14-Solution-Favorite-Component, Mosh wrote the following line in the favorite.component.ts file: isFavorite: boolean; and it showed a red squiggly line for me and my front end would not compile right. There were two different changes that I could choose in order to fix it. I could either declare the state as false like so: isFavorite = false; or I could add the exclamation point after my variable name like so: isFavorite!: boolean;. The reasoning why it worked for Mosh his way and not for me is because I am using a later version of TypeScript. His tutorial is a few years old. That is something you'll always have to keep in mind as a programmer. Things are always changing.

Component API
This is where the input is the state and the output is the event. You would need to import the Input decorator and also note  @Input() isFavorite!: boolean; in your variable to define an Input property.

Output Properties
For some reason, I had to go over this twice to get it to work. Just like the input part, I had to import Output and additionally, also import EventEmitter and to define the output I had to code the following:  @Output() change = new EventEmitter(); and then add the following as a method: onClick() { this.isTest = !this.isTest; this.change.emit(); } and then finally, in the html file I could implement this like so: <favorite [isfavorite]="post.isFavorite" (change)="onFavoriteChange()"></favorite>

Styles
There are three ways to style. What comes last will become effective. You can write in-line with a style tag. You can use styles through the component.css or add styles property in an array. There's a reference on Shadow DOM.
 

About Ninja Space Content


Ninja Space Content I have been building simple websites on my own since 2008 and currently run several websites at the moment, including this one. I used to be an account manager for an affiliate/e-commerce company, helping affiliates grow their sales so I have some knowledge on the business side and client side of affiliate marketing. During the Covid-19 pandemic, I completed a JavaScript coding bootcamp and graduated in Dec 2020. I've been working as a contractor for a few software companies ever since.
Note: links to resources and promoting special deals may allow me to earn a small commission from each sale.