In this tutorial, I will discuss how to make the popups and how they can be assembled to make a movable popup modal on the web page. And I can tell you, it is not hard at all.
Introduction
I want to start 2022's tutorial series with a very useful yet simple design. Imagining such a scenario, you need to create a popup modal on the screen. It is very simple to do. Then the customers come back and ask for the popup to be movable, allowing users to drag the popup modal anywhere on the screen. With AngularJS and ui-bootstrap (a.k.a. Angular-UI), it is easy to display the popup modal, but making it movable requires some extra components and some assembly work. In this tutorial, I will discuss these components and how they can be assembled to make a movable popup modal on the web page. And I can tell you, it is not hard at all.
Architecture Overview
Normally, this section will describe the overall architecture. Instead of this, I will explain the JavaScript libraries I used to make this design to work. The obvious ones are JQuery, Bootstrap (yes it has js file), and AngularJS. I also needed Angular-UI (a.k.a ui-bootstrap) and JQuery UI. What is least obvious here is the use of JQuery UI. This is a less known extension library from JQuery and it provided a lot of powerful functionalities that can be easily added to a web application. This is the key component of this tutorial. I needed the drag-able functionality from this library. It made my implementation much simpler than it needed to be.
Next, I want to describe the idea of how to implement this. Angular-UI (or ui-bootstrap) is the best component working with AngularJS to boost the best functionalities from Bootstrap. It provided the easiest way to display the Bootstrap modal popup. This popup is not movable. "ui-bootstrap" did provide the configuration options to make this possible but it stopped short. I guess the developers of ui-bootstrap knew this is possible but decided to leave it as is so that if someone who likes to make the popup movable, they can make the leap without too much trouble.
Now, I can describe the architecture of this application. It is not much. This web application uses the Spring Boot to serve a single page to the web browser. The web page has an AngularJS application running on it. And there is just a button on the page. When user clicks the button, the movable modal popup will be displayed. User can drag the popup almost any where on the screen.
In order to make this work, I need to get rid of the backdrop when the popup displays, the other thing I have to do is making the popup movable. Removing the backdrop is very easy. Making it movable requires some coding. It is going to be fun, and let's dig in.
AngularJS Application Overview
I used ES6 script to write this application. The main entry file is called app.js, it is the file that defines the module dependency setup. In order to get the sample application to work, I need an application controller, a controller for the popup, and a directive that can make the popup movable. I also defined a service to handle the popup configuration and actual display of the popup. Here is how I set them up:
import { appRouting } from '/assets/app/js/app-routing.js';
import { AppController } from '/assets/app/js/AppController.js';
import { MoveablePopupController, MoveablePopupService }
from '/assets/app/js/MoveablePopupService.js';
import { uiMoveableDirective } from '/assets/app/js/UIMoveableDirective.js';
let app = angular.module('startup', ["ngRoute", "ui.bootstrap" ]);
app.config(appRouting);
app.factory("MoveablePopupService", ["$uibModal", MoveablePopupService]);
app.directive("uiMoveable", [ uiMoveableDirective ]);
app.controller("MoveablePopupController", [ "$scope", MoveablePopupController ]);
app.controller("AppController",
[ "$rootScope", "$scope", "MoveablePopupService", AppController ]);
The import
statements are pulling all the necessary classes and functions needed by the AngularJS application setup. Then I declare a module called "startup
". This is the module that will be used by the ngApp
. For this module, I would setup all the dependencies, like this:
app.config(appRouting);
app.factory("MoveablePopupService", ["$uibModal", MoveablePopupService]);
app.directive("uiMoveable", [ uiMoveableDirective ]);
app.controller("MoveablePopupController", [ "$scope", MoveablePopupController ]);
app.controller("AppController",
[ "$rootScope", "$scope", "MoveablePopupService", AppController ]);
The first line is the application URL routing setup. The second line defines the service to display the modal popup. The third line is the directive I have to create to make the popup movable. This is the vital piece of this tutorial. The fourth line is the controller for the modal popup. Finally, the last line is the application controller.
The Application Routing Configuration
The application URL routing is provided by a function, All it does is reroute the display to the application's index page and nothing else.
export function appRouting($routeProvider) {
$routeProvider.when("/", {
templateUrl : "/assets/app/pages/index.html",
controller: "AppController",
controllerAs: "vm"
});
}
There is nothing interesting about function exception that AngularJS will know the page to display, and what controller would be associated with the page.
The Controller for the Main Page
Next, I want to show what the controller for the main page. Again, it is very simple:
export class AppController {
constructor($rootScope, $scope, moveablePopupService) {
this._rootScope = $rootScope;
this._scope = $scope;
this._moveablePopupService = moveablePopupService
}
handleOpenPopup() {
this._moveablePopupService
.openMovablePopup()
.then(function () {
console.log("Popup closed by user.");
}, function () {
console.log("Popup cancelled.");
});
}
}
The controller is defined as a class, with constructor used to pass in external dependencies via injection. The only dependency I need here is the service that displays the popup. It is called moveablePopupService
. I will show it in the next section.
There is just one function within this class, called handleOpenPopup
. This method is the event handler for the main page. There is a button and the button click is handled by this method. It calls the service to open the popup. The then()
does nothing but display a line on the console that user either closed the popup via the close button on the popup or via the escape key on the keyboard.
The Service that Opens the Popup
The service function, and the popup controller are all packaged in one js file, it is called "MoveablePopupService.js". The service function itself is very simple, but in order to make the popup movable, I had to do something in the configuration of the popup when I open it. They are highlighted:
export function MoveablePopupService ($uibModal) {
let retVal = {};
retVal.openMovablePopup = function () {
let moveablePopup = $uibModal.open({
templateUrl: "/assets/app/pages/moveablePopup.html",
controller: "MoveablePopupController",
controllerAs: "popup",
backdrop: false,
size: "md",
resolve: { }
});
return moveablePopup.result;
};
return retVal;
}
There is just one method inside the function, it is called openMovablePopup
. It uses Angular-UI's $uibModal
to open the popup. $uibModal
does the work of adding the popup modal with some template and sets the configuration before displaying. The highlighted line is to remove the gray veil background. The idea is that, when you make the popup movable, you want to be able to see what is under the popup. The gray veil can be a usability issue if remained. Luckily, there is the configuration property backdrop
, we set it as boolean value false
and the gray veil (the backdrop) will not be displayed.
The Popup Controller
Next, I will show you the controller used by the popup dialog. It is also pretty simple:
export class MoveablePopupController {
constructor($scope) {
this._scope = $scope;
}
handleClickClose() {
this._scope.$close();
}
}
It is a simple class, with a constructor that does almost nothing. It also has a member method that handles the close of popup. Angular-UI's popup modal has its own isolated scope and in it two methods for closing the popup, one is called close()
. The other is called dismiss()
. I use the method close()
to explicitly close the popup. It can also pass back a result object indicating whatever the activity done in the popup is completed and successful.
In this sample application, there is no meaningful operation for the popup. All we do is show the popup so that it can be moved around. And it can be closed with either by a button or click the key "Escape".
Making Popup Modal Moveable
This is the most important section of this tutorial, where I explain how to make the popup movable. I have to create a directive for my popup window template. Then I will use the directive on my popup modal template. This directive will make the popup movable.
The way it works is that the directive finds the parent div of the popup modal. Then it will add drag-able attribute to this div. "draggable
" attribute is part of the JQueryUI
library. Once the attribute is added via JQueryUI
provided API, the popup is instantly movable.
Here is my definition of the directive:
export function uiMoveableDirective() {
return {
restrict: "EA",
link: function ($scope, element, attrs) {
var popupDiv = element.parent();
if (popupDiv) {
popupDiv.draggable();
}
}
}
}
I have highlighted the parts that are important. The first one is the line that finds the parent popup div. Then the next one adds the drag-able attribute to the popup. Here is how I applied this directive to my popup modal template:
<div ui-moveable>
<div class="modal-header">
<h4>Moveable Popup</h4>
</div>
<div class="modal-body">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Nam pulvinar enim eu nibh aliquam, ac laoreet sem lobortis.
Etiam vitae sodales ipsum. Donec tempus turpis eget nibh euismod,
a mollis ipsum rhoncus. Aliquam nulla augue, dapibus et nisl vitae,
imperdiet bibendum tellus. Mauris vitae ante sodales, eleifend arcu quis,
interdum magna. Sed vestibulum eu sapien id eleifend.
Maecenas quis imperdiet lacus. Mauris vehicula turpis nisi,
nec dapibus velit pharetra vitae. Orci varius natoque penatibus et
magnis dis parturient montes, nascetur ridiculus mus.</p>
<div class="row">
<div class="col-xs-12 text-center">
<button class="btn btn-danger" ng-click="popup.handleClickClose()">Close</button>
</div>
</div>
</div>
</div>
The directive was used in the outer most div. It wraps the inner content so that the popup frame would be modified to have the drag-able attribute. Then the whole popup would be movable.
There is one more action to take, to make the application more usable. When popup modal displays, the back ground would be non scroll-able. This creates a dilemma that even you can drag the popup around, you can't see much on the background. I need to overwrite this restriction so that user can scroll up and down the back ground content and when dragging the popup around, the user can still see what is beneath.
All I have to do is add a CSS style for the page CSS file (see index.css), like this:
.modal-open {
overflow: auto;
}
That is all needed to make a usable, drag-able popup modal. This concludes the tutorial.
How to Test
After you download the tutorial sample code, uncompress the file, then rename all the *.sj file to *.js file.
Next, at the base directory of the sample code (where you can find pom.xml), run the following command:
mvn clean install
This command will build the application. The next command will start the web application:
java -jar target/hanbo-angular-movable-popup-1.0.1.jar
Assuming the build is successful and application starts correctly, you can use the browser to get to the application by typing in the following URL:
http://localhost:8080/
Once the web page displays correctly, there will be a button on the top center position. Click it, and you will see the popup. Then user can click the popup anywhere and drag it around.
Here is a screenshot of the application after starting up:
Here is a screen shot of the application popup has been moved to the lower right corner of the page:
Summary
Compared to my past tutorials, this is a simple one. And it is a very effective trick that I have described in this tutorial. When I first heard the request on this, I was baffled that it can be done. Eventually, this is the solution I have come up with. There is one issue with this solution. The user can drag the popup modal, but cannot use mouse to highlight the text on the popup modal.
In this tutorial, I have explained how to create the directive to make the angular-ui
(ui-bootstrap
) popup modal drag-able and movable on the page. I also explained the override needed to make the background page scroll-able so that the whole setup can be more usable. There is one more issue with the drag-able functionality from JQueryUI
. If the user drags the popup too far to the bottom of the page, the popup would disappear. This might be an issue with the JQueryUI
library.
I have had a slow start for this year, and just finished this tutorial. It is simple but I think it will be very useful. I hope you find this tutorial useful as well. Good luck.
History
- 13th February, 2022 - Initial draft