Pop Up Problems

We all remember pop up windows. Or, most of us probably do- it’s been a while since they were in fashion.  Very few legitimate sites use them anymore, but sometimes there are legitimate reasons to have something ‘pop up’ from your site.  Now we have the modal window.  Or maybe it’s just ‘modal’- maybe the old term ‘pop up window’ carries such bad memories that we’re not even allowed to use ‘window’ anymore.

Good use case for a modal (in my opinion)- delete command confirmation.  You’re creating your app- it’s CRUD(ding) your database just like any good interactive app will do.  You can Create a record, Read it to the screen, Update it as needed, and Delete it when necessary.  But you should also be pretty damn sure the user really wants to delete that record when they click/tap the option to do so.

A good place for a modal to step in.  User clicks to delete, modal fades in asking for confirmation, user confirms, process completes.  Nice and easy, but when I went to add this functionality to a new Angular 2 app, it turned out to be a bit tricky.

In my last entry, I covered refactoring some ugly Angular 2 code.  I’d been manipulating the DOM directly from within Components.  At the time, all that mattered was that it worked, but it turned out to be (as advertised) a bad idea.  Separation of concerns is a good thing!  It became even more complicated when updating the modal component- but in the end, it had multiple benefits.

I followed the same basic process when updating the modal as I did with the more simple shared services.  The component is mostly a home for the selector and template- and it imports the shared modal service.  That modal service is then used by any other component to open and configure a new modal window.  The first update to the service was to remove any document.querySelector call- any manipulation could be done through double curly interpolation.

So, instead of:

const modalBackdrop = document.querySelector('#modal-backdrop');
modalBackdrop.classList.add('fade-in');

To show the modal backdrop screen, we can just set a boolean (on the class) and assign the [hidden] attribute on the template to that boolean:

//initialized to true to hide on first load
modalBackdropHidden: boolean = true;

//called within the modalOpen method
this.modalBackdropHidden = false;

And on the template:

[hidden]="modalService.modalBackdropHidden"

But there’s also a config object that can be passed to modalOpen- most options are for style and the inner text- these are inserted using either the method above, or using {{textVarHere}} interpolation in the template.  The tricky part was the ‘confirmation’ option.  Some modals just open, give you a notice, and either close on their own after a setTimeout, or close when you click to close.  But to confirm an action, then fire off a callback function (to actually delete the record) took a bit more work.

With the initial setup, I was grabbing the ‘confirm’ button element and setting an event listener on it to fire my ‘delete’ method.  Worked fine the first time, but I couldn’t find a way to remove the event listener after firing.  An Angular app ideally never reloads, so that event listener never goes away.  The 2nd (and 3rd, etc) click to confirm a deletion would still work to delete a different entry, but the handler to delete the first one was still on the button.  So it tried to send the id of a non-existent entry (already deleted) along with the new one to delete.  It still worked, but would give an error each time.  I knew using removeEventListener would work, but couldn’t figure out how to get the ‘name’ of the function to remove it (I logged just about everything out, and the name never appeared- even though it had on on the class).

So I put that component on hold.  It worked, more or less, and there was a lot of other work to do.  But now I was fixing everything else, so it was time to dig in on this one.  As it turns out, sometimes the recommended way of doing things is recommended for a reason.  With the new setup (not putting an event listener directly on the DOM element button), getting the confirmation to work properly was pretty simple.

In the component calling the showModal method, we pass along a cached version of the ‘this’ context variable (I know- caching ‘this’ in another variable is so uncool, but it seemed like a good use case to pass the context between the component and a shared service), as well as the callback to fire (located on the component):

let _this = this;
this._modalService.showModal({
    element: _this, 
    callback: this.deleteContact, 
    type: 'confirm'
});

Two thisses!  _this refers to the class you’re calling the modal from- a way of passing the necessary context along.  Then, the original ‘this’, allows you to pass the intended callback (a method on the same component called deleteContact) to the service.  On the service, inside showModal, there’s a conditional:

if(type === 'confirm') {
    this.confirmCallback = this.callback.bind(element);
}

In the template for the modal, the (click) event on the ‘confirm’ button is bound to the confirmCallback method.  If I understand correctly (and there’s a good chance I don’t), we are just passing the proper context from the calling class to the modal service- the modal service then uses that info, along with its own ‘this’ context, to execute the callback properly.

Either way, it works great- two bugs with ‘this’ stone!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s