Angular 7 Material Modal

Overview

Getting modals working in Angular + Material took me a lot longer than I expected.  But I must confess that the documentation for them here -> https://material.angular.io/components/dialog/overview was spot on.  You just have to actually read all of it.

I’m going to provide a shorter crash course here showing 100% of what you need code-wise.  I suggest you refer to that main link to understand everything fully though.

Requirements Summary

To get a modal working, assuming you already have Angular + Material working, you need to do the following.  This assumes you are just using the root @NgModule in app.module.js, but you can use other modules if you like.

  • import MatDialogModule at the top of your app.module.ts and it to your imports array in the same file.
  • import {MatDialog, MatDialogRef, MAT_DIALOG_DATA} and {Inject} in your current page’s .ts file.
  • Create a new HTML file for your modal at the same level as your current page.
  • Add a dialog component into your typescript file.
  • Write code to trigger your dialog to open.
  • Import your dialog component back in your app.module.js and register it as a declaration *and* as an entry component (you probably have to add entry components as they’re not there by default).
    • This is because dialogs are created on-the-fly and angular needs extra information to deal with ad-hoc components.

Detailed Code Example

app.module.ts

Again, if you have a multi @NgModule angular app, you can still refer to this but you may put the content in other modules.

//... (normal imports left out for brevity)
import { MatDialogModule} from '@angular/material';
import { DialogOverviewExampleDialog } from "./cs-job-monitor/cs-job-monitor.component"

@NgModule({
  declarations: [
    ...,
    DialogOverviewExampleDialog
  ],
  imports: [
    ...
    MatDialogModule
  ],
  providers: [],
  bootstrap: [AppComponent],
  entryComponents: [
    DialogOverviewExampleDialog
  ],
})
export class AppModule { }

cs-job-monitor.component.ts

This is just one of the pages in my angular project as generated by the angular CLI. It just happens to be called cs-job-monitor but that isn’t important to you.

//... (normal imports left out for brevity)
import { Inject } from '@angular/core';
import {MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material';

//Your normal page component.
@Component({
  selector: 'app-cs-job-monitor',
  templateUrl: './cs-job-monitor.component.html',
  styleUrls: ['./cs-job-monitor.component.styl']
})
export class CsJobMonitorComponent {

  constructor(private http: HttpClient, public dialog: MatDialog) {
    //Normal work.
  }

  //In my case, I am opening the modal on the "on select row" event
  //of an angular grid (ag-grid).  But this is not important, just look
  /at how it opens.
  onSelectionChanged(event: Object) {
    const dialogRef = this.dialog.open(DialogOverviewExampleDialog, {
      data: event["api"].getSelectedRows()
    });
  }
}

//Here's your dialog component.  Mine is still named after the example one from
//angular's documentation page (I'll fix that!).  But it works fine.
@Component({
  selector: 'dialog-overview-example-dialog',
  templateUrl: 'dialog-overview-example-dialog.html'
})
export class DialogOverviewExampleDialog {

  constructor(
    public dialogRef: MatDialogRef,
    @Inject(MAT_DIALOG_DATA) public data: DialogData) {}

  onNoClick(): void {
    this.dialogRef.close();
  }
}

dialog-overview-example-dialog.html

Here is the HTML that appears in your dialog when it pops up. For now, I just have it displaying the object you gave it as data as JSON. In this case, as it will display the selected rows from the ag-grid I was using to call onSelectionChanged(). But I’m not bothering to add that here.

<pre>
  {{data | json}}
</pre>

Apache HTTPD Proxy Add CORS Headers to Use Remote API

I just suffered quite a lot while trying to use an API someone provided me to hack up a dashboard.  This was because it was missing the necessary headers to enable CORS to work from a web-app.

What is CORS?

Suffice to say that CORS is  away to allow your web-app to request resources from a server other than the one it came from.

While we’re not doing Java here, the Spring documentation explains the same origin policy which stops JavaScript from being able to invoke calls for resources from a different origin than it came from.  James Chamber’s blog explains the CORS concepts in terms of a guest list and makes it very comprehensible right here.

Make My Web-App Work!

Chances are, if you’re here, you’re currently writing a web-app (maybe Angular or React?), and you are trying to get data from a URL, and it is failing with the word CORS in your dev-tools console.

This is because the “Access-Control-Allow-Origin” header is not being added by the server.  So, your client app is not allowed to use it.  In terms of the blog referred to earlier, you’re not on the guest list.

If you’re in a company, the correct fix for this is to have the API add the header with the proper origins, or a * if you’re not overly worried about security.  But, often you don’t have time for that, or you’re not in control of the API.  In that case, you can use a proxy!

Assuming you are working on Linux and you’ve already installed Apache (HTTPD), it is pretty easy to fix this with a proxy.  Here is a perfect example. Just add this at the bottom of /etc/httpd/conf/httpd.conf, ensure there is a “Listen 80” in your file, and then restart Apache with “sudo systemctl restart httpd”.

<br /><LocationMatch "/SomePath">
ProxyPass http://:8080/SomePath
Header add "Access-Control-Allow-Origin" "*"


This will make sure all requests to this server, whether they’re from it or another host, which target :8080/SomePath will be proxied to http://:8080/SomePath and will return to the client with the extra header “Access-Control-Allow-Origin” “*”. This will let your web-app correctly talk to URLS under http://:8080/SomePath even though it doesn’t have CORS headers itself.

Note that you can do / instead of SomePath to target the whole server, and of course, you can change the ports/etc.

I hope this helps you!

Oh, and one more thing… If you installed Apache and are having trouble running it in the first place, maybe you’re running SELINUX, in which case you might want to try “/usr/sbin/setsebool -P httpd_can_network_connect 1”. That nailed me before I went on to work on the rest of this.

Also,  if you want to use a different port and it’s not working on Centos/etc, you may need to open the port in SELINUX “semanage port -a -t http_port_t -p tcp 8222”.