Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

Sorry, you do not have permission to ask a question, You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please type your username.

Please type your E-Mail.

Please choose an appropriate title for the post.

Please choose the appropriate section so your post can be easily searched.

Please choose suitable Keywords Ex: post, video.

Browse

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Navigation

  • Home
  • About Us
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • About Us
  • Contact Us
Home/ Questions/Q 1811

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Latest Questions

Author
  • 62k
Author
Asked: November 25, 20242024-11-25T11:37:08+00:00 2024-11-25T11:37:08+00:00

Homemade dialog service in Angular

  • 62k

Let's put all our knowledge in creating components programmatically in Angular 16 to good use. Let's create a Dialog service, that allows us to host any component. We already covered the major features in Rose Dialog in our StackBlitz project. Today we dig into the following issues to get closure:

  • Style it to properly look like a dialog, and pass extra css
  • Add extra event handlers to close
  • Keep references of opened dialogs
  • Housekeeping

Find the final Dialog service in StackBlitz project, under lib/Dialog folder.

Styling

The sweetest part. We shall add a css file to the service, and style just enough to make it look right. In the wild, we should rely on a global stylesheet for coloring and measurements. We use Less or Sass for that, but I am leaving that part out to you. The idea is simple, make an overlay of semi transparent black, and host the dialog in the middle of it.

<!-- lib/Dialog/partial.html --> <div class="dialog-overlay">    <div class="dialog">       <div class="dialog-header">          <h6 class="dialog-title" id="dialogtitle">{{ title }}</h6>          <button type="button" class="dialog-close" (click)="close()">❌</button>       </div>       <div class="dialog-body">          <ng-content></ng-content>       </div>    </div> </div> 
Enter fullscreen mode Exit fullscreen mode

The bare minimum styles; which I promise will need more work, and may be done better within a couple of months as CSS quickly advances.

