Angular 13: How to make a clickoutside Directive with RXjs

Going outside with clickoutside directives

Hi all, it has certainly been a while, even though the only comments I get are from bizarre spam bots. As always there is a TLDR for you down below. So lets get into it.

Have you ever been in a situation where you make a lovely custom dropdown menu for someone and you do not know how to close the dropdown when you or they click away?

Well this is where a directive comes in and we even get to use fancy Rxjs in the mix.

Directives are defined as classes that can add new behavior to the elements in the template or modify existing behavior.

First off you will need to create your directive would recommend using the Angular CLI tool on this one.

// Angular generate new service
ng g s servicename

[Pro tip]: If you are unsure where the CLI tool will place your directive you can use the extension –dry-run.

We will be using RXjs in this directive to listen to the clicking of the element outside the assigned element

TLDR here is the Directive code to handle the clicking outside directive. Be sure to declare the Directive in your module that you are using said directive.

import { DOCUMENT } from "@angular/common";
import { AfterViewInit, Directive, ElementRef, EventEmitter, Inject, OnDestroy, Output } from "@angular/core";
import { filter, fromEvent, Subscription } from "rxjs";

@Directive({
    selector: '[appClickOutside]',
})

export class ClickOutsideDirective implements AfterViewInit, OnDestroy {
    @Output() appClickOutside = new EventEmitter<void>();

    documentClickSubscription: Subscription | undefined;

    constructor(
        private element: ElementRef,
        @Inject(DOCUMENT) private document: Document,
    ) { }

    ngAfterViewInit(): void {
        // you are listening for a click anywhere on the document
        this.documentClickSubscription = fromEvent(this.document, 'click')
            .pipe(
                filter((event) => {
                    // the Rxjs filter will return when a click is not within the directive element
                    return !this.isInside(event.target as HTMLElement);
                }),
            )
            .subscribe(() => {
                // upon the case of element clicked outside the subscription will be emitted
                this.appClickOutside.emit();
            });
    }

    ngOnDestroy(): void {
        this.documentClickSubscription?.unsubscribe();
    }

    isInside(elementToCheck: HTMLElement): boolean {
        return (
            elementToCheck === this.element.nativeElement ||
            this.element.nativeElement.contains(elementToCheck)
        );
    }
}

How does this directive work?

The finer detail is that the subscription is being used to subscribe to specifically where the user is clicking on the document. The pipe contains a filter which checks if the user has clicked off the element or on the element, this is where we subscribe to said subscription to when the event has been clicked outside the element. The emitter will emit the appClickOutside emitter.

I don’t care how it works, what else must I do to get this setup in my project!

Assuring that you have created your directive and have declared it in your module next you will need to update the view. On the view of your component you will need to add the appClickOutside directive to the element that wraps around your custom dropdown menu, as you can see on line 2 I have a dropdown wrapper class to ensure that I have the area of the dropdown covered so you may need to specify the height or width depending on your layout.

If the click off is not quite working, I’d advice to take look at your styles and element wrapper.

Ya that is about it. Feel free to take a look at my working sample on github if you are facing any challenges

How to setup JEST with ANGULAR incl working sample

unit testing

I wanted to make a whole story on this, but i’d rather try make this document as helpful and to the point as possible, hope this helps you setup jest in your Angular project.


import jest setup that is found in the jest-preset-angular.
This will be in your test.ts file

// add to test.ts file
import 'jest-preset-angular/setup-jest';

Add the following jest packages the the package.json

 // package.json file
 
 // update your test script to
    "test": "jest",
 
 // Packages to add to devDependencies
    "jest": "^27.3.1",
    "jest-preset-angular": "^10.0.1"

After adding the jest packages be sure to run npm install, you can also remove the karma packages to keep things tidy.
In the project’s tsconfig.json add the following to your compilerOptions object:

// add the following in your tsconfig.json
...,
  "angularCompilerOptions": {
    "fullTemplateTypeCheck": true,
    "strictInjectionParameters": true
  }

Run the following command, this will help transcript jest to typescript seeing that jest is JavaScript and will need to work with type script.

npm i ts-jest

Create a jest.config.js file in the base of your project ‘src/jest.config.js’

const { pathsToModuleNameMapper } = require('ts-jest');
const { compilerOptions } = require('./tsconfig.json');

module.exports = {
    preset: "jest-preset-angular",
    setupFilesAfterEnv: ["<rootDir>/src/test.ts"],
    testMatch: ["**/+(*.)+(spec).+(ts)"],
    moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths || {}, {prefix: "<rootDir>/"})
}

