To select a country (or any other geographical entity), we can offer our end users a dropdown list, a list of values in a popup window, an auto suggest input text component or a more visual, map based approach. In this article, I describe the latter: Open a modal popup that presents a map of the world and allows the user to inspect and select a country. Note: this article is a logical extrapolation from previous articles on OpenLayers and Oracle JET, specifically this article.
This article demonstrates how we go from a simple input text component and extend it: with a background image (a little globe) that the user can click.
When the user clicks the icon, a popup opens and shows the world map. If the input component contains a valid country name, this country is selected in the map. The user can click on any country and in doing so, selects the country.
The name of the country is written back to the input text component (Mali in the next figure).
The main challenges discussed in this article:
- Embed the OpenLayers map in an Oracle JET popup component
- Embed a clickable icon in an input text component to allow the end user to open the popup with a mouse click
- Link up the map with the input text component (as demonstrated in https://technology.amis.nl/2018/01/02/using-an-openlayers-map-to-select-countries-in-an-oracle-jet-application/)
Starting with the situation from the previous article (the third bullet overhead), this article describes how to create an input text component with clickable icon that opens a popup window that shows the map.
The final code is found here on GitHub: https://github.com/lucasjellema/jet-and-openlayers.
Embed the OpenLayers map in an Oracle JET Popup Component
This step is a simple one. I used the entry in the JET Cookbook on Popup Component: http://www.oracle.com/webfolder/technetwork/jet/jetCookbook.html?component=popup&demo=modal. I copied the styles used in this example to the file src/css/app.css:
.demo-popup { width: 80vw; height: 80vh; display: none; } .demo-popup-body { width: 75vw; height: 75vh; display: flex; flex-direction: column; align-items: stretch; } .demo-popup-header { align-self: flex-start; margin-bottom: 10px; } .demo-popup-content { align-self: stretch; overflow: auto; flex-basis: 60vh; } .demo-popup-footer { align-self: flex-end; margin-top: 10px; }
Next I added the popup component in the mapArea.html file, with the DIV that acts as the map container in
<div id="popupWrapper"> <oj-popup class="demo-popup" id="countrySelectionPopup" tail="none" position.my.horizontal="center" position.my.vertical="bottom" position.at.horizontal="center" position.at.vertical="bottom" position.of="window" position.offset.y="-10" modality="modal" data-bind="event:{'ojAnimateStart': startAnimationListener}"> <div class="demo-popup-body"> <div class="demo-popup-header"> <h5>Select a country by clicking on it</h5> </div> <div class="demo-popup-content"> <div id="countryInfo"></div> <div id="mapContainer" class="map"></div> </div> <div class="demo-popup-footer"> <oj-button id="btnClose" data-bind="click: function() { var popup = document.querySelector('#countrySelectionPopup'); popup.close(); }"> Close </oj-button> </div> </div> </oj-popup> </div>
I added the function startAnimationListener to the ViewModel in mapArea.js:
self.startAnimationListener = function (data, event) { var ui = event.detail; if (!$(event.target).is("#countrySelectionPopup")) return; if ("open" === ui.action) { event.preventDefault(); var options = { "direction": "top" }; oj.AnimationUtils.slideIn(ui.element, options).then(ui.endCallback); // if the map has not yet been initialized, then do the initialization now (this is the case the first time the popup opens) if (!self.map) initMap(); } else if ("close" === ui.action) { event.preventDefault(); ui.endCallback(); } }
The initialization of the map is taken care of in open stage for the popup this startAnimationListener – on the first occasion this popup is opened.
Embed a clickable icon in an input text component to allow the end user to open the popup with a mouse click
It is important that the user has a convenient way of opening the country selection popup. Just like the date and time input components provide a little clickable icon, inline in the input component, I want the country selector to consist of a regular JET input text component with an inline icon that the user can click on to bring up the popup.
The HTML code to make this happen is added to mapArea.html:
<div id="countryInput"> <oj-label for="country-input">Country</oj-label> <oj-input-text id="country-input" value="{{selectedCountry}}" on-value-changed="[[countryChangedListener]]" ></oj-input-text> <img id="countrySelectorOpen" src="css/images/icon-world.png" class='iconbtnintext' title ="Click to open World Map in Popup" data-bind="click: openPopup" /> </div>
Note how the click event on the image is bound to the KnockOut ViewModel’s openPopup function using the data-bind notation,
The CSS style iconbtnintext takes care of the positioning of the icon. This style is added to app.css:
.iconbtnintext { position:absolute; cursor:pointer; width:22px; height:23px; margin-left:-24px; padding-top:4px; }
Finally the icon to be used – icon-world.png – is copied to src/css/images
References
JET Cookbook on Popup Component: http://www.oracle.com/webfolder/technetwork/jet/jetCookbook.html?component=popup&demo=modal