/* lib/Dialog/styles.css */ .dialog-overlay {   position: fixed;   top: 0;   right: 0;   bottom: 0;   left: 0;   z-index: 1030;   background-color: rgba(0, 0, 0, 0.6);   display: flex;   align-items: center;   justify-content: center; }  .dialog {   background-color: #fff;   width: clamp(300px, 75vw, 90vw);   z-index: 1040;   overflow: hidden;   outline: 0;   display: flex;   flex-direction: column;   max-height: 90vh; }  .dialog-header {   display: flex;   justify-content: space-between;   align-items: center;   padding: 1.2rem; }  .dialog-body {   position: relative;   flex: 1 1 auto;   padding: 1.2rem;   overflow-y: auto; } 
Enter fullscreen mode Exit fullscreen mode

Catch click and escape events

A nice feature to add is to hide the dialog when user clicks on the overlay or presses escape. We can add that directly to the dialog component.

// lib/Dialog/partial.ts  export class DialogPartialComponent {   // ...    // close on overlay click   @HostListener('click', ['$event.target'])   onClick(target: HTMLElement): void {     // find d-overlay (add it to the dialog-overlay)     if (target.matches('.d-overlay')) {       this.close(null);     }   }    // close on escape as well   @HostListener('window:keydown', ['$event'])   onEscape(event: KeyboardEvent): void {     // hide on escape     if (event.code === 'Escape') {       this.close(null);     }   } } 
Enter fullscreen mode Exit fullscreen mode

I added a new class to the .dialog-overlay named .d-overlay, because I formed a habit long ago:

Never mix classes used in styles, with those used in scripts. It always bites back.

Add extra css

We can create a new option property to pass extra css to our dialog component, for maximum control.

// lib/Dialog/service  // add extra option in open method public open(c: Type<any>, options?: {       // ...       css?: string, ) {   // ...    // get dialog element first   const dialogElement = (<EmbeddedViewRef<any>>componentRef.hostView)     .rootNodes[0];    // add css to the element root   if (options?.css) {     dialogElement.classList.add(...options.css.split(' '));   }    // then append   this.doc.body.append(dialogElement);    // ... } 
Enter fullscreen mode Exit fullscreen mode

For example we can define our window to be a full screen dialog, then pass it like this

// pass a different cs this.dialogService.open(MyPeachComponent, {   title: 'Peach',   css: 'dialog-full-screen' }); 
Enter fullscreen mode Exit fullscreen mode

I have included in StackBlitz some different styles, have a look at the Final component. We can do something like this:

css: 'dialog-half-screen reverse animate fromright',

This would open a half screen dialog, and animates its position from the right.

Keep references of opened dialogs

It would be nice to be able to get a reference of an open dialog from anywhere in the app, by simply targeting its ID. Like this

// find the dialog by id and close it this.dialogService.get('uniquePeach')?.close(); 
Enter fullscreen mode Exit fullscreen mode

To implement that, we first need the ID to be a passable option. then we can collect them to a new property: dialogs

// lib/Dialog/service  // keep references of opened dialogs dialogs: { [key: string]: DialogPartialComponent | null } = {};  public open(c: Type<any>, options?: {   //...   // add id   id?: string, }) {    // get referecne to root element   const dialogElement = (<EmbeddedViewRef<any>>componentRef.hostView)     .rootNodes[0];    // and assign it the id   if (options?.id) {     dialogElement.id = options.id;     // add to collection     this.dialogs[options.id] = componentRef.instance;   }   // ...    // when closed destroy   const s = componentRef.instance.onClose.subscribe((res) => {     // get rid of reference     if (options?.id) {       delete this.dialogs[options.id];     }     // ..   });   // .. }  // then get it public get(id: string) {   // find the dialog ref component in collection   return this.dialogs[id] || null; } 
Enter fullscreen mode Exit fullscreen mode

So next time we want to track a dialog, we simply pass it a unique ID. This probably needs a bit more work to guarantee uniqueness of ID, I'll let you work that out as you pleased.

Housekeeping

A little bit of housekeeping for the code includes the following

  • Destroying the child: upon closing and destroying the componentRef, I forgot to destroy the childRef!
  • Removed the onChildClose event. We can simply call the close method directly on the dialog reference passed.
  • Options need an interface: IDialogOptions. It now looks like this
// lib/Dialog/service  // the dialog options interface export interface IDialogOptions {   title?: string;   data?: any;   css?: string;   id?: string;   onclose?: (res: any) => void;   providers?: StaticProvider[]; } 
Enter fullscreen mode Exit fullscreen mode

I could think of other enhancements to make, but that should do for now.

Note on the side, once you start using it, ExpressionChangedAfterItHasBeenCheckedError error will almost always fire, I do not bother much because I do not believe there is a solid solution for that. It is a development environment warning only.

Let's use it? Find the example in components FinalComponent.

Thank you for reading this far, where you able to make use of this Dialog?

angulartutorialtypescriptwebdev
  • 0 0 Answers
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

Sidebar

Ask A Question

Stats

  • Questions 4k
  • Answers 0
  • Best Answers 0
  • Users 2k
  • Popular
  • Answers
  • Author

    ES6 - A beginners guide - Template Literals

    • 0 Answers
  • Author

    Understanding Higher Order Functions in JavaScript.

    • 0 Answers
  • Author

    Build a custom video chat app with Daily and Vue.js

    • 0 Answers

Top Members

Samantha Carter

Samantha Carter

  • 0 Questions
  • 20 Points
Begginer
Ella Lewis

Ella Lewis

  • 0 Questions
  • 20 Points
Begginer
Isaac Anderson

Isaac Anderson

  • 0 Questions
  • 20 Points
Begginer

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help

Footer

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore, ask, and connect. Join our vibrant Q&A community today!

About Us

  • About Us
  • Contact Us
  • All Users

Legal Stuff

  • Terms of Use
  • Privacy Policy
  • Cookie Policy

Help

  • Knowledge Base
  • Support

Follow

© 2022 Querify Question. All Rights Reserved

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.