I think that that should sort you out. With regards to the different versions of Angular and Jest, and its angular preset packages, they seem to be inline with the angular version number, like my project is angular 11 and I have another using 12 and the preset 10 and up seems to cover it. I also have used it on an Angular 9 project and the jest-preset-angular version was 9.0.4

EXTRA SETUP NOTE FOR JEST
I had to added the Jest to the types in the tsconfig.spec.json, so that my unit test files have access to jest.

Sample project with Jest setup already:
sample-project

Working with component instances & @ng-bootstrap’s pop up modal

pop up modal

I’m gonna cut to the chase, so no story time here.

First off here is the documentation for the @ngbootrap Modal !

To install and setup first
run this command line to get access to the @ng-bootstrap/ng-bootstrap node modules.

npm i @ng-bootstrap/ng-bootstrap

Once you have installed the files make a new component for the Modal itself

You would need to import the NgbModal with the line below.


// import into modal.component.ts file
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';

For this sample I am using the NgbModal. If you take a look at the documentation it has a list of APIs associated with the NgbModal and we are going to use some of the basic samples to get this up and running.

For the modal.component.html view what is nice is that you can use conditional rendering if you want to hide some of the details. or comment it out…I was having issues toggling and where to hide and show these details and I think this is the simplest way to manage with my specific demo project.

I have a working Angular 9 project that I have setup to emit a value of a userProfile component that contains the details of Name, Job and bio.

I was thinking of going through the code line by line but you’ll need to pass data to the component and through the emitter the function will emit the value to the parent component and now to create the component instance. So in the view you can see I’m using the event emitter value from the card profile component, so
this ‘(selectedProfile)= parentFunction(userProfile)” is going to help us create this component instance of the bootstrap modal in the main app component .

  parentfunction(userProfile:CardProfile) {
    const modalReff = this.modalService.open(ModalComponent, { size: 'sm' });
    const componentInstance = modalReff.componentInstance as ModalComponent;

    componentInstance.userProfile = userProfile
  }

The above is where the magic happens, upon clicking the parentFunction it’ll create a const modalReff. from there you can reference the componentInstance and should have access to the ModalComponent’s inputs and variables.

Granted its a bit of setup work but this is how one would use a component instance in a project

If you have any issues you can check out the working sample project right here

this carries examples of inputs and outputs

BASIC MONGODB COMMANDS

basic bottles of mongoDB commands

Hi all, this is just my personal dev notes that I keep here so I can get a reminder from time to time. I’ve recently started to play around with Mongdb with great success using the MEAN stack approach, so I wont be going through the setting up but just some basic commands to get you going and started.

RUN COMMAND

mongo

CREATE A NEW TABLE COMMAND

use DBNAME

Once successful you will see the command line mention that you have “switched to db DBNAME”

SHOW DATABASE LIST COMMAND

show dbs

The terminal will list the available databases.

INSERT DOCUMENT INTO DATABASE

db.DBNAME.insert({
         firstName: "Tony", 
         lastName: "Marques",
         avatar: "01"
})

// result should show 
WriteResult({ "inserted" : 1 })

Hope this helps you quickly along. I am using MongoDB Compass Version 1.29.5 and node v14.18.0

Angular how to make reuseable components with @Output Part 2

If you have read my first post covering @inputs then be sure to give that a quick read before continuing.

In the previous post we have covered all our inputs and you might be thinking what about @outputs, well lets make one. When you have an @output it would emit a value up to the parent component, I believe there are many ways to do this, but all I’m trying to say is that @output requires at least an emmitter. As seen in the card.profle component. (picture below).


Update the following code in the app.component.ts file:
  data = [
    {
      name: 'Jonny Doe',
      job: 'FrontEnd Gopher',
      bio: 'I am a Gopher that likes to FrontEnd Gophe',
    },
    {
      name: 'John Dear',
      job: 'Dear FrontEnd',
      bio: 'I am a Dear that likes to dearly FrontEnd',
    },
    {
      name: 'Tony Tones',
      job: 'FrontEnd Developer',
      bio: 'I am a Tony Tones,I use JavaScript, TypeScript and Angular Framework',
    },
  ];

  ngOnInit() {
    this.userProfiles = this.data;
  }

Refresh and you should have 3 loverly gophers staring at you.

In this example we will link the follow button to emit the username of the profile selected to the parent component. This exercise we will use the eventmitter to help us emit the profile name selected to the parent component.

Line 13 we have our @output named btnClick connected to our new eventEmitter of type string (for our name variable).

