Creating A Simple Modal System In AngularJS
For most of my web development career, "modal windows" have been something of a blackbox. Meaning, I've never really implemented one myself - only consumed various UI (User Interface) libraries, such as jQuery UI or Bootstrap, that provided a modal window solution. The problem with this is that it makes the modal window feel "magical". And, magical things are hard to reason about. As such, I wanted to try and pull back the curtain - I wanted to try and create a simple modal window management solution, in AngularJS, that has zero magic.
Run this demo in my JavaScript Demos project on GitHub.
The first trick to removing the magic is accepting the fact that there is absolutely nothing technically special about a modal window. From an AngularJS perspective, a modal window is nothing more than a Controller than manages a View-Model, interacts with Services, and is rendered by a View. The same it true for every other UI-oriented component in your AngularJS application.
From a workflow standpoint, however, modal windows are slightly unique in that they require some cross-controller interaction. Meaning, some Controller - A - needs to prompt the user for information using some modal window Controller - B. Modal window Controller B then needs to return information to Controller A. Since controllers can't communicate directly, except through scope-inheritance (which creates tight coupling and is falling out of favor), it means that our loosely-coupled controllers need to communicate through some shared model or service. This works out perfectly because this shared service provides an opportunity to encapsulate the communication behind an asynchronous promise-based workflow.
Each modal window controller takes care of managing its own state; but, we still need something that manages the meta-rendering of modal windows. Meaning, we need something that determines which modal window is currently being rendered. And, since we're already thinking about a shared service to model the state and communication, all we need is a simple Directive to make sure the modal window "layout" reflects the state of the service.
What we end up with is a relationship that looks like this:
As you can see the, "modal service" sits in the middle of the love-triangle and facilitates communication and state. But, more importantly, it keeps all the parts loosely coupled. The modals windows can be built and tested separately; the modal-consuming controllers can be built and tested separately; and, the modal service can be built and tested separately.
When a core Controller needs a modal window, it asks the service to .open() it. This action returns a promise that the Controller can bind to. As with any promise, this promise can be resolved or it can be rejected. How how that happens is completely up to the modal window instance. The general workflow is as follow:
- Controller asks modal service to .open() a modal window.
- Modal service prepares modal data and emits an "open" event.
- Directive listens for "open" event and renders the appropriate modal.
- Modal window manages its own view-modal and user-interactions.
- Modal window calls .resolve() or .reject() on the modal service.
- Modal service resolves or rejects the relevant promise.
- Original controller reacts to the resolution of the promise.
In the above diagram (and workflow description), you may also notice that I am using the $rootScope to emit events from the modal service to the modal directive. Since the service layer can't directly affect the state of the DOM (Document Object Model), we need the directive in order to execute the DOM manipulation. Technically, we could have achieved this with a $watch() binding in the directive - watching for some service value. But, this would have required our directive to be chronically watching for acute application events, which feels unnecessarily complex and expensive.
To explore this architecture, I've put together a small demo that has three different types of modal windows: Alert, Confirm, and Prompt. I've kept the HTML and CSS styling super simple in order to isolate the technically-interesting aspects of the code. As you look at the following, pay special attention to the fact that all the Controller are very simple, very ordinary controllers - there's nothing unique about a Controller that happens to manage a modal window, or about a Controller that happens to consume a modal window.
<!doctype html>
<html ng-app="Demo">
<head>
<meta charset="utf-8" />
<title>
Creating A Simple Modal System In AngularJS
</title>
<link rel="stylesheet" type="text/css" href="./demo.css"></link>
</head>
<body ng-controller="AppController">
<h1>
Creating A Simple Modal System In AngularJS
</h1>
<ul>
<li>
<a ng-click="alertSomething()">Alert something</a>
</li>
<li>
<a ng-click="confirmSomething()">Confirm something</a>
</li>
<li>
<a ng-click="promptSomething()">Prompt something</a>
</li>
</ul>
<!--
BEGIN: Modals Layout.
--
Our simple modal system makes the assumption that only one modal window can be
open at a time. I find this to be a very reasonable assumption for 99% of use-
cases. And, I'd rather keep it simple and work around the rare use-case in some
other fashion (such as a multi-state modal).
To remove any "magic", I'm explicitly presenting the control properties rather
than trying to hide them behind a compile() step. Notice that rendering a modal
is JUST LIKE RENDERING ANY OTHER VIEW - we have a subview variable which we are
using to swap modals, or remove modals altogether.
The big take-away in the following code is that modal windows aren't a *special*
concept. They are views or components that work the same exact way that the rest
of your application works! Sure, there are a few more rules, like only one open
at a time; but, other than that, there's nothing here that's any different than
any other view you will build. It has a Controller, it has a View, and it works
with other services to execute its functionality.
-->
<div
bn-modals
ng-show="subview"
class="m-modals"
ng-switch="subview">
<!-- BEGIN: Alert Modal. -->
<div
ng-switch-when="alert"
ng-controller="AlertModalController"
class="modal">
<p>
{{ message }}
</p>
<p>
<a ng-click="close()">Ok, I got it!</a>
</p>
<!-- This is to demonstrate that we can jump from one modal to another. -->
<p>
<a ng-click="jumpToConfirm()" class="jump">Jump over to the confirm modal</a>
</p>
</div>
<!-- END: Alert Modal. -->
<!-- BEGIN: Confirm Modal. -->
<div
ng-switch-when="confirm"
ng-controller="ConfirmModalController"
class="modal">
<p>
{{ message }}
</p>
<p>
<a ng-click="confirm()">{{ confirmButton }}</a>
—
<a ng-click="deny()">{{ denyButton }}</a>
</p>
</div>
<!-- END: Confirm Modal. -->
<!-- BEGIN: Prompt Modal. -->
<form
ng-switch-when="prompt"
ng-controller="PromptModalController"
ng-submit="submit()"
class="modal">
<p>
{{ message }}
</p>
<p ng-if="errorMessage">
<strong>Sorry:</strong> {{ errorMessage }}
</p>
<p>
<input type="text" ng-model="form.input" />
</p>
<p>
<a ng-click="submit()">Submit</a>
—
<a ng-click="cancel()">Cancel</a>
</p>
</form>
<!-- END: Prompt Modal. -->
</div>
<!-- END: Modals Layout. -->
<!-- Load scripts. -->
<script type="text/javascript" src="../../vendor/angularjs/angular-1.3.15.min.js"></script>
<script type="text/javascript" src="../../vendor/angularjs/angular-animate-1.3.15.min.js"></script>
<script type="text/javascript">
// Create an application module for our demo.
var app = angular.module( "Demo", [ "ngAnimate" ] );
// -------------------------------------------------- //
// -------------------------------------------------- //
// I control the root of the application.
app.controller(
"AppController",
function( $scope, modals ) {
// I open an Alert-type modal.
$scope.alertSomething = function() {
// The .open() method returns a promise that will be either
// resolved or rejected when the modal window is closed.
var promise = modals.open(
"alert",
{
message: "I think you are kind of beautiful!"
}
);
promise.then(
function handleResolve( response ) {
console.log( "Alert resolved." );
},
function handleReject( error ) {
console.warn( "Alert rejected!" );
}
);
};
// I open a Confirm-type modal.
$scope.confirmSomething = function() {
// The .open() method returns a promise that will be either
// resolved or rejected when the modal window is closed.
var promise = modals.open(
"confirm",
{
message: "Are you sure you want to taste that?!"
}
);
promise.then(
function handleResolve( response ) {
console.log( "Confirm resolved." );
},
function handleReject( error ) {
console.warn( "Confirm rejected!" );
}
);
};
// I open a Prompt-type modal.
$scope.promptSomething = function() {
// The .open() method returns a promise that will be either
// resolved or rejected when the modal window is closed.
var promise = modals.open(
"prompt",
{
message: "Who rocks the party the rocks the body?",
placeholder: "MC Lyte."
}
);
promise.then(
function handleResolve( response ) {
console.log( "Prompt resolved with [ %s ].", response );
},
function handleReject( error ) {
console.warn( "Prompt rejected!" );
}
);
};
}
);
// -------------------------------------------------- //
// -------------------------------------------------- //
// I control the Alert modal window.
// --
// NOTE: This controller gets "modals" injected; but, it is in no way
// different than any other Controller in your entire AngularJS application.
// It takes services, manages the view-model, and knows NOTHING about the DOM.
app.controller(
"AlertModalController",
function( $scope, modals ) {
// Setup default values using modal params.
$scope.message = ( modals.params().message || "Whoa!" );
// ---
// PUBLIC METHODS.
// ---
// Wire the modal buttons into modal resolution actions.
$scope.close = modals.resolve;
// I jump from the current alert-modal to the confirm-modal.
$scope.jumpToConfirm = function() {
// We could have used the .open() method to jump from one modal
// to the next; however, that would have implicitly "rejected" the
// current modal. By using .proceedTo(), we open the next window, but
// defer the resolution of the current modal until the subsequent
// modal is resolved or rejected.
modals.proceedTo(
"confirm",
{
message: "I just came from Alert - doesn't that blow your mind?",
confirmButton: "Eh, maybe a little",
denyButton: "Oh please"
}
)
.then(
function handleResolve() {
console.log( "Piped confirm resolved." );
},
function handleReject() {
console.warn( "Piped confirm rejected." );
}
);
};
}
);
// -------------------------------------------------- //
// -------------------------------------------------- //
// I control the Confirm modal window.
// --
// NOTE: This controller gets "modals" injected; but, it is in no way
// different than any other Controller in your entire AngularJS application.
// It takes services, manages the view-model, and knows NOTHING about the DOM.
app.controller(
"ConfirmModalController",
function( $scope, modals ) {
var params = modals.params();
// Setup defaults using the modal params.
$scope.message = ( params.message || "Are you sure?" );
$scope.confirmButton = ( params.confirmButton || "Yes!" );
$scope.denyButton = ( params.denyButton || "Oh, hell no!" );
// ---
// PUBLIC METHODS.
// ---
// Wire the modal buttons into modal resolution actions.
$scope.confirm = modals.resolve;
$scope.deny = modals.reject;
}
);
// -------------------------------------------------- //
// -------------------------------------------------- //
// I control the Prompt modal window.
// --
// NOTE: This controller gets "modals" injected; but, it is in no way
// different than any other Controller in your entire AngularJS application.
// It takes services, manages the view-model, and knows NOTHING about the DOM.
app.controller(
"PromptModalController",
function( $scope, modals ) {
// Setup defaults using the modal params.
$scope.message = ( modals.params().message || "Give me." );
// Setup the form inputs (using modal params).
$scope.form = {
input: ( modals.params().placeholder || "" )
};
$scope.errorMessage = null;
// ---
// PUBLIC METHODS.
// ---
// Wire the modal buttons into modal resolution actions.
$scope.cancel = modals.reject;
// I process the form submission.
$scope.submit = function() {
// If no input was provided, show the user an error message.
if ( ! $scope.form.input ) {
return( $scope.errorMessage = "Please provide something!" );
}
modals.resolve( $scope.form.input );
};
}
);
// -------------------------------------------------- //
// -------------------------------------------------- //
// I manage the modals within the application.
app.service(
"modals",
function( $rootScope, $q ) {
// I represent the currently active modal window instance.
var modal = {
deferred: null,
params: null
};
// Return the public API.
return({
open: open,
params: params,
proceedTo: proceedTo,
reject: reject,
resolve: resolve
});
// ---
// PULBIC METHODS.s
// ---
// I open a modal of the given type, with the given params. If a modal
// window is already open, you can optionally pipe the response of the
// new modal window into the response of the current (cum previous) modal
// window. Otherwise, the current modal will be rejected before the new
// modal window is opened.
function open( type, params, pipeResponse ) {
var previousDeferred = modal.deferred;
// Setup the new modal instance properties.
modal.deferred = $q.defer();
modal.params = params;
// We're going to pipe the new window response into the previous
// window's deferred value.
if ( previousDeferred && pipeResponse ) {
modal.deferred.promise
.then( previousDeferred.resolve, previousDeferred.reject )
;
// We're not going to pipe, so immediately reject the current window.
} else if ( previousDeferred ) {
previousDeferred.reject();
}
// Since the service object doesn't (and shouldn't) have any direct
// reference to the DOM, we are going to use events to communicate
// with a directive that will help manage the DOM elements that
// render the modal windows.
// --
// NOTE: We could have accomplished this with a $watch() binding in
// the directive; but, that would have been a poor choice since it
// would require a chronic watching of acute application events.
$rootScope.$emit( "modals.open", type );
return( modal.deferred.promise );
}
// I return the params associated with the current params.
function params() {
return( modal.params || {} );
}
// I open a modal window with the given type and pipe the new window's
// response into the current window's response without rejecting it
// outright.
// --
// This is just a convenience method for .open() that enables the
// pipeResponse flag; it helps to make the workflow more intuitive.
function proceedTo( type, params ) {
return( open( type, params, true ) );
}
// I reject the current modal with the given reason.
function reject( reason ) {
if ( ! modal.deferred ) {
return;
}
modal.deferred.reject( reason );
modal.deferred = modal.params = null;
// Tell the modal directive to close the active modal window.
$rootScope.$emit( "modals.close" );
}
// I resolve the current modal with the given response.
function resolve( response ) {
if ( ! modal.deferred ) {
return;
}
modal.deferred.resolve( response );
modal.deferred = modal.params = null;
// Tell the modal directive to close the active modal window.
$rootScope.$emit( "modals.close" );
}
}
);
// I manage the views that are required to render the modal windows. I don't
// actually define the modals in anyway - I simply decide which DOM sub-tree
// should be linked. The means by which the modal window is defined is
// entirely up to the developer.
app.directive(
"bnModals",
function( $rootScope, modals ) {
// Return the directive configuration.
return( link );
// I bind the JavaScript events to the scope.
function link( scope, element, attributes ) {
// I define which modal window is being rendered. By convention,
// the subview will be the same as the type emitted by the modals
// service object.
scope.subview = null;
// If the user clicks directly on the backdrop (ie, the modals
// container), consider that an escape out of the modal, and reject
// it implicitly.
element.on(
"click",
function handleClickEvent( event ) {
if ( element[ 0 ] !== event.target ) {
return;
}
scope.$apply( modals.reject );
}
);
// Listen for "open" events emitted by the modals service object.
$rootScope.$on(
"modals.open",
function handleModalOpenEvent( event, modalType ) {
scope.subview = modalType;
}
);
// Listen for "close" events emitted by the modals service object.
$rootScope.$on(
"modals.close",
function handleModalCloseEvent( event ) {
scope.subview = null;
}
);
}
}
);
</script>
</body>
</html>
When a modal window is instantiated, it can read data out of the modal service "params" to help itself render. This works very much like the AngularJS $route service and its associated $routeParams object - a Controller can read from them to help render and provide behavior.
Obviously, there opportunity to add more features, like the ability for the modal window (or the calling Controller) to prevent the backdrop-click from implicitly rejecting the modal. But, as it stands now, this entire modal system is powered by 200 lines of (heavily commented) code. It's simple - not magical. And, to me, the simplicity makes it much easier to reason about.
Want to use code from this post? Check out the license.
Reader Comments
Hi,
Thanks for this great article. Any good reason why you don't compile at runtime the Modal directive and add it to the DOM?
For example Inside the modal service open() method do something like:
var html = $compile('<bn-modals data-view="alert" class="m-modals" />')($rootScope);
var body = document.body[0];
angular.element(body).append(html);
I am using this technique a lot to add and remove directives and it feels to me that this way I avoid a lot of data binding from ng-if / ng-switch.
What do you think?
@Sebastian,
It's an interesting approach. But, I think it may be come too complex when the HTML for the modals starts to get larger, unless I am misunderstanding what you are suggesting.
I think jQuery UI and Bootstrap do something a bit similar to what you are saying. I think they inject, at the very least, the "back drop" element for the modal window into the body element and then just re-use that for each modal that needs to be opened. I am also using a backdrop, but in my case, the modals container is the backdrop.
I don't think I have answered your question directly - I guess this is just the way that I am comfortable doing it currently.
Yes you're right it could get very complex on the directive side.
That's why I embed another directive for the content inside the modal.
The template of the modal is just: "<div class="modal"></div>"
As I don't know how many modals I might need and don't really like to have lots of hidden DIVs in the DOM I prefer to build and destroy them.
This is the code I use inside my "overlay" directive (which correspond to your modal).
var link = function(scope, elem, attrs, ctrl) {
// contentType is the child directive that holds the content
var html = "<"+scope.contentType;
if('args' in attrs){
html += " " + attrs.args;
}
var params = OverlayService.getParams();
if(params.directiveData){
html += " " + params.directiveData;
}
// overlay is the name of the main modal (controlesAs)
html += " data-on-ready='overlay.onContentReady()' data-resolve='overlay.resolve()' data-reject='overlay.reject()' />";
html= $compile(html)(scope);
elem.append(html);
elem.on(
"click",
function handleClickEvent( event ) {
if ( elem[ 0 ] !== event.target ) {
return;
}
scope.$apply( function(){ctrl.reject();} );
}
);
};
@Sebastian,
I see what you're saying - you compile a new directive whenever the user needs to see a particular modal. That's an interesting approach. One thing though, you made reference to "hidden divs." If you "switch" on a type, the Divs aren't really hidden in the DOM - they are completely removed. But, that said, they are compiled and the linking functions are cached in memory (this is AngularJS, not me). So, I wouldn't be too concerned about seeing Divs in the initial design of the page if only one of them is actually linked at any one time.
Interesting stuff!
You've saved my bacon so many times, I use you as pretty much my default "goto" on many things angular. You're articles\ videos are great. When you're next in the UK, I'll buy you a beer!
@Nick,
Kind sir, that is much appreciated and made my day :D Glad that you find this stuff interesting! Hopefully more good stuff to come.
Finally, I understood how modal works. I am going to mix your code logic with angular-ui-router. I am newbie with angularjs and I think your code and explanation is a fresh and positive approach to this paradigm. Thank you, I have learned more than some books that I have read.
Hi,
"First I want to say thanks for being there."
I have a situation which all its forms are wrapped in windows that compiles on demand. Consider that each window form has it's own controller.
Now I want to pass some parameters to the opening windows from a parent window form controller, and resolve their promises (modal behavior). I'm wondering is there a solution to handle this case by the same window architecture?
I'm just separated the windows to the parent directive, and the child one. (Using above code)
Hi, many thanks for this clear tutorial, it helped me a lot! I have a small problem with the ngSwitch scope:
Within the directive I want to add a dom manipulation within the method:
$rootScope.$on(
"modals.open", ....
Here I ofcourse get the directive element, but I cannot get the selectors within the active ngSwitch element. I think that this is not ready at this moment directly after setting "scope.subview = modalType;"
I thought I can get it by "element[0].queryString('.someContentClassWithinNgSwitch')" but I believe I have a scope or runtime problem....
In case of using ng-show instead of ngSwitch everything is working ...
can you help me out here?
much appreciate and greetings,
Robert
How would I just trigger the modal on init?
Hi,
i have just started working in angularjs and recently i came to a dead end situation. I am trying to develop a page where after clicking a add button a small window will popup and ask for name number and 2 check box. but no matter what i try the window doesn't pop up. if you can share some knowledge or some code .. even i tried the above mention code but still the pop up window wont appear. it only getting displayed on the same page as hidden parameter.
do rply in my mail. and not in this comment .
Thanks & regards
:)
First of all, this is probably the best tutorial and explanation of modals in angular. And I love the promise/pipe approach you have here. I'm running into a snap though when I attempt to run this from within a framework. Normally when this happens, I have a typo somewhere, but not seeing it this time...
I have everything mostly copied/pasted into my project as a proof of concept. I'm getting no errors before or after trying this and the files are included into the page load. When I click on the sample links, no popup appears. Regardless to the link I click, if I click on another modal link, I get the rejected console error.
Do you have any ideas as to why this might not be working?
Hi, sorry if this is a silly question, but I notice that in this example the HTML code for the modal is included in the main page. But I was wondering, in the case where I want to show the modals in different pages, how this system might work without having to include the HTML code for the modals in every page I want to show them, let's say having it in a separate .html file.
Thanks in advance!
Where can I down load your CSS file for this modal example please?
Good sample, thank you
How to change state and URL for every popup?.,
Thanks in Advance
Thanks ! it helped me a lot
Thank you, this is a great article. This is the best explanation on angular modals I've seen.
What I need to do is add a User Agreement to a DPS app for android. Adobe uses Angular for their app. If they accept, the Adobe DPS loads, if they reject it quits.
Currently, I have an window.confirm("This is the EULA.\nDo you accept?"); but I need to have the whole User Agreement. I set one up a bootstrap css modal, but I can't get it to load on open when it needs to and if it denies to quit the app.
I am a new to the angular structure and syntax. I need to have the controller.js open the ("modal") div in the index.html then return the when they accept.
What would the syntax be in the controller.js? Can I just add the ConfirmModalController function() to the existing Adobe controller?
Thanks in advance for your help.
G
Thanks a lot. Really nice explanation.
There's seems to be a bug in your modal. It is possible to tab out of the modal to the underlying view. To test it, set an input on the main view, open the modal and press the tab key repeatedly. Eventually you will end up with the cursor on the input filed from the main view. Can this be somehow prevented?
@Will,
Not sure if you fixed your problem. I'm pretty sure you tested it with an MVC project. Check the .modal class in your Site.css. Maybe rename it. Had the same problem...
Cheers,
Harald
Why are you still using $scope? This is what I really don't get from the Angular community. I've read that $scope is outdated and should be replaced with controllerAs syntax and using vm. syntax is best practice to avoid nesting/overriding $scope variables.
Where can I down load your CSS file for this modal example please?
@Jacobus
just open the demo with Chrome, open the developer tools, View sources, expand the tree at demos/managing-modals-angularjs
and then you find demo.css
Great article sir,
You have saved my lot of days and nights! I have been struggling to understand core functionality of modal and overlays. This superb article made me play with angular's core functionality of data sharing, event emitting & event listening as well. Thanks you very much.
Hi, how can I reset the modal params when he clicks on th backdrop?
currently the modal does not reset the same as when clicking on the deny buttons
Nvm, I've added this functionality by myself
How to use 'modal.closing' event?
Ben I had previously read useful posts on your blog, but this one in particular was extremely informative and helpful. Thanks much for spending the time to share your expertise. Much appreciated.
I used to make living as a programmer in the 80's, but unfortunately got derailed into sales & marketing and now coming back to my roots and learning web development. There's a lot of useful stuff around the interweb, but they have a pretty common problem for an old-timer like me: People like to put a lot of unrelated "magic" in their code examples and sometimes it is really hard to find the beef. I really appreciate your style of leaving that clutter out and focus on the essentials. Thanks!
@Sebastian,
Cool answer buddy, I came to this page, looking to see how to use $compile for such approach :)
Thanks
And thank you Ben for the article :)
Thanks for the retorting these fabulous discussion, learn a lot Thanks
href="http://www.letsnurture.com/blog/making-web-applications-angularjs.html">AngularJS web Solutions</a>
@Will,
Is your problem resolved?
I too added all the required coding, however i don't get the modal opened but getting a rejected error in console
Hi
Does anyone else have the problem of the modal.close events not being recognised? basically the modals open up fine but then there is no way of closing them (neither by clicking elsewhere on the screen nor using the close <a> tags.
Thanks Ben,
very well explained and easy to learn.
I have about 3 days trying to implement the Bootstrap modal, but, the modal gets loaded with the whole page view, the whole <html>...</html>
I've tried multiple versions with some libraries such as: ui.bootstrap, angular-confirm, angular-modal... And always I got the same, the modal loaded with a duplicate of my site.
So, I have applied this version that works pretty fine!
Thanks!
How could I do for the directive to close the modal when 'escape' key is pressed?
I've tried with:
element.on(
"keydown keypress keyup",
function handleKeyPressEvent( event ) {
if ( event.keyCode !== 27 ) {
return;
}
scope.$apply( modals.reject );
}
);
Currently, the modal closes when I click on somewhere else on the screen other than the modal. How can I prevent that from happening?
thanks for this...
i make integration in my project with angular v1.4.1 and doesn't work, with angular v1.3.15 is good, so i will try to fix this.
blessings
i found that solution for the issue, i haven't had the v1.4.1 of animate!
@Emisael,
try this in any modalController...
document.querySelector('body').onkeydown = function (e) {
if (e.keyCode === 27) {
document.querySelector('body').onkeydown = undefined;
$scope.$apply(modals.reject);
}
};
i try in the div's but, never take the focus, so i go to the body element xd
@Akhilesh,
remove or comments this part of code in bnModals directive...
//element.on(
// "click",
// function handleClickEvent( event ) {
// if ( element[ 0 ] !== event.target ) {
// return;
// }
// scope.$apply( modals.reject );
// }
//);
How would you allow HTML in the content of your model. When the message property has HTML tags, it simply renders the tags. What's the best way to get around this??
Thanks @Clandher,
you gave me the idea, I added it at the end of open() method in Modals Service :
$("body").keydown(function(e){
if(e.keyCode === 27){
reject();
}
});
Hi, thanks for the tutorial, I have the same problem that Naven, i get 'Alert rejected!' and 'Confirm rejected!' when pressing button.
I copy/pasted code from this page, so don't know what can be. Any help would be appreciated.