angular-modal-service keeps AngularJS dialogs out of your controllers
Modal service for AngularJS - supports creating popups and modals via a service.
At a glance
- What is it?
- angular-modal-service is an MIT licensed AngularJS service that creates popups and modals through a single showModal call, handling the controller, the scope, input injection, two close promises and, since version 0.15, AngularJS 1.5 components. It is small and MIT licensed, but it targets a framework that the ecosystem has moved past.
- Who is it for?
- angular-modal-service fits a team maintaining an existing AngularJS application that opens Bootstrap or custom modals from many controllers and wants the wiring in one place, since it removes the repeated template and controller plumbing and gives you a defined close contract.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 43 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One showModal call replaces the dialog plumbing
The library's stated job is small and specific: creating popups and modals through a service, with full support for Angular 1.5 and later components. What it removes is repetition. In a typical AngularJS application, every dialog needs a template, a controller, a scope, a way to pass data in and a way to get a result out, and that plumbing tends to be copied from controller to controller with small variations.
The service centralises it behind a single call. The call takes an options object and returns a promise that resolves when the modal DOM element has been created and its controller has been instantiated. The resolved value is a modal object holding the created element, the controller, the scope, and two more promises, close and closed.
That distinction between close and closed is the most useful thing in the API and the easiest to get wrong if you implement this yourself. Both resolve to the result of the modal close function, but close resolves as soon as the close function is called, while closed resolves only once the modal has finished animating and been completely removed from the DOM. A caller that needs to know when the element is really gone listens to closed; a caller that wants to start work as soon as the user has decided listens to close.
The library is MIT licensed and authored by Dave Kerr, and the repository ships a bower.json as well as npm metadata, which is itself a clue about the era it comes from.
The modal controller always receives a close function
The controller contract is the second thing the service standardises. A modal controller can be any controller you like, with one condition: it is always provided with one extra parameter, the close function.
The close function is injected automatically and takes the result object, which is the same object passed to the close and closed promises the caller listens to. Its optional second parameter is the number of milliseconds to wait before destroying the DOM element, which exists so a closing animation has time to finish before the element is removed.
The documentation's controller example shows the intended use:
app.controller('SampleModalController', function($scope, close) {
$scope.dismissModal = function(result) {
close(result, 200); // close, but give 200ms for bootstrap to animate
};
});Passing 200 there is what tells Bootstrap's animation to finish before the element is torn down. The same delay can be set as a default through the global options configuration, which the documentation lists as its own section along with the modal object, animation and error handling topics.
For Bootstrap based modals there is a constraint that catches people out, and the documentation flags it in bold: the modal template must contain only one root level element. The FAQ entry linked from that note covers the details of why. This is a Bootstrap requirement that the service cannot paper over, and a multi root template produces a dialog that misbehaves in ways that look like a bug in the service.
Inputs are injected by name into the controller constructor
Passing data into a modal is done with the inputs field, and the values are injected into the controller constructor as named parameters. The example makes the contract explicit:
ModalService.showModal({
templateUrl: "exampletemplate.html",
controller: "ExampleController",
inputs: {
name: "Fry",
year: 3001
}
})The corresponding controller receives those names alongside scope and close:
app.controller('ExampleController', function($scope, name, year, close) {
});This is the part with the sharpest edge. Because the injection is by parameter name rather than by an object, a mismatch between the key in inputs and the parameter in the controller signature fails at runtime rather than at compile time. AngularJS dependency injection will look for a service called name and fail to find one, and the error message will not point at the inputs object.
You can also supply the controller inline as a function rather than by name, with or without the controllerAs attribute. If you provide a controller with the as syntax and also set controllerAs, then controllerAs has the higher priority, which the documentation states explicitly. The inline form is shown like this:
ModalService.showModal({
template: "<div>Fry lives in {{futurama.city}}</div>",
controller: function() {
this.city = "New New York";
},
controllerAs : "futurama"
})Components and bindings are the 1.5 path
Since version 0.15 the service can render a component instead of a template and controller pair. You pass a component name and an optional bindings object:
ModalService.showModal({
component: 'myComponent',
bindings: {
name: 'Foo',
myRecord: { id: '123' }
}
})When a component is provided, all properties in bindings are bound to the rendered component. That is a cleaner model than the inputs mechanism because a component already has an inputs contract, and the two mechanisms do not need to be combined.
The options list documented for showModal covers the ground: controller, controllerAs, templateUrl, template as raw HTML when templateUrl is not specified, inputs, component, bindings, and appendElement, which takes a custom angular element or selector such as #element-id to append the modal to instead of the default body element. The options list also includes scope.
The appendElement option is the one to know if you are working inside an existing layout with its own stacking context. Appending to body is the default, which is right for most overlays and wrong for a modal that needs to sit inside a positioned container.
The repository ships samples in a samples directory, with subdirectories for complex and custom modals plus a yesno example, and the documentation points to a quick fiddle and a hosted set of samples at dwmkerr.github.io/angular-modal-service.
Installing it and wiring the first modal
Installation is a package manager call, and the project supports both Bower and npm:
bower install angular-modal-service
# or...
npm install angular-modal-serviceThe script tag in the README references the Bower path to the minified build, which is the pre-Angular-CLI era convention:
<script src="bower_components/angular-modal-service/dst/angular-modal-service.min.js"></script>The npm route points at the same package name, and the package.json main field is ./index.js, so a bundler based setup uses that instead of a script tag. The build produces a webpack bundle in the dst directory, and the build script in package.json runs webpack in production mode and then minifies the result with uglifyjs into angular-modal-service.min.js with a source map. The repository contains a dst directory, so a prebuilt bundle is committed as well.
Registering the module with your application is one dependency entry:
var app = angular.module('sampleapp', ['angularModalService']);After that, inject ModalService into any controller, service or directive that needs it. The sample controller in the documentation shows the full sequence: call showModal with a templateUrl and a controller, then in the promise handler show the element, and wire the close promise to update the scope.
app.controller('SampleController', ["$scope", "ModalService", function($scope, ModalService) {
$scope.showAModal = function() {
// Just provide a template url, a controller and call 'showModal'.
ModalService.showModal({
templateUrl: "yesno/yesno.html",
controller: "YesNoController"
}).then(function(modal) {
// The modal object has the element built, if this is a bootstrap modal
// you can call 'modal' to show it, if it's a custom modal just show or hide
// it as you need to.
modal.element.modal();
modal.close.then(function(result) {
$scope.message = result ? "You said Yes" : "You said No";
});
});
};
}]);That call gives you back a modal object with the element already built, and the documentation is explicit that if it is a Bootstrap modal you call modal.element.modal() to show it, and if it is a custom modal you show or hide it yourself.
A library with no upstream feature work
The maintenance picture is the deciding factor here, and it is not ambiguous.
The most recent tagged release is v0.15.0 from 2018-11-10, before that v0.12.0 from 2017-05-20 and v0.11.0 from 2017-03-05. The last push was on 2026-09-10, and the package.json records version 0.15.6, which is a patch version above the newest tag. In other words, the source tree has moved past the last published tag, but there is no tag to point a dependency at beyond v0.15.0.
That combination is workable. The library is small enough that the patch level difference is unlikely to matter, and the code has been stable enough that an eight year gap between tags is more evidence of stability than of neglect. But there is a difference between a stable library and a maintained one, and the practical consequence is that there is no new feature coming: if you need AngularJS 1.5 component support, it is already there in 0.15, and if you need anything else, you are writing it yourself.
The build and test tooling in package.json is likewise a snapshot of its period: webpack 4, Karma 5 with a ChromeHeadless browser, mocha and chai for assertions, sinon for stubs, and standard-version for releases, with a build script, a start script that serves the samples through webpack-dev-server, and test and test-debug scripts that run Karma in single run and interactive modes.
For an application that must keep working, the sensible posture is to vendor or pin the dependency and own it from there rather than expecting a stream of fixes. On licensing, the package is MIT, which puts no obstacle in front of that.
Where it stops being the right answer
Three situations rule this library out.
The first is any new project. AngularJS is a framework in its end of life period, and building a new application on it means choosing a modal library for a platform you have already decided to leave. That cost is not recoverable later without rewriting the dialog layer, which touches every screen that opens a dialog.
The second is a team that wants dialogs to look like the rest of the application. The service renders a modal with a specific structure and hands you the element and the scope, but it does not document a theming or template override layer beyond template and templateUrl, which do exist. In practice the freedom is there in the template but not in the surrounding chrome, so a heavily branded dialog usually ends up fighting the service.
The third is a dialog with a complex result. The close function carries a single result object and the two promises resolve to it. If your dialog needs to return a form with validation state, a multi step wizard, or a distinguishable cancel versus dismiss outcome, you are encoding that into one result value and reconstructing the semantics on the caller side. The documentation gives you the contract and nothing more, and there is no documented facility for a modal to signal that it was dismissed rather than confirmed beyond whatever convention you impose on the result.
What the library does well is narrower and still valuable: it removes the repeated template and controller plumbing, standardises how a modal hands back a result, and gives you two clearly different close semantics so you do not have to reinvent them. If you are already inside AngularJS with a working codebase, that is a real saving. It is just not a reason to stay on AngularJS.
Editorial conclusion
angular-modal-service fits a team maintaining an existing AngularJS application that opens Bootstrap or custom modals from many controllers and wants the wiring in one place, since it removes the repeated template and controller plumbing and gives you a defined close contract. It is the wrong choice for a new project, because AngularJS itself is at end of life and this library's last tagged release was v0.15.0 on 2018-11-10, so you would be building on a dependency with no upstream feature work. Before adopting it in an existing codebase, check three things: that your Bootstrap modal templates contain a single root element, that your application can live with the promise semantics the service provides, where close resolves as soon as the close function is called and closed only resolves after the animation ends and the element leaves the DOM, and that the version you install matches the 0.15.6 in package.json rather than the older 0.15.0 tag.
Frequently asked questions
How do I install angular-modal-service?
The documentation gives two commands, bower install angular-modal-service and npm install angular-modal-service. The package is then registered as an AngularJS dependency with angular.module('yourapp', ['angularModalService']), and the ModalService is injected into any controller, service or directive that opens a dialog.
What is the difference between the close and closed promises?
Both resolve to the result of the modal close function, but close resolves as soon as the close function is called, while closed resolves only once the modal has finished animating and has been completely removed from the DOM. The close function takes an optional second parameter with a delay in milliseconds before the DOM element is destroyed.
How do I pass data into a modal with angular-modal-service?
Use the inputs field of the options object. Each value provided is injected into the controller constructor by name, alongside scope and the close function. Because the injection is by parameter name, a mismatch between the inputs keys and the controller signature fails at runtime.
Does angular-modal-service support AngularJS 1.5 components?
Yes. Full support for AngularJS 1.5 and later components is a stated feature. You provide a component name and an optional bindings object to showModal, and all properties in bindings are bound to the rendered component.
Is angular-modal-service still maintained?
The last push was on 2026-09-10 and the package.json records version 0.15.6, but the most recent tagged release is v0.15.0 from 2018-11-10. The library is MIT licensed and its build and test tooling still uses webpack 4, Karma 5 and standard-version.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/dwmkerr-angular-modal-service)Was this useful?