We use the buttonClicked() function and pass the variable name in the buttonClick(name), which we have declared in our cardprofile component and the next step is to link it on the app.component.html and you can see the emitter btnClick.

And finally to add the function in the app component

Now spin up the project and open the dev tools and click on one of the profiles.

Here we are console logging the profile username to the parent component and thats about it.


Feel free to fork my github, here is the source code.

Dev Notes on Angular: Re-useable components with @Input() decorator (part 1)

Using reusable components and passing data to them. There are a number of ways to pass data to and from parent to child, but I will be focusing on the @Input and @Output decorators. I’m hoping to go through both these guys and have a working angular project with working examples with TypeScript models.

Setting up


I would start by just making a new component via the cmd.

// Generates a new component into the "components" folder.
ng g c components/cardprofile
// or
ng generate component components/cardprofile

From there you will need to build your ideal card UI and take note on what data you would like to populate into said component.

// Just add the card component selector to the app.component like so
<app-card-profile></app-card-profile>
My favorite frontEnd Gopher

Not the prettiest I’m no designer, but this is a card profile component none the less. I think its fine to start with some hard coded data. Just note the details we want to add dynamically, like Name, Job and bio.

In the card profile component i made the following object just for a single entry.


// set within the card profile component for hardcoded data
      name = 'Jonny Doe';
      job = 'FrontEnd Gopher';
      bio = 'I am a Gopher that likes to FrontEnd Gophe';

In the card.profile.component.html view I updated the fields with string interpolation for name, job, bio fields.


// card profile component html
<div class="container">
  <div class="card">
    <div class="banner-image">
      <img class="cover" src="../../../assets/images/profilebgimage.PNG" />
    </div>
    <img class="profile" src="../../../assets/images/gopher.PNG" alt="Avatar" />
    <div class="footer">
      <h1 class="name">{{ name }}</h1>
      <p class="description">{{ job }}</p>
      <p class="description">{{ bio }}</p>
    </div>
    <button class="btn">Follow</button>
  </div>
</div>

Now we have a working card that can accept values and render them in the view!

A good practice would be to create a modal for that data structure, so I made a Card interface.

export interface CardProfile {
    name: String,
    job: String,
    bio: String,
}

Implementation of @input()

Now we import our @Input() from ‘@angular/core’ for the Card component and make an @input for each piece of data we want to populate in the card component.

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


@Component({
  selector: 'app-card-profile',
  templateUrl: './card.profile.component.html',
  styleUrls: ['./card.profile.component.css'],
})
export class CardProfileComponent {
  @Input() name: String;
  @Input() job: String;
  @Input() bio: String;
}

Now that we have our inputs set for the card component we’ll need to pass through the data from the app.component (Parent component) into the card component’s selector, so we’ll need to add the inputs in the card component selector as seen below <app-card-profile> is the child component and we are using it in the app.component.html.


// Add this to your app.component.html
<app-card-profile [name]="userProfile.name" 
          [job]="userProfile.job" 
          [bio]="userProfile.bio">
</app-card-profile>

Now for the data to come from the app.component.ts into the card component via the app.component.html view.

import { Component, OnInit } from '@angular/core';
import { CardProfile } from './models/cardProfile.model';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
  title = 'input-output';

// userProfile with type cardProfile
  userProfile: CardProfile;

// object that we are going to pass into the card component
  data = {
    name: 'Jonny Doe',
    job: 'FrontEnd Gopher',
    bio: 'I am a Gopher that likes to FrontEnd Gophe',
  };

  ngOnInit() {
// setting the userProfile with the data...data
    this.userProfile = this.data;
  }
}

Now to add multiple card profiles! Lets setup the data first and make our userProfile to be userProfiles array…. which i’ll save for the next post.
If you faced any problems have a look at source code on this repo

Deploying angular cannot find js files?

Deploying Angular

Hey gents and ladies, as always there is a TLDR section for all of you. From time to time I need to deploy an angular project to my hosting site and just about everytime I try I come across the same issue.

I have a site hosted by Gator and I have access to the file manager where I can upload my Angular projects and what not.

Just about everytime I come across the following issue and at first it always baffels me.

First is create a folder on my gator file manager and give it the name of my angular project and then do a ng build which creates a dist folder and then upload the dist folder contents (mainly js files) into the folder I created on the gator file manager ( that I pay a hefty price for) then this happens.

Then I think to myself, how does one fix this issue?
Upon closer inspection you will see that the file structure is not quite right. When selecting one of the failed js files have a look at the Request URL. It appears to have skipped my Angular project file name and tries to find the files at the base of my URL as seen below when highlighting main.js.

TLDR

I came across this Stackoverflow solution , which in the command terminal the command will set the baseUrl for the distribution file to find the proper location of said files. I forget the command line so this is a personal note for me.

ng build --base-href=/ANGULARPROJECTNAME/

This will update the file structure and the results after deploying and uploading the appropriate files should look something this. Make sure your File naming convention is inline, I won’t lie this took me a few tries and yes my project is called fireCrud. I had to update my angular.json file to fix my naming convention.

Here is the stackOverflow solution that I referenced

Enjoy and happy deploying. Hope this helps

Code review notes

learning the ropes

As always there is a TLDR section highlighted in bold.
These are just my personal notes and some tips and tricks I have picked up from my peers in my current job and naturally very open to any feedback on my topics I post.

Code Reviews


Now, I would normally use the prettier formatter to organize my code, but at my job I was advised not to do that due to some of the code being in a layout that is easier to read and work with. My auto formatter happens to mess it up, so I started to check my code format by eye, which is far from efficient and takes a bit of time ..sometimes. I just felt I had to share why I cannot just use a auto formatter.

Check list

TLDR

  • Use your editor search for console logs and remove ’em
  • Also removed unused code/imports to your project
  • use ESLint and make sure it passes (this is my next post trying to set it up now)
  • Run your unit tests, I’ve had my review approved and be denied by unit tests failing
  • Important make sure your naming conventions make sense so other developers can follow
  • Personally I think it is perfectly fine to leave commented notes on certain methods that are helpful! Such as // This section cleans up the goods and returns so and so
  • Create the PR ( Peer Review/ Merge Request..etc) and go through the comparison. The layout shows exactly where changes have been made and I tend to catch a lot of my syntactical errors at this point.

That’s all I have for now, these tips came from my colleagues and also through my own experiences. Checking is good but checking only once is even better.
Good luck and I hope to hear some comments on this one.

How to add material design icons on Angular 11

material icons

Hi guys as always i have a TLDR section so that people don’t have to deal with my sob story of the week. These are just my personal notes that I’ll go back to as to when I want to add material design icons to my project.

So, I have seen this before and of course there are a number of options as to how to install material design icons for your angular project, this is the solution that worked for me on my personal project.

TLDR
please note, if you have a project already built be sure to delete your node_modules in case of any problems.

// use this command in gitbash
npm install --save @angular/material && @angular/cdk && material-design-icons

This would be my first step the npm site has good documentation on how to set everything up but i’ll just continue on what i did next.

Add the following link in you index.html file

<link href="https://fonts.googleapis.com/icon?family=Material+Icons"
      rel="stylesheet">

Now you’ll need to do some imports to your app.module.component.ts file it should look something like this.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

// Material stuff
import { MatIconModule } from '@angular/material/icon';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

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

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, BrowserAnimationsModule, MatIconModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

I just added a basic material icon examples here for the view app.component.html.

<div class="container">
    <h2>Material icon samples</h2>
    <span class="material-icons">face</span>
    <i class="material-icons">visibility</i>
    <mat-icon>home</mat-icon>
</div>

That should be it, I have a repo that has the material design’s icon on an Angular 11 project so it should be straight forward to compare. Have a great weekend!

POST EDIT
I came across an issue when following my step, if you have any compiling issue after setting up the icons run this command to install this version of material icons

npm i @angular/material@11.2.13

stackoverflow: solution here

How to add scss to your Angular project fast

scss new niece

Of course as always there is a TLDR thing somewhere down there.
I come across this from time to time and I normally forget all about it…then I just end up always using css (come on I can not be the only one)

Lets get to it, you have either built or going to build an angular project. In the past i have seen it ask and say “you wanna css, scs,s sasax and …” other options.
so i’d normally select scss and then it just worked.

TLDR COMMAND FOR ANGULAR PROJECT ALREADY BUILT
If you have already built an angular project then run the following line of code.

ng config schematics.@schematics/angular:component.style scss

This should add the style as scss in your angular.json package
for more in-depth detail you can read up on the source. on the one that has 557 votes oh wait 558.

ANOTHER THING TO NOTE
if you get the following error:
“Error Module not found: Error: Can’t resolve PATH in PATH or “didn’t return string.

Be sure to update your component’s style location cause it will still point to your component.css file


In the reference above, they also show you how to add scss to a new project by use of the following command:
TDLR COMMAND FOR NEW ANGULAR PROJECT

ng new NEWANGULARPROJECTNAME --style=scss 

Ok that’s it good luck
If this solution did not work for you, please feel free to comment. I know there never is a solution for everything.