tag:blogger.com,1999:blog-43123621312909628242024-03-05T19:57:42.991+01:00About OracleBlog about Oracle and Application ExpressDick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.comBlogger87125tag:blogger.com,1999:blog-4312362131290962824.post-30885646559060687002021-10-15T00:35:00.000+02:002021-10-15T00:35:11.568+02:00Creating a mobile app with APEX - Part 11: Displaying form items side by side After delivering two new presentations at Apex World 2021 and hROUG Conference
2021, I have enough material for a few extra blogposts on mobile development with
APEX. I will start with displaying form items side by side on an APEX smartphone app. <div><br /><div><b>Description of the challenge</b></div><div>Normally all form items will be placed underneath each other in a form on a smartphone:</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdwOKWAmOoxzdFnoje9GC8AkV6KBf2NaZzDvrx5_Toh2zFIla8yLJqsrGdKpUAz2Py5IDR9ElKS07hByxTm1CY-Z1Xah8CEXpUoRj577c2s2w8NHr9K2-MyyzyJRysppUdSJjv4gDfymti/s1180/Schermafbeelding+2021-10-15+om+00.31.16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1180" data-original-width="744" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdwOKWAmOoxzdFnoje9GC8AkV6KBf2NaZzDvrx5_Toh2zFIla8yLJqsrGdKpUAz2Py5IDR9ElKS07hByxTm1CY-Z1Xah8CEXpUoRj577c2s2w8NHr9K2-MyyzyJRysppUdSJjv4gDfymti/s320/Schermafbeelding+2021-10-15+om+00.31.16.png" width="202" /></a></div><div><br /></div>To save valuable screen estate, or to group two logically connected items, sometimes we would like to have two items on the same line. <div><div><br /></div><div>APEX has the possibility to place form items side by side by disabling the <b>Start New Row</b> attribute for the second item:</div><br /><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgROzTT8V0cl9tIT9_p99IPWpZBBSx9ctFhyphenhyphene_8ID4j8aK_8qMkgDL1tG9uBX4CTVaBZNEh8eLjYtU7xk7EH5eIKq22TzWYbD6r9ue6_EnrsptTpvzS_JB5097JQeLYMzqWzuWBRHD5Qo0T/s852/Schermafbeelding+2021-10-14+om+14.02.39.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="472" data-original-width="852" height="246" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgROzTT8V0cl9tIT9_p99IPWpZBBSx9ctFhyphenhyphene_8ID4j8aK_8qMkgDL1tG9uBX4CTVaBZNEh8eLjYtU7xk7EH5eIKq22TzWYbD6r9ue6_EnrsptTpvzS_JB5097JQeLYMzqWzuWBRHD5Qo0T/w445-h246/Schermafbeelding+2021-10-14+om+14.02.39.png" width="445" /></a></div><br /><div>On the desktop the result will look like this:</div><div><br /></div><div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg55I6e5orupfoy1MNLWFKgT6zLwmmGX7NUZYXMnjMEdJKEtbo5_ggQXwecZMX6cCYjsMDyF0iiXtYxHg5KoTA1ORDvxaySTvKXgm0ew6AXdHB85LJ_KKuto7uNiLjLKxU5SJgvzK4-L_Vy/s1426/Schermafbeelding+2021-10-14+om+14.05.40.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="294" data-original-width="1426" height="83" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg55I6e5orupfoy1MNLWFKgT6zLwmmGX7NUZYXMnjMEdJKEtbo5_ggQXwecZMX6cCYjsMDyF0iiXtYxHg5KoTA1ORDvxaySTvKXgm0ew6AXdHB85LJ_KKuto7uNiLjLKxU5SJgvzK4-L_Vy/w403-h83/Schermafbeelding+2021-10-14+om+14.05.40.png" width="403" /></a></div></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;">This does not work when the width of the page is less than 640 px, i.e. on most mobile devices, Commission will be placed on a new line:</div><div style="text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsiwNalyx37nGssOP8i_PZgZeqhOpqHgtOK5aqYcaiIosVzQBTYU2kF7ztogTkNJOBJ7JnkkW3n1VI1ZOuMENu4ulhf7MIWFLuaK8j6kA6Tgh2f1VuuZzegf-H2pQEgS5cJ8sRxgFhq5bZ/s904/Schermafbeelding+2021-10-14+om+14.08.10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="342" data-original-width="904" height="145" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsiwNalyx37nGssOP8i_PZgZeqhOpqHgtOK5aqYcaiIosVzQBTYU2kF7ztogTkNJOBJ7JnkkW3n1VI1ZOuMENu4ulhf7MIWFLuaK8j6kA6Tgh2f1VuuZzegf-H2pQEgS5cJ8sRxgFhq5bZ/w383-h145/Schermafbeelding+2021-10-14+om+14.08.10.png" width="383" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><b>Analysis</b></div><div class="separator" style="clear: both; text-align: left;">This behavior is caused by CSS. A media query aimed at screens with a width smaller than 640 pixels, determines that the width of the items is 100%. This way no more than one item fits on one row. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKXdviFB-xsBX3oUnkE_u8Gu30br3z49FHYCI9tfkWZb_X5qgyx74-2YtmmqTLOgiT1vrb5QmPW4nEH1eDUUaUx2C6tspF5lH5j7eG-RPweadT2VinSsXsdZnbD2zzsiO8Uetbw1EX6hyv/s592/Schermafbeelding+2021-10-14+om+14.09.53.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="266" data-original-width="592" height="144" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKXdviFB-xsBX3oUnkE_u8Gu30br3z49FHYCI9tfkWZb_X5qgyx74-2YtmmqTLOgiT1vrb5QmPW4nEH1eDUUaUx2C6tspF5lH5j7eG-RPweadT2VinSsXsdZnbD2zzsiO8Uetbw1EX6hyv/s320/Schermafbeelding+2021-10-14+om+14.09.53.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;"><b>Solution</b></div><div class="separator" style="clear: both; text-align: left;">So we need to overrule this CSS. For this we can define this snippet of CSS on the page:</div></div>
<pre>@media (max-width: 640px) {
.col.side_by_side {
width: 50%;
float: left;
clear: none;
}
}</pre><pre><span style="font-family: -webkit-standard; white-space: normal;">This CSS sets the <b>width</b> of the item to 50% instead of 100%, and applies <b>float:left</b>.</span></pre><pre><span style="font-family: -webkit-standard;"><span style="white-space: normal;">The two items that are supposed to be on one line, should both receive the class <b>side_by_side</b> and the second item should have the property <b>Start new row</b> unchecked:</span></span></pre><pre><span style="font-family: -webkit-standard;"><span style="white-space: normal;"><br /></span></span></pre><pre><span style="font-family: -webkit-standard;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGC4Ba5j7jTVXVLn7PHi62DqZ2rVSTr-dsREgTQnOZINfy1HfVfQjBGs-M7bXpcYd80ctOM272tIRGgtZlQs7sbcIVbnJzeL2NHD3cqOX3cXSr92gTNUMRefGsLYl3AxL2tRM38XXEHhW2/s620/Schermafbeelding+2021-10-15+om+00.12.47.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="620" data-original-width="558" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGC4Ba5j7jTVXVLn7PHi62DqZ2rVSTr-dsREgTQnOZINfy1HfVfQjBGs-M7bXpcYd80ctOM272tIRGgtZlQs7sbcIVbnJzeL2NHD3cqOX3cXSr92gTNUMRefGsLYl3AxL2tRM38XXEHhW2/w180-h200/Schermafbeelding+2021-10-15+om+00.12.47.png" width="180" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div></span></pre><pre><span style="font-family: -webkit-standard;"><span style="white-space: normal;">If you do not want both items to take up half of the row, you can change the width in the </span><b style="white-space: normal;">Column Attributes</b><span style="white-space: normal;">. Just make sure that the total width does not exceed 100%, otherwise the items will again be placed underneath each other. </span></span></pre><pre><span style="font-family: -webkit-standard;"><span style="white-space: normal;">The result of these definitions looks for the columns Salary and Commission looks like:</span></span></pre><pre><span style="font-family: -webkit-standard;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRmQGsXJA_wzt8VGoGtR7VdLng75wFYV_1rwvvaVNyYHVoUybmkhpXHIFFEKACbgF-dx7idGmLiMhNmjKB2EBgTTB71i7LC98tLhlwj-vJdyz3kXmctQ1iAKU0UeoOMPLX7afyPJffCjpD/s1062/Schermafbeelding+2021-10-15+om+00.24.39.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1062" data-original-width="746" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRmQGsXJA_wzt8VGoGtR7VdLng75wFYV_1rwvvaVNyYHVoUybmkhpXHIFFEKACbgF-dx7idGmLiMhNmjKB2EBgTTB71i7LC98tLhlwj-vJdyz3kXmctQ1iAKU0UeoOMPLX7afyPJffCjpD/s320/Schermafbeelding+2021-10-15+om+00.24.39.png" width="225" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Happy APEXing!</div><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><span style="white-space: normal;"><br /></span></span></pre><pre><span style="font-family: -webkit-standard;"><span style="white-space: normal;"><br /></span></span></pre><pre><span style="font-family: -webkit-standard;"><span style="white-space: normal;"><br /></span></span></pre><pre><br /></pre>
<br />
</div></div>Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com0tag:blogger.com,1999:blog-4312362131290962824.post-29303458493433550092020-08-01T14:32:00.000+02:002020-08-01T14:33:53.440+02:00New Plug-in: Reacting on changed content of APEX Autocomplete Item<h3>
Why use Text Field with autocomplete</h3>
Right now I am developing a series of mobile applications for personal use. And of course I learn a lot about developing with Oracle APEX for mobile.<br />
I like to use auto complete items in mobile apps because to my opinion select lists are less suited for mobile use most of the time. One of the reasons is that in long lists, in contrast to in desktop applications, you cannot filter on the first character. Because you do not have a keyboard! So you have to scroll a lot. On iPhone this takes place on the bottom of the screen where you see only about three items at a time. I once checked in for a flight using the mobile app from the airline. I had to select my country (The Netherlands) from a select list of some 200 countries <b>four</b> times. And each time it took me about 30 secs to scroll through the 170 countries until I reached <b>The Netherlands</b>.<br />
With an autocomplete item it would take 2 secs to type in a discriminating 3 or 4 characters to get to the result.<br />
So that is why I prefer to use Autocomplete items. But I found out there is a problem with reacting on when the content of such an item changes. A Dynamic Action of type <b>Change </b>does not fire when the value of an Autocomplete item is selected.<br />
<h3>
Reacting on change of Text Field with autocomplete</h3>
A quest for an answer on the internet revealed, that a special event type should be used. Under the heading Component Events we can find: <b>Update [Text Field with autocomplete]</b>.<br />
<br />
When using this event to define a Dynamic Action, the DA fires the moment a value from the suggestion list of the autocomplete item is chosen. So contrary to the Change event the focus is still on the Autocomplete item. Furthermore there is no reaction on values entered that are not in the suggestion list.<br />
<br />
So I started off to find a way to detect changes in the Text Field with autocomplete.<br />
I defined a On Focus DA in which the current value is stored into an attribute <b>old-value </b>of the input item. In a <b>Lose Focus</b> DA this value is retrieved and compared to the current value of the item. If there is a difference, action can be taken. Then I selected a value form the auto complete suggestion list. This took the focus away from the Autocomplete item, and the change event was triggered.<br />
So the <b>Lose Focus DA </b>does not work.<br />
With some googling I found out that only <b>input</b>, <b>select</b>, <b>textarea</b>, <b>anchor</b> and <b>button</b> elements can receive focus. So I could try to implement the code in <b>Lose Focus</b> in the <b>Get focus </b>actions for the other elements. And this works!<br />
<br />
But it is quite cumbersome to define these actions for each Autocomplete item and all the other elements we want to monitor. Besides the task would be difficult for developers who are not so familiar with JavaScript. So I decided to create a plug-in for it.<br />
<h3>
Autocomplete change detection DA Plug-in</h3>
So I have developed a plug-in for watching changes in Autocomplete items and triggering an event when a change is detected.<br />
You can find the plug-in on <b><a href="http://apex.world/">apex.world</a>. </b><br />
You can apply this plug-in on Page Load. The plug-in automatically watches all Text Fields with autocomplete on the page.<br />
<br />
When the value of an Autocomplete item is changed, an event is fired from that autocomplete item. You can find the event in the event in the Component Event chapter of the Event list, as you can see in the DA definition below:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoe25lPxkimSAufpw1eoxJeurAT8Y-NbGgn2jje_E6BYy2MbeYBVxNJRt8VLd4ArmgmLn96g0HPW9BcPXY-QsgEkaqxgiEXwq0i0Y_4aRo7hcPT78v8Q0anSeUQNrrmN_ZxzYx5rM3OZFD/s1600/APEX+WAC+DA+event.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="704" data-original-width="1034" height="271" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoe25lPxkimSAufpw1eoxJeurAT8Y-NbGgn2jje_E6BYy2MbeYBVxNJRt8VLd4ArmgmLn96g0HPW9BcPXY-QsgEkaqxgiEXwq0i0Y_4aRo7hcPT78v8Q0anSeUQNrrmN_ZxzYx5rM3OZFD/s400/APEX+WAC+DA+event.png" width="400" /></a></div>
<br />
<br />
<br />
Happy APEXing.<br />
<br />
<br />
<br />
<br />
<br />Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com0tag:blogger.com,1999:blog-4312362131290962824.post-18604713222845468732020-08-01T10:34:00.002+02:002020-08-01T10:35:06.743+02:00APEX 20.1 nugget: Mega menu'sA few days ago I downloaded and installed APEX 20.1. I was inspired by the presentations of Shakeeb Rahman and Patrick Wolf on <b>ODTUG Learn from home</b>. They talked about mega menu and user friendly URL's. And I thought, I want to have this too!<br />
<br />
So I downloaded the installation package, studied the installation/upgrade manual carefully, and followed the instructions as well as possible. In the past I have had troubles with ords and static files support after upgrading, costing me hours to fix. This time everything went smoothly and after performing all the indicated steps I fired up my new APEX 20.1, and it worked :-).<br />
I suppose it pays to RTFM.<br />
<h2>
Mega menu's</h2>
<div>
Mega menu's provide a large menu, where you have all the menu options available at once. </div>
<div>
In my APEX demo application the menu's have been a constant struggle for me. I wanted all the pages to be reachable from the menu directly, but this way the menu grew too long and the options disappeared below the bottom of the screen (even on my 27" iMac!). </div>
<div>
So mega menu's were very promising. And indeed they did fulfill my expectations.:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1ZhJ28WJwrajy_IcpNVcm7NY-yWgvJ3NnpRDUMfiYkDKN9qA6Zaov2rMC9_4S90fOvTH5cKrZwS41y_PZox7f9EBud-GZ-i2dkv9b3zsdwGXNkF5_U_pdR5KxTNI4xbbqmRkrGL4vuDdU/s1600/Schermafbeelding+2020-08-01+om+10.09.12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="469" data-original-width="1600" height="186" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1ZhJ28WJwrajy_IcpNVcm7NY-yWgvJ3NnpRDUMfiYkDKN9qA6Zaov2rMC9_4S90fOvTH5cKrZwS41y_PZox7f9EBud-GZ-i2dkv9b3zsdwGXNkF5_U_pdR5KxTNI4xbbqmRkrGL4vuDdU/s640/Schermafbeelding+2020-08-01+om+10.09.12.png" width="640" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
The nice thing is that Mega menu's work on the same data as the original APEX side menu. So initially you do not have to change anything to start using them. Just change some setting in the User Interface and you have your own Mega Menu:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEij4I2C76jhV82KG5PRUQEUpwoa5pNcL2yWdncko5NK5Z10dGa8VJ6HhEuJy01EEiCpdpgcKtNuWfLU9NiTxFktkPWZ1PNMUnxnHWOnLaVNuBmHQMfRKuyj7CKz-Axz14aWI7e9OElZVCdX/s1600/Schermafbeelding+2020-08-01+om+10.08.49.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="718" data-original-width="1426" height="321" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEij4I2C76jhV82KG5PRUQEUpwoa5pNcL2yWdncko5NK5Z10dGa8VJ6HhEuJy01EEiCpdpgcKtNuWfLU9NiTxFktkPWZ1PNMUnxnHWOnLaVNuBmHQMfRKuyj7CKz-Axz14aWI7e9OElZVCdX/s640/Schermafbeelding+2020-08-01+om+10.08.49.png" width="640" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
You can visit my APEX demo application <a href="https://www.speech2form.com/ords/detora/r/opfg" target="_blank">https://www.speech2form.com/ords/detora/r/opfg</a> to see the Mega Menu in action. It took me about half an hour to reconstruct the menu. Btw, do you notice the user friendly URL? </div>
<div>
<br /></div>
<div>
Of course once you have got your menu, you can tweak the content to your liking. Watch this <a href="https://www.youtube.com/watch?v=ZBBtADTSm6g" target="_blank">video </a>by Shakeeb Rahman on Mega Menu's. Unfortunately I was not able to find a recording of the webinar from ODTUG Learn from Home Series in which Shakeeb demonstrates what you can do with Mega Menu's in great detail. </div>
<div>
<br /></div>
<div>
Happy APEXing</div>
<div>
<br /></div>
Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com0tag:blogger.com,1999:blog-4312362131290962824.post-83637196862488850282020-02-12T10:27:00.000+01:002020-02-12T10:43:34.451+01:00Oracle APEX Geolocation 1: How to get your location<!--[if gte mso 9]><xml>
<o:DocumentProperties>
<o:Revision>0</o:Revision>
<o:TotalTime>0</o:TotalTime>
<o:Pages>1</o:Pages>
<o:Words>531</o:Words>
<o:Characters>2926</o:Characters>
<o:Company>Detora</o:Company>
<o:Lines>24</o:Lines>
<o:Paragraphs>6</o:Paragraphs>
<o:CharactersWithSpaces>3451</o:CharactersWithSpaces>
<o:Version>14.0</o:Version>
</o:DocumentProperties>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
</o:OfficeDocumentSettings>
</xml><![endif]-->
<!--[if gte mso 9]><xml>
<w:WordDocument>
<w:View>Normal</w:View>
<w:Zoom>0</w:Zoom>
<w:TrackMoves/>
<w:TrackFormatting/>
<w:HyphenationZone>21</w:HyphenationZone>
<w:PunctuationKerning/>
<w:ValidateAgainstSchemas/>
<w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid>
<w:IgnoreMixedContent>false</w:IgnoreMixedContent>
<w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText>
<w:DoNotPromoteQF/>
<w:LidThemeOther>NL</w:LidThemeOther>
<w:LidThemeAsian>JA</w:LidThemeAsian>
<w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript>
<w:Compatibility>
<w:BreakWrappedTables/>
<w:SnapToGridInCell/>
<w:WrapTextWithPunct/>
<w:UseAsianBreakRules/>
<w:DontGrowAutofit/>
<w:SplitPgBreakAndParaMark/>
<w:EnableOpenTypeKerning/>
<w:DontFlipMirrorIndents/>
<w:OverrideTableStyleHps/>
<w:UseFELayout/>
</w:Compatibility>
<m:mathPr>
<m:mathFont m:val="Cambria Math"/>
<m:brkBin m:val="before"/>
<m:brkBinSub m:val="--"/>
<m:smallFrac m:val="off"/>
<m:dispDef/>
<m:lMargin m:val="0"/>
<m:rMargin m:val="0"/>
<m:defJc m:val="centerGroup"/>
<m:wrapIndent m:val="1440"/>
<m:intLim m:val="subSup"/>
<m:naryLim m:val="undOvr"/>
</m:mathPr></w:WordDocument>
</xml><![endif]--><!--[if gte mso 9]><xml>
<w:LatentStyles DefLockedState="false" DefUnhideWhenUsed="true"
DefSemiHidden="true" DefQFormat="false" DefPriority="99"
LatentStyleCount="276">
<w:LsdException Locked="false" Priority="0" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Normal"/>
<w:LsdException Locked="false" Priority="9" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="heading 1"/>
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 2"/>
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 3"/>
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 4"/>
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 5"/>
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 6"/>
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 7"/>
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 8"/>
<w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 9"/>
<w:LsdException Locked="false" Priority="39" Name="toc 1"/>
<w:LsdException Locked="false" Priority="39" Name="toc 2"/>
<w:LsdException Locked="false" Priority="39" Name="toc 3"/>
<w:LsdException Locked="false" Priority="39" Name="toc 4"/>
<w:LsdException Locked="false" Priority="39" Name="toc 5"/>
<w:LsdException Locked="false" Priority="39" Name="toc 6"/>
<w:LsdException Locked="false" Priority="39" Name="toc 7"/>
<w:LsdException Locked="false" Priority="39" Name="toc 8"/>
<w:LsdException Locked="false" Priority="39" Name="toc 9"/>
<w:LsdException Locked="false" Priority="35" QFormat="true" Name="caption"/>
<w:LsdException Locked="false" Priority="10" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Title"/>
<w:LsdException Locked="false" Priority="1" Name="Default Paragraph Font"/>
<w:LsdException Locked="false" Priority="11" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Subtitle"/>
<w:LsdException Locked="false" Priority="22" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Strong"/>
<w:LsdException Locked="false" Priority="20" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Emphasis"/>
<w:LsdException Locked="false" Priority="59" SemiHidden="false"
UnhideWhenUsed="false" Name="Table Grid"/>
<w:LsdException Locked="false" UnhideWhenUsed="false" Name="Placeholder Text"/>
<w:LsdException Locked="false" Priority="1" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="No Spacing"/>
<w:LsdException Locked="false" Priority="60" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Shading"/>
<w:LsdException Locked="false" Priority="61" SemiHidden="false"
UnhideWhenUsed="false" Name="Light List"/>
<w:LsdException Locked="false" Priority="62" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Grid"/>
<w:LsdException Locked="false" Priority="63" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 1"/>
<w:LsdException Locked="false" Priority="64" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 2"/>
<w:LsdException Locked="false" Priority="65" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 1"/>
<w:LsdException Locked="false" Priority="66" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 2"/>
<w:LsdException Locked="false" Priority="67" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 1"/>
<w:LsdException Locked="false" Priority="68" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 2"/>
<w:LsdException Locked="false" Priority="69" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 3"/>
<w:LsdException Locked="false" Priority="70" SemiHidden="false"
UnhideWhenUsed="false" Name="Dark List"/>
<w:LsdException Locked="false" Priority="71" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Shading"/>
<w:LsdException Locked="false" Priority="72" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful List"/>
<w:LsdException Locked="false" Priority="73" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Grid"/>
<w:LsdException Locked="false" Priority="60" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Shading Accent 1"/>
<w:LsdException Locked="false" Priority="61" SemiHidden="false"
UnhideWhenUsed="false" Name="Light List Accent 1"/>
<w:LsdException Locked="false" Priority="62" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Grid Accent 1"/>
<w:LsdException Locked="false" Priority="63" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 1"/>
<w:LsdException Locked="false" Priority="64" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 1"/>
<w:LsdException Locked="false" Priority="65" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 1 Accent 1"/>
<w:LsdException Locked="false" UnhideWhenUsed="false" Name="Revision"/>
<w:LsdException Locked="false" Priority="34" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="List Paragraph"/>
<w:LsdException Locked="false" Priority="29" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Quote"/>
<w:LsdException Locked="false" Priority="30" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Intense Quote"/>
<w:LsdException Locked="false" Priority="66" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 2 Accent 1"/>
<w:LsdException Locked="false" Priority="67" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 1"/>
<w:LsdException Locked="false" Priority="68" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 1"/>
<w:LsdException Locked="false" Priority="69" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 1"/>
<w:LsdException Locked="false" Priority="70" SemiHidden="false"
UnhideWhenUsed="false" Name="Dark List Accent 1"/>
<w:LsdException Locked="false" Priority="71" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Shading Accent 1"/>
<w:LsdException Locked="false" Priority="72" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful List Accent 1"/>
<w:LsdException Locked="false" Priority="73" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Grid Accent 1"/>
<w:LsdException Locked="false" Priority="60" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Shading Accent 2"/>
<w:LsdException Locked="false" Priority="61" SemiHidden="false"
UnhideWhenUsed="false" Name="Light List Accent 2"/>
<w:LsdException Locked="false" Priority="62" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Grid Accent 2"/>
<w:LsdException Locked="false" Priority="63" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 2"/>
<w:LsdException Locked="false" Priority="64" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 2"/>
<w:LsdException Locked="false" Priority="65" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 1 Accent 2"/>
<w:LsdException Locked="false" Priority="66" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 2 Accent 2"/>
<w:LsdException Locked="false" Priority="67" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 2"/>
<w:LsdException Locked="false" Priority="68" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 2"/>
<w:LsdException Locked="false" Priority="69" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 2"/>
<w:LsdException Locked="false" Priority="70" SemiHidden="false"
UnhideWhenUsed="false" Name="Dark List Accent 2"/>
<w:LsdException Locked="false" Priority="71" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Shading Accent 2"/>
<w:LsdException Locked="false" Priority="72" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful List Accent 2"/>
<w:LsdException Locked="false" Priority="73" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Grid Accent 2"/>
<w:LsdException Locked="false" Priority="60" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Shading Accent 3"/>
<w:LsdException Locked="false" Priority="61" SemiHidden="false"
UnhideWhenUsed="false" Name="Light List Accent 3"/>
<w:LsdException Locked="false" Priority="62" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Grid Accent 3"/>
<w:LsdException Locked="false" Priority="63" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 3"/>
<w:LsdException Locked="false" Priority="64" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 3"/>
<w:LsdException Locked="false" Priority="65" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 1 Accent 3"/>
<w:LsdException Locked="false" Priority="66" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 2 Accent 3"/>
<w:LsdException Locked="false" Priority="67" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 3"/>
<w:LsdException Locked="false" Priority="68" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 3"/>
<w:LsdException Locked="false" Priority="69" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 3"/>
<w:LsdException Locked="false" Priority="70" SemiHidden="false"
UnhideWhenUsed="false" Name="Dark List Accent 3"/>
<w:LsdException Locked="false" Priority="71" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Shading Accent 3"/>
<w:LsdException Locked="false" Priority="72" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful List Accent 3"/>
<w:LsdException Locked="false" Priority="73" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Grid Accent 3"/>
<w:LsdException Locked="false" Priority="60" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Shading Accent 4"/>
<w:LsdException Locked="false" Priority="61" SemiHidden="false"
UnhideWhenUsed="false" Name="Light List Accent 4"/>
<w:LsdException Locked="false" Priority="62" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Grid Accent 4"/>
<w:LsdException Locked="false" Priority="63" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 4"/>
<w:LsdException Locked="false" Priority="64" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 4"/>
<w:LsdException Locked="false" Priority="65" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 1 Accent 4"/>
<w:LsdException Locked="false" Priority="66" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 2 Accent 4"/>
<w:LsdException Locked="false" Priority="67" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 4"/>
<w:LsdException Locked="false" Priority="68" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 4"/>
<w:LsdException Locked="false" Priority="69" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 4"/>
<w:LsdException Locked="false" Priority="70" SemiHidden="false"
UnhideWhenUsed="false" Name="Dark List Accent 4"/>
<w:LsdException Locked="false" Priority="71" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Shading Accent 4"/>
<w:LsdException Locked="false" Priority="72" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful List Accent 4"/>
<w:LsdException Locked="false" Priority="73" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Grid Accent 4"/>
<w:LsdException Locked="false" Priority="60" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Shading Accent 5"/>
<w:LsdException Locked="false" Priority="61" SemiHidden="false"
UnhideWhenUsed="false" Name="Light List Accent 5"/>
<w:LsdException Locked="false" Priority="62" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Grid Accent 5"/>
<w:LsdException Locked="false" Priority="63" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 5"/>
<w:LsdException Locked="false" Priority="64" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 5"/>
<w:LsdException Locked="false" Priority="65" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 1 Accent 5"/>
<w:LsdException Locked="false" Priority="66" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 2 Accent 5"/>
<w:LsdException Locked="false" Priority="67" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 5"/>
<w:LsdException Locked="false" Priority="68" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 5"/>
<w:LsdException Locked="false" Priority="69" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 5"/>
<w:LsdException Locked="false" Priority="70" SemiHidden="false"
UnhideWhenUsed="false" Name="Dark List Accent 5"/>
<w:LsdException Locked="false" Priority="71" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Shading Accent 5"/>
<w:LsdException Locked="false" Priority="72" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful List Accent 5"/>
<w:LsdException Locked="false" Priority="73" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Grid Accent 5"/>
<w:LsdException Locked="false" Priority="60" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Shading Accent 6"/>
<w:LsdException Locked="false" Priority="61" SemiHidden="false"
UnhideWhenUsed="false" Name="Light List Accent 6"/>
<w:LsdException Locked="false" Priority="62" SemiHidden="false"
UnhideWhenUsed="false" Name="Light Grid Accent 6"/>
<w:LsdException Locked="false" Priority="63" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 6"/>
<w:LsdException Locked="false" Priority="64" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 6"/>
<w:LsdException Locked="false" Priority="65" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 1 Accent 6"/>
<w:LsdException Locked="false" Priority="66" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium List 2 Accent 6"/>
<w:LsdException Locked="false" Priority="67" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 6"/>
<w:LsdException Locked="false" Priority="68" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 6"/>
<w:LsdException Locked="false" Priority="69" SemiHidden="false"
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 6"/>
<w:LsdException Locked="false" Priority="70" SemiHidden="false"
UnhideWhenUsed="false" Name="Dark List Accent 6"/>
<w:LsdException Locked="false" Priority="71" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Shading Accent 6"/>
<w:LsdException Locked="false" Priority="72" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful List Accent 6"/>
<w:LsdException Locked="false" Priority="73" SemiHidden="false"
UnhideWhenUsed="false" Name="Colorful Grid Accent 6"/>
<w:LsdException Locked="false" Priority="19" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Subtle Emphasis"/>
<w:LsdException Locked="false" Priority="21" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Intense Emphasis"/>
<w:LsdException Locked="false" Priority="31" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Subtle Reference"/>
<w:LsdException Locked="false" Priority="32" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Intense Reference"/>
<w:LsdException Locked="false" Priority="33" SemiHidden="false"
UnhideWhenUsed="false" QFormat="true" Name="Book Title"/>
<w:LsdException Locked="false" Priority="37" Name="Bibliography"/>
<w:LsdException Locked="false" Priority="39" QFormat="true" Name="TOC Heading"/>
</w:LatentStyles>
</xml><![endif]-->
<style>
<!--
/* Font Definitions */
@font-face
{font-family:"MS 明朝";
panose-1:0 0 0 0 0 0 0 0 0 0;
mso-font-charset:128;
mso-generic-font-family:roman;
mso-font-format:other;
mso-font-pitch:fixed;
mso-font-signature:1 134676480 16 0 131072 0;}
@font-face
{font-family:"MS ゴシック";
panose-1:0 0 0 0 0 0 0 0 0 0;
mso-font-charset:128;
mso-generic-font-family:modern;
mso-font-format:other;
mso-font-pitch:fixed;
mso-font-signature:1 134676480 16 0 131072 0;}
@font-face
{font-family:"MS ゴシック";
panose-1:0 0 0 0 0 0 0 0 0 0;
mso-font-charset:128;
mso-generic-font-family:modern;
mso-font-format:other;
mso-font-pitch:fixed;
mso-font-signature:1 134676480 16 0 131072 0;}
@font-face
{font-family:Calibri;
panose-1:2 15 5 2 2 2 4 3 2 4;
mso-font-charset:0;
mso-generic-font-family:auto;
mso-font-pitch:variable;
mso-font-signature:-520092929 1073786111 9 0 415 0;}
@font-face
{font-family:Cambria;
panose-1:2 4 5 3 5 4 6 3 2 4;
mso-font-charset:0;
mso-generic-font-family:auto;
mso-font-pitch:variable;
mso-font-signature:-536870145 1073743103 0 0 415 0;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{mso-style-unhide:no;
mso-style-qformat:yes;
mso-style-parent:"";
margin:0cm;
margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:12.0pt;
font-family:Cambria;
mso-ascii-font-family:Cambria;
mso-ascii-theme-font:minor-latin;
mso-fareast-font-family:"MS 明朝";
mso-fareast-theme-font:minor-fareast;
mso-hansi-font-family:Cambria;
mso-hansi-theme-font:minor-latin;
mso-bidi-font-family:"Times New Roman";
mso-bidi-theme-font:minor-bidi;
mso-ansi-language:NL;}
h1
{mso-style-priority:9;
mso-style-unhide:no;
mso-style-qformat:yes;
mso-style-link:"Kop 1 Teken";
mso-style-next:Normaal;
margin-top:24.0pt;
margin-right:0cm;
margin-bottom:0cm;
margin-left:0cm;
margin-bottom:.0001pt;
mso-pagination:widow-orphan lines-together;
page-break-after:avoid;
mso-outline-level:1;
font-size:16.0pt;
font-family:Calibri;
mso-ascii-font-family:Calibri;
mso-ascii-theme-font:major-latin;
mso-fareast-font-family:"MS ゴシック";
mso-fareast-theme-font:major-fareast;
mso-hansi-font-family:Calibri;
mso-hansi-theme-font:major-latin;
mso-bidi-font-family:"Times New Roman";
mso-bidi-theme-font:major-bidi;
color:#345A8A;
mso-themecolor:accent1;
mso-themeshade:181;
mso-font-kerning:0pt;
mso-ansi-language:NL;}
h2
{mso-style-priority:9;
mso-style-qformat:yes;
mso-style-link:"Kop 2 Teken";
mso-style-next:Normaal;
margin-top:10.0pt;
margin-right:0cm;
margin-bottom:0cm;
margin-left:0cm;
margin-bottom:.0001pt;
mso-pagination:widow-orphan lines-together;
page-break-after:avoid;
mso-outline-level:2;
font-size:13.0pt;
font-family:Calibri;
mso-ascii-font-family:Calibri;
mso-ascii-theme-font:major-latin;
mso-fareast-font-family:"MS ゴシック";
mso-fareast-theme-font:major-fareast;
mso-hansi-font-family:Calibri;
mso-hansi-theme-font:major-latin;
mso-bidi-font-family:"Times New Roman";
mso-bidi-theme-font:major-bidi;
color:#4F81BD;
mso-themecolor:accent1;
mso-ansi-language:NL;}
a:link, span.MsoHyperlink
{mso-style-priority:99;
color:blue;
mso-themecolor:hyperlink;
text-decoration:underline;
text-underline:single;}
a:visited, span.MsoHyperlinkFollowed
{mso-style-noshow:yes;
mso-style-priority:99;
color:purple;
mso-themecolor:followedhyperlink;
text-decoration:underline;
text-underline:single;}
span.Kop1Teken
{mso-style-name:"Kop 1 Teken";
mso-style-priority:9;
mso-style-unhide:no;
mso-style-locked:yes;
mso-style-link:"Kop 1";
mso-ansi-font-size:16.0pt;
mso-bidi-font-size:16.0pt;
font-family:Calibri;
mso-ascii-font-family:Calibri;
mso-ascii-theme-font:major-latin;
mso-fareast-font-family:"MS ゴシック";
mso-fareast-theme-font:major-fareast;
mso-hansi-font-family:Calibri;
mso-hansi-theme-font:major-latin;
mso-bidi-font-family:"Times New Roman";
mso-bidi-theme-font:major-bidi;
color:#345A8A;
mso-themecolor:accent1;
mso-themeshade:181;
font-weight:bold;}
span.Kop2Teken
{mso-style-name:"Kop 2 Teken";
mso-style-priority:9;
mso-style-unhide:no;
mso-style-locked:yes;
mso-style-link:"Kop 2";
mso-ansi-font-size:13.0pt;
mso-bidi-font-size:13.0pt;
font-family:Calibri;
mso-ascii-font-family:Calibri;
mso-ascii-theme-font:major-latin;
mso-fareast-font-family:"MS ゴシック";
mso-fareast-theme-font:major-fareast;
mso-hansi-font-family:Calibri;
mso-hansi-theme-font:major-latin;
mso-bidi-font-family:"Times New Roman";
mso-bidi-theme-font:major-bidi;
color:#4F81BD;
mso-themecolor:accent1;
font-weight:bold;}
.MsoChpDefault
{mso-style-type:export-only;
mso-default-props:yes;
font-family:Cambria;
mso-ascii-font-family:Cambria;
mso-ascii-theme-font:minor-latin;
mso-fareast-font-family:"MS 明朝";
mso-fareast-theme-font:minor-fareast;
mso-hansi-font-family:Cambria;
mso-hansi-theme-font:minor-latin;
mso-bidi-font-family:"Times New Roman";
mso-bidi-theme-font:minor-bidi;
mso-ansi-language:NL;}
@page WordSection1
{size:595.0pt 842.0pt;
margin:70.85pt 70.85pt 70.85pt 70.85pt;
mso-header-margin:35.4pt;
mso-footer-margin:35.4pt;
mso-paper-source:0;}
div.WordSection1
{page:WordSection1;}
</style>
<br />
-->
<!--[if gte mso 10]>
<style>
/* Style Definitions */
table.MsoNormalTable
{mso-style-name:Standaardtabel;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:12.0pt;
font-family:Cambria;
mso-ascii-font-family:Cambria;
mso-ascii-theme-font:minor-latin;
mso-hansi-font-family:Cambria;
mso-hansi-theme-font:minor-latin;
mso-ansi-language:NL;}
</style>
<![endif]-->
<!--StartFragment-->
<br />
<div class="MsoNormal" style="font-size: medium;">
<span style="font-size: 12pt;">Some decades ago people used to work in an office. And occasionally they went on business trips. Nowadays users are much more mobile. They can work from home, form the office or from a field location. So their location varies and it can be useful information for the applications they use.</span><span style="font-size: 12pt;">The
location might be tied to a customer or a fact that should be reported. This
series of blogposts will deal with determining the location in Oracle APEX, and
how to use and display it:</span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;"><a href="https://dickdral.blogspot.com/2020/02/oracle-apex-geolocation-1-how-to-get.html">Part 1:Getting the location</a><o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;">Part 2: </span>Using Oracle Spatial</div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;">Part 3:
Display location(s) on map<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;">Part 4:
Correct locations on map<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;">Part 5:
Getting location data from photos<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;"><i>The parts
without link are not yet available. </i><o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;">There is an
example application available at :<span style="mso-spacerun: yes;"> </span><a href="https://github.com/dickdral/apex_location_demo" target="_blank">https://github.com/dickdral/apex_location_demo</a><o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<h2>
<span lang="EN-US" style="mso-ansi-language: EN-US;">Getting your location in
the browser<o:p></o:p></span></h2>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;">In order to
retrieve your GPS location in the browser some JavaScript needs to be applied: <o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;">function
getLocation() {<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;"><span style="mso-spacerun: yes;"> </span>if (navigator.geolocation) {<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;"><span style="mso-spacerun: yes;">
</span>navigator.geolocation.getCurrentPosition(function(position) {<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;"><span style="mso-spacerun: yes;"> </span>alert(`Latitude:
${position.coords.latitude} \n` + <o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;"><span style="mso-spacerun: yes;"> </span>`Longitude:
${position.coords.longitude}`);<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;"><span style="mso-spacerun: yes;"> </span>});<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;"><span style="mso-spacerun: yes;"> </span>} else {<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;"><span style="mso-spacerun: yes;"> </span>alert("Geolocation is not supported by
this browser.");<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;"><span style="mso-spacerun: yes;"> </span>}<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;">}<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;">The most
important line is the call to <b style="mso-bidi-font-weight: normal;">navigator.geolocation.getCurrentPosition</b>.
<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;"><span style="mso-spacerun: yes;"> </span>This is the function to retrieve the GPS
location. As this may take several seconds the function is asynchronous.<span style="mso-spacerun: yes;"> </span>The function to be performed after retrieval
of the location is the argument for the function. There also is a check when
the function is not available. <o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;">After the
call JavaScript will immediately continue with others tasks. Otherwise the page
would hang until the function returns. <o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b style="mso-bidi-font-weight: normal;"><span lang="EN-US" style="mso-ansi-language: EN-US;"><i>NB The function can only be called from a https
web site. </i></span></b><span lang="EN-US" style="mso-ansi-language: EN-US;"><o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<h2>
<span lang="EN-US" style="mso-ansi-language: EN-US;">Accuracy<o:p></o:p></span></h2>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;">In ideal
situations the accuracy of the location is within a few meters. In less
favorable situations this might expand to tens of even hundred of meters. Especially
in an urban environment with many high buildings the visiblity of the GPS
sattelites is less good and the accuracy is less accordingly. <o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;">More
information on GPS accuracy can be found </span><a href="https://www.storelocatorwidgets.com/blogpost/20453/Everything_you_ever_wanted_to_know_about_HTML5_Geolocation_Accuracy" style="font-size: 12pt;" target="_blank"> here</a></div>
<h2>
<span lang="EN-US" style="mso-ansi-language: EN-US;">Privacy<o:p></o:p></span></h2>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;">Privacy can
be an issue when you retrieve and store the location of employees, customers
etc. The privacy legislation differs per country. <o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;">As of May
2018 the General Data Protection Regulation is issued by the EU. This law
contains high penalties for offenders. It is wise to take notice of the privacy
regulations in the countries where the application will be used. <o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<h2>
<span lang="EN-US" style="mso-ansi-language: EN-US;">Getting location using a
APEX plug-in<o:p></o:p></span></h2>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;">For APEX
developers it is not necessary to write JavaScript to retrieve the GPS
location. You can use the Store location plug-in that can be found on <a href="https://apex.world/" target="_blank">https://apex.world</a>. <o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;">After
importing the plug-in in your application, you can reference it in a dynamic
action. All you need to fill in are the APEX items to store the latitude and
longitude once the location is retrieved. <o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJYsclkGRG6vYFKUJ8OeVyccYmXwgBYYNzaQjJDvTu5mA4q479zb3EDd-JJBD7uZEvEQLd4Vb2aAvwMK7xxnEZbOO1gtMxD40U6Fxhm3dsoVpNZqH5KeiPB1KbcZAh8Gr3cWyYwtJc6jfl/s1600/Schermafbeelding+2020-02-12+om+11.21.53.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="427" data-original-width="906" height="150" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJYsclkGRG6vYFKUJ8OeVyccYmXwgBYYNzaQjJDvTu5mA4q479zb3EDd-JJBD7uZEvEQLd4Vb2aAvwMK7xxnEZbOO1gtMxD40U6Fxhm3dsoVpNZqH5KeiPB1KbcZAh8Gr3cWyYwtJc6jfl/s320/Schermafbeelding+2020-02-12+om+11.21.53.png" width="320" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;">To define
follow-up actions a custom DA can be defined acting on the event <b style="mso-bidi-font-weight: normal;">location-retreived</b>. <o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVHe0H8tu8jxiD22bguxBXFAKo-gNMn7SpZh6VImJpWi9rL8wg08tDRXSb68keOtsDaK6eGcFeo1bBqTgve-yJJecpuY1rFppGlA2lk8GASPqgIVXQy4WraxQKxNcVC0DGYl3x4terZv8U/s1600/Schermafbeelding+2020-02-12+om+11.23.07.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="746" data-original-width="900" height="265" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVHe0H8tu8jxiD22bguxBXFAKo-gNMn7SpZh6VImJpWi9rL8wg08tDRXSb68keOtsDaK6eGcFeo1bBqTgve-yJJecpuY1rFppGlA2lk8GASPqgIVXQy4WraxQKxNcVC0DGYl3x4terZv8U/s320/Schermafbeelding+2020-02-12+om+11.23.07.png" width="320" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US" style="mso-ansi-language: EN-US;">Happy
apexing ;-)<o:p></o:p></span></div>
<!--EndFragment--><br />Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com1tag:blogger.com,1999:blog-4312362131290962824.post-76818297918628748802019-12-21T13:01:00.000+01:002019-12-21T13:01:27.800+01:00Missing grid layout attributes for APEX itemsWhilw preparing for a new presentation I created a new application using the APEX Create Application Wizard and started to create pages and adding items.<br />
While doing so I noticed I missed the usual Grid layout attributes in the Layout section of the item attributes:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiw1GN0v1GeWfHH1Knn6U90_l-iezVKLunbb2wuDYCU36jkBgCxFHo_HGc6vrd2hTZcBKIK8LdfwIeiHCLMCEGibhIT1MCkjCBKwsAmrHyt6vNE1Vucp8dBe6XdcDipI3cPiWqU0KQQfj3a/s1600/Schermafbeelding+2019-12-21+om+12.52.37.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="442" data-original-width="1110" height="127" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiw1GN0v1GeWfHH1Knn6U90_l-iezVKLunbb2wuDYCU36jkBgCxFHo_HGc6vrd2hTZcBKIK8LdfwIeiHCLMCEGibhIT1MCkjCBKwsAmrHyt6vNE1Vucp8dBe6XdcDipI3cPiWqU0KQQfj3a/s320/Schermafbeelding+2019-12-21+om+12.52.37.png" width="320" /></a></div>
<br />
No column info etcetera. :-(.<br />
I have spent at least an hour looking for a setting that governs this behavior. Without a result...<br />
Also Google did not provide me with the right answers.<br />
<br />
Until I created a new application with all the optional pages. When I set out to examine one of these pages I noticed that the region in the Breadcrumb position did not have the grid layout attributes either.<br />
Going back to my other application I saw that all the regions were created in the Breadcrumb Bar position...<br />
So moving the regions to the Content Body give me back my Grid Layout options (I really missed them).<br />
<br />
Happy APEXing<br />
<br />
<br />Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com1tag:blogger.com,1999:blog-4312362131290962824.post-3417610740989372382019-11-07T12:30:00.000+01:002019-11-07T12:30:21.523+01:00Internet Explorer does not show content of Modal DialogIn my current assignment I develop using either Firefox or Chrome. But the organization also uses Internet Explorer 11. <div>
<br /></div>
<div>
<div>
So this morning one of the testers calls me and shows an empty Modal Dialog in Internet Explorer. When using Firefox or Chrome the Modal Dialog shows the expected content. So where is the difference. </div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9-Hxf0uVvL9fygLzKnoD434DevCod4rl0ysBBdSoKSh77IN1QWMKNxeZEmDH3IW7tX491tvwk8RC7EHf6sS_FAZe11xUquLRDsKzhxMF-2t41HQ2E2B_rE0NDWMMrVASVohBQObKly9Kk/s1600/leeg_modal.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="257" data-original-width="929" height="176" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9-Hxf0uVvL9fygLzKnoD434DevCod4rl0ysBBdSoKSh77IN1QWMKNxeZEmDH3IW7tX491tvwk8RC7EHf6sS_FAZe11xUquLRDsKzhxMF-2t41HQ2E2B_rE0NDWMMrVASVohBQObKly9Kk/s640/leeg_modal.png" width="640" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
So I reproduce the situation in the Development environment and there the same behavior shows when using Internet Explorer: an empty modal dialog. After opening the Developer Tools the cause shows in the console window: a Javascript error. </div>
<div>
<br /></div>
<div>
<span style="color: red;"><b>Invalid character</b></span></div>
<div>
<br /></div>
<div>
The error points to the JS code below:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">$( `[headers="${id}"] input[type="checkbox"]`).each(function() {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> if ( $(this).prop('checked') != checked_status ) {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> $(this).click(); </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">});</span></div>
</div>
<div>
<br /></div>
<div>
In the above code snippet a template string is used enclosed with the backtick character (`). You can find more information on this technique on <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals</a>. And at the end of the page there is a compatibility table, in which it is clear that Internet Explorer does not support this feature. </div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhply5xsZ3CDIMkLgS6HBYqNBAFr8fu0EjdJ4T_Qtht8LLG9cCH3BynrkIK36Zio9qa21kh8ovS7jonBvpjogfHCOqb_8ts1BDU9twvBoCz-QtaorExW1RbIGUscRvZ0tL1Hy0e3oU02xtf/s1600/browser_compatibility.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="381" data-original-width="1013" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhply5xsZ3CDIMkLgS6HBYqNBAFr8fu0EjdJ4T_Qtht8LLG9cCH3BynrkIK36Zio9qa21kh8ovS7jonBvpjogfHCOqb_8ts1BDU9twvBoCz-QtaorExW1RbIGUscRvZ0tL1Hy0e3oU02xtf/s640/browser_compatibility.png" width="640" /></a></div>
<div>
<div class="separator" style="clear: both; text-align: center;">
</div>
</div>
<div>
It is clear that this feature is not supported by IE :-(. </div>
<div>
<br /></div>
<div>
After a change of the code to: </div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">$( '[headers="'+id+ '"] input[type="checkbox"]').each(function() {</span></div>
<div>
<br /></div>
<div>
the modal window showed its content again in IE. </div>
<div>
<br /></div>
<div>
So when coding for IE be sure not to use too modern JavaScript. Or use a tool like Babel that supplies backward compatibility. </div>
<div>
<br /></div>
<div>
Pff...</div>
<div>
<br /></div>
<div>
But still, it is quite tricky that a JavaScript error on startup prevents the whole APEX page to be displayed. </div>
<div>
<br /></div>
<div>
Happy APEXing</div>
<div>
<br /></div>
<div>
<br /></div>
</div>
Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com1tag:blogger.com,1999:blog-4312362131290962824.post-38156315167092167322019-08-02T13:01:00.002+02:002019-08-04T10:43:59.655+02:00Oracle APEX Plug-in to retrieve GPS locationYour smartphone is aware where it is using its GPS capabilities. From HTML it is possible to retrieve this GPS location. For this you will have to write some JavaScript.<br />
The new <b>Store location</b> plug-in relieves you from writing the Javascript code. Use it by:<br />
<br />
<ul>
<li>downloading the plug-in from <a href="https://apex.world/">apex.world</a></li>
<li>importing the plug-in into your application</li>
<li>create two items on a page for the GPS coodinates (lattitude and longitude). They may be hidden items</li>
<li>creating a Page Load dynamic action on the page referencing the plug-in in the action</li>
<li>filling in the names of the GPS coordinate items in the plug-in attributes</li>
</ul>
<div>
That's all! Run your page on a smart phone and the items will be filled with the GPS coordinates of your current position. <i>For security reasons the smartphone might ask you for permission to access the GPS sensor. </i></div>
<div>
<i><br /></i></div>
<h2>
Acting upon the GPS location</h2>
<div>
Retrieving the GPS location is an asynchonous process. That means that the process is started and at some later point in time the result is stored in the items. This means that adding another in the Page Load dynamic action will access empty GPS location items because the process is not finished. </div>
<div>
To act upon the coordinates you need to define a custom dynamic action with the following specification:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDRxw0JVvdki7dvrQSjeqnLOnoQp7WSu1COhwH7cVHsfFx3mEASU8yWaLaRsYnqeq5NKlV3fE0sqERs1KbD3sNDYvVd7VHYs-s7oo9YCB92CXg-2ZoNZIWhiE-SoAtnXfiqUPLyEPn4Q-F/s1600/Schermafbeelding+2019-08-04+om+10.43.32.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="346" data-original-width="800" height="138" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDRxw0JVvdki7dvrQSjeqnLOnoQp7WSu1COhwH7cVHsfFx3mEASU8yWaLaRsYnqeq5NKlV3fE0sqERs1KbD3sNDYvVd7VHYs-s7oo9YCB92CXg-2ZoNZIWhiE-SoAtnXfiqUPLyEPn4Q-F/s320/Schermafbeelding+2019-08-04+om+10.43.32.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div>
<br /></div>
<div>
In the actions you can for example define a PL/SQL action with the GPS coordinate items as inputs. </div>
<div>
<br /></div>
<div>
In this <a href="https://dickdral.blogspot.com/2019/08/creating-mobile-app-with-apex-part-10.html" target="_blank">blogpost</a> you find a full example for applying the plug-in. </div>
<div>
<br /></div>
<div>
Happy APEXing</div>
Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com2tag:blogger.com,1999:blog-4312362131290962824.post-63286313309909286252019-08-02T13:00:00.001+02:002020-02-25T22:55:50.585+01:00Creating a mobile app with APEX - Part 10: Use your locationYour phone is packed with all kind of sensors. Many of them you can not use in JavaScript, but you can access the GPS sensor to retrieve your location. In this blogpost we will access the GPS location and use it in our application.<br />
<br />
On the <b>Activity </b>page the GPS Location will be retrieved. When the record is entered when the user is still on the location of the activity she can check <b>Use location</b>. With this item checked the GPS location is stored with the activity.<br />
When the page is opened to enter a new activity the GPS location is retrieved. It is compared to the GPS locations in the database and if possible it will provide a default value for the <b>Location </b>item.<br />
<br />
We will:<br />
<br />
<ul>
<li>update the database to add columns to store the location</li>
<li>import a plug-in to retrieve the GPS location</li>
<li>add items on the <b>Activity </b>page to store the location</li>
<li>add Dynamic Actions on the <b>Activity </b>page to act on the GPS location</li>
<li>use the stored location to provide a default value for the column <b>Location</b></li>
</ul>
<h2>
Update the database objects</h2>
<div>
There are some changes needed in the database to accommodate the location data. </div>
<div>
The columns <b>act_lattitude</b> and <b>act_longitude</b> are added to the table <b>ttm_activity</b>. We also add a column <b>act_sdo_location</b> which is of the type <b>sdo_geometry </b>to be able to use Oracle Spatial functions. </div>
<div>
The trigger on <b>ttm_activity</b> is changed to construct the column <b>act_sdo_location</b> from <b>act_lattitude</b> and <b>act_longitude</b>.</div>
<div>
The view <b>ttm_activity_vw</b> and it's instead of trigger are changed to support the new columns. </div>
<div>
The package <b>ttm_alg </b>is expanded with a function to yield a default value for <b>location</b>. </div>
<div>
<br /></div>
<div>
Download the change script from <a href="https://raw.githubusercontent.com/dickdral/ttm/master/part10/alter_objects.sql" target="_blank">here</a> and execute it. </div>
<div>
<br /></div>
<h2>
Import the Store Location plug-in</h2>
<div>
Download the <b>Store Location</b> plug-in from <a href="https://apex.world/" target="_blank">APEX World</a>. (Go to the Plug-ins page and search for <b>store location)</b></div>
<div>
<br /></div>
<div>
Load into the application plug-ins:</div>
<div>
<ul>
<li>Go to <b>Shared Components > Plug-ins</b></li>
<li>Press <b>Import</b></li>
<li>Select the plug-in file</li>
<li>Hit <b>Next</b></li>
<li>Press <b>Next </b>again</li>
<li>Hit the button <b>Install Plug-in</b></li>
</ul>
<div>
The plug-in is installed and ready for use. </div>
</div>
<div>
<br /></div>
<h2>
Add items to the Activity page</h2>
<div>
Items are needed to store the GPS coordinates that are retrieved by the plug-in. </div>
<div>
Furthermore we need a check box to indicate whether the record is entered on the location of the activity. </div>
<div>
<br /></div>
<div>
<ul>
<li>Go to page 15</li>
<li>Go to <b>P15_ACT_LOCATION</b> and press the right mouse button</li>
<li>Select <b>Create Page Item</b></li>
<ul>
<li><b>Name</b>: P15_USE_LOCATION</li>
<li><b>Type</b>: Checkbox</li>
<li><b>Appearance > Template</b>: Hidden</li>
<li><b>List of Values</b>:</li>
<ul>
<li><b>Type</b>: Static Values</li>
<li><b>Static Values</b>: STATIC:Use location;Y</li>
</ul>
</ul>
<li>Create another item:</li>
<ul>
<li><b>Name</b>: P15_ACT_LATTITUDE</li>
<li><b>Type</b>: Text Field</li>
<li><b>Label</b>: Lattitude</li>
<li><b>Source</b>:</li>
<ul>
<li><b>Type</b>: Database Column</li>
<li><b>Database Column</b>: ACT_LATTITUDE</li>
</ul>
</ul>
<li>And another one:</li>
<ul>
<li><b>Name</b>: P15_ACT_LONGITUDE</li>
<li><b>Type</b>: Text Field</li>
<li><b>Label</b>: Longitude</li>
<li><b>Source</b>:</li>
<ul>
<li><b>Type</b>: Database Column</li>
<li><b>Database Column</b>: ACT_LONGITUDE</li>
</ul>
</ul>
</ul>
</div>
<h2>
Adding the Dynamic Actions</h2>
<div>
Two dynamic actions will be added. One to retrieve the GPS location and another to react on the retrieval of the location. These are separate actions because the retrieval of the GPS location is an asynchronous process. </div>
<div>
<br /></div>
<div>
<ul>
<li>Go to page 15</li>
<li>Go to the Dynamic Actions tab</li>
<li>Right click on <b>Page Load</b> and click <b>Create Dynamic Action</b></li>
<ul>
<li><b>Name</b>: Page Load - Retieve GPS location</li>
<li>Select the <b>Action</b></li>
<ul>
<li>Change <b>Identification > Action</b> to <b>Store Location [Plug-In]</b></li>
<li>In <b>Settings</b>:</li>
<ul>
<li><b>Lattitude Item</b>: P15_ACT_LATTITUDE</li>
<li><b>Longitude Item</b>: P15_ACT_LONGITUDE</li>
</ul>
</ul>
</ul>
<li>Right click on <b>Page Load</b> and click <b>Create Dynamic Action</b></li>
<ul>
<li><b>Name</b>: Location retrieved</li>
<li><b>When:</b></li>
<ul>
<li><b>Event</b>: Custom</li>
<li><b>Custom Event</b>: location-retrieved</li>
<li><b>Selection Type</b>: jQuery Selector</li>
<li><b>jQuery Selector</b>: html</li>
</ul>
<li>Select the <b>Action</b></li>
<ul>
<li><b>Action: </b>Execute PL/SQL Code</li>
<li><b>Settings > PL/SQL Code</b>:</li>
</ul>
</ul>
</ul>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">:P15_ACT_LOCATION := nvl(:P15_ACT_LOCATION</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , ttm_alg.default_location(:P15_ACT_LATTITUDE,:P15_ACT_LONGITUDE));</span></div>
</div>
</div>
<div>
<ul><ul><ul>
<li><b>Items to Submit</b>: P15_ACT_LATTITUDE,P15_ACT_LONGITUDE</li>
<li><b>Items to Return</b>: P15_ACT_LOCATION</li>
</ul>
</ul>
</ul>
The last Dynamic Action retrieves a default value for the field <b>Location</b> based on the GPS location. The default value is retrieved in the function <b>ttm_alg.default_location</b> that uses an Oracle Spatial query to find activities that have been recorded close to the current GPS location. The query is discussed in the last paragraphs of this post. </div>
<div>
<br />
<h2>
Eating the pudding</h2>
<div>
The proof is in..., well you know. </div>
<div>
So let's try it. </div>
<div>
<ul>
<li>log in to the application on your phone</li>
<li>press <b>New activity</b></li>
<li>after the page has been opened your permission to use the location is asked. Confirm it.</li>
<li>now scroll down to the bottom</li>
<li>(after some time) you will see the GPS coordinates</li>
<li>fill in the items of the activity</li>
<li>enter in <b>Location</b>: test GPS location</li>
<li>check <b>Use Location</b>, else the GPS location will not be stored</li>
<li>accept the changes</li>
<li>press <b>New again</b></li>
<li>scroll down to the bottom</li>
<li>(after some time) you will see the GPS coordinates</li>
<li><b>Location </b>should then contain the value you entered for the previous record: <b>test GPS location</b></li>
</ul>
<div>
If things do not work as expected use the Chrome Inspector (or any other Developer tools) to examine what is wrong. </div>
</div>
<div>
<br /></div>
<h2>
Background on selecting the default value</h2>
</div>
<div>
Based on the GPS coordinates the default value for the <b>Location</b> field is determined. This is done using a spatial query:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> select act_location</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> from ttm_activities</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> where act_lattitude is not null </span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> and act_longitude is not null</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> and act_use_location = 'Y'</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> and sdo_geom.within_distance(act_sdo_location,<b>0.2</b></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , sdo_geometry(2001,8307</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ,sdo_point_type(p_longitude,p_lattitude,null)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ,null,null),1,'unit=km') </span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> = 'TRUE'</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> order by sdo_geom.sdo_distance(act_sdo_location</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , sdo_geometry(2001,8307</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ,sdo_point_type(p_longitude,p_lattitude,null)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ,null,null),1,'unit=km') </span></div>
<div>
; </div>
</div>
<div>
<br /></div>
<div>
This query uses the Oracle Spatial function <b>sdo_geom.within_distance</b> to find all the records with a GPS location within 200 m's (the value <b>0.2</b>) from the given GPS coordinates. This proves to be a reasonable distance to correct for the bias in GPS location. </div>
<div>
The records are sorted based on the distance to the coordinates which is retrieved using the function <span style="font-family: inherit;"><b>sdo_geom.sdo_distance</b>. This way if the query delivers more than one row the one closest to the the given point is selected. </span></div>
<div>
<br /></div>
<div>
<br /></div>
<h2>
Using Oracle Spatial functions</h2>
<div>
The Oracle Spatial functions expect arguments in the form of a <b>SDO Geometry objects</b>. The column <b>act_sdo_location</b> is already stored as such an object. The GPS coordinates are numbers and need to be converted to a <b>SDO Geometry object</b>:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">sdo_geometry(2001,8307</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ,sdo_point_type(p_longitude,p_lattitude,null)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ,null,null),1,'unit=km')</span></div>
</div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
<div>
Without going to deep into the complex geometry subjects: </div>
<div>
<ul>
<li>the first parameter <b>2001</b> indicates that the shape is a point. You can also have lines, polygons etc. </li>
<li>the second parameter <b>8307</b> references the coordinate system</li>
<li>you notice the values for longitude and lattitude in the call to <b>sdo_point_type</b></li>
<li>the last parameter indicates that the unit used is kilometers. </li>
<li>more detailed information can be found in the <a href="https://docs.oracle.com/database/121/SPATL/sdo_geometry-object-type.htm#SPATL489" target="_blank">Oracle documentation</a></li>
</ul>
<div>
To be able to compare values the coordinate system and the unit used should be consistent in database and code. </div>
<div>
<br /></div>
</div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
<div style="margin: 0px;">
</div>
Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com2tag:blogger.com,1999:blog-4312362131290962824.post-77979102184151337862019-07-30T12:15:00.003+02:002019-07-30T22:54:16.746+02:00Creating a mobile app with APEX - Part 9: Put the app on your phone's home screenNow you have a cool app. But to use it you still have start your phone's browser and type an URL or select a bookmark.<br />
In this post I will show you how to put an icon for the app on the home screen of your phone. The steps are:<br />
- get suitable icons<br />
- prepare your application to use the icon(s)<br />
- put your app on the home screen<br />
<br />
For the last step only iOS is described.<br />
Because of the lack of an suitable Android device I was not able to describe the process for Android.<br />
However I expect the process on Android to be similar.<br />
<br />
<h2>
Get suitable icons</h2>
The first step is to get suitable icons. Apple sets strict standards around application icons. Among others they need to come in various prescribed sizes, like 16x16, 32x32 etc.<br />
Luckily you can find many sites on the internet to do create such an icon set for you. I have used <a href="http://realfavicongenerator.net/" target="_blank">realfavicongenerator.net</a>. On this site you provide an image as base for the icons and the whole set is generated, even including the HTML code to add to your page.<br />
<br />
<ul>
<li>go to <a href="http://realfavicongenerator.net/" target="_blank">realfavicongenerator.net</a></li>
<li>upload your image</li>
<li>set the options:</li>
<ul>
<li>you can specify a background color</li>
<li>specify the location for the files: <b>#APP_IMAGES#/img </b><i>All the way at the bottom of the page</i></li>
<li>generate the files</li>
</ul>
<li>download the ZIP file with icons </li>
<li>cut the HTML code and paste it into a file</li>
</ul>
<br />
<h2>
Prepare your application</h2>
<div>
Now we will implement these icons to our application. First we load the icons in the Static Application Files:</div>
<div>
<ul>
<li>go to <b>Shared Components > Static Application Files</b></li>
<li>click on <b>Upload Files </b>and fill in the values:</li>
<ul>
<li><b>Directory</b>: img</li>
<li><b>Files: </b><i>Choose the ZIP file with icons</i></li>
<li><b>Unzip File</b>: Yes</li>
<li>Press the <b>Upload </b>button</li>
</ul>
<li>all the icons are now available in the <b>img</b> directory in the Static Application Files</li>
</ul>
<div>
Then we can reference these files:</div>
</div>
<div>
<ul>
<li>go to <b>Shared Components > User Interface Attributes</b></li>
<li>fill <b>Favicon > Favicon HTML </b>with the HTML code from the previous paragraph</li>
</ul>
<div>
That's it. </div>
</div>
<div>
When running your application in the desktop browser, you will notice that the image in the tab next to the page name has changed to the new image. </div>
<h2>
Put your app on the home screen (iOS)</h2>
<div>
To put your application on the home screen of your iPhone: </div>
<div>
<ul>
<li>open your application in Safari on you phone</li>
<li>log in</li>
<li>press the <b>Share Button</b></li>
<li>chose <b>Add to Home Screen</b></li>
<li>you will see a form with the icon. The title of the icon can be changed here</li>
<li>press <b>Add </b> and the icon will be added to the Home Screen</li>
</ul>
<div>
This process is illustrated in the image below:</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div>
<table>
<tbody>
<tr>
<td><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWUdA8v-qO3JVEVfZh7es1EZQXLNUD1Z_2lLndAgZhgMcpE3LsCPElIQysx87C_7kDUn_S-ZXCyssKqk9g24fHB4ep-DfLN9EB7tfeHy7zvXT2rhyphenhyphenf_pZ9D9i07K2sRMYUdBrPMVLfNFjQ/s1600/application.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1600" data-original-width="900" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWUdA8v-qO3JVEVfZh7es1EZQXLNUD1Z_2lLndAgZhgMcpE3LsCPElIQysx87C_7kDUn_S-ZXCyssKqk9g24fHB4ep-DfLN9EB7tfeHy7zvXT2rhyphenhyphenf_pZ9D9i07K2sRMYUdBrPMVLfNFjQ/s320/application.png" width="180" /></a></div>
</td>
<td><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8Pg5FiXQ9ECN61xEU7mPWZL1IgQGQSWZCS9SrS9dhXM_uq929BlVMeSfKOkxFLUNykutwGao3xGKd6vRKtWPvUjfmBn3Q98o-xAliBpFBmWJjmSjLRPjYmPLgy_ruAvi2g7puyH17wf9J/s1600/share_button.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1600" data-original-width="900" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8Pg5FiXQ9ECN61xEU7mPWZL1IgQGQSWZCS9SrS9dhXM_uq929BlVMeSfKOkxFLUNykutwGao3xGKd6vRKtWPvUjfmBn3Q98o-xAliBpFBmWJjmSjLRPjYmPLgy_ruAvi2g7puyH17wf9J/s320/share_button.png" width="180" /></a></div>
</td>
</tr>
<tr>
<td><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiq8i_DjOhLIHya5LqZIX7etyefRt8Qr5i4yy7IQYSZuwFuIWIAA0sqcZjVFfr7lMSzaD8dCBRuz8qnjx87vDRMOLCYdNGS08MnKPNgLC7Nx3XGJg9tXa99xmmN8J0eWvZJXwSza7DygIjG/s1600/add_to_homescreen.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1600" data-original-width="900" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiq8i_DjOhLIHya5LqZIX7etyefRt8Qr5i4yy7IQYSZuwFuIWIAA0sqcZjVFfr7lMSzaD8dCBRuz8qnjx87vDRMOLCYdNGS08MnKPNgLC7Nx3XGJg9tXa99xmmN8J0eWvZJXwSza7DygIjG/s320/add_to_homescreen.png" width="180" /></a></div>
</td>
<td>img 4<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKEf9ybXRsUzMq7RUhILHc81ZjBWjz2_Z1r-cEauDFix4AKSbImbE8xAjy3mWK-aUNwxACMFAsxw1OSXKp_AsSxENVA74_KU62lshIrWnDzzoEZ_JMuUDHnGFZYi5c59g11G_xa6hZk8Yf/s1600/added_to_homescreen2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="320" data-original-width="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKEf9ybXRsUzMq7RUhILHc81ZjBWjz2_Z1r-cEauDFix4AKSbImbE8xAjy3mWK-aUNwxACMFAsxw1OSXKp_AsSxENVA74_KU62lshIrWnDzzoEZ_JMuUDHnGFZYi5c59g11G_xa6hZk8Yf/s1600/added_to_homescreen2.png" /></a></div>
</td>
</tr>
</tbody></table>
</div>
<div>
<br /></div>
<div>
This way you will have easy access to your application to enter data fast. </div>
<div>
<br /></div>
<div>
Happy APEXing</div>
<div>
<span id="goog_657172919"></span><span id="goog_657172920"></span><br /></div>
</div>
<style type="text/css">
li.li1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px 'Helvetica Neue'; color: #000000}
span.s1 {font-kerning: none}
span.s2 {text-decoration: underline ; font-kerning: none}
ol.ol1 {list-style-type: decimal}
</style>Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com1tag:blogger.com,1999:blog-4312362131290962824.post-44348139432282701902019-07-24T13:13:00.001+02:002019-07-24T13:16:02.170+02:00Creating a mobile app with APEX - Part 8: Implementing autologinIn the mobile app I usually enter only a few records of data like an expense or a few activities.<br />
<div>
It is pretty annoying when I have to enter username and password each time I use the app for a few seconds. </div>
<div>
<br /></div>
<div>
In most native apps the authentication is asked once and then stored safely to be reused at further use. The security lies in the presumption that the authorization has been done by unlocking the phone. For most applications this will be sufficient. Only apps with a high stake or high risk like banking apps will need additional authentication. </div>
<div>
<br /></div>
<div>
So I want this scenario also for my mobile APEX apps: </div>
<div>
<ul>
<li>enter username and password the first time</li>
<li>checking the 'Stay logged in' checkbox</li>
<li>next time when starting the app the login process is done automatically and I will be led to the starting page</li>
</ul>
</div>
<div>
We can accomplish this by using cookies to store client side data. This idea has been described by Christian Rokita in <a href="http://rokitta.blogspot.com/2012/10/remember-me-apex-autologin.html" target="_blank">this blogpost</a>. </div>
<div>
<br /></div>
<div>
I have created a bit different implementation without the need of an extra page:</div>
<div>
<ul>
<li>a custom authentication scheme is created based on a tables with users and passwords</li>
<li>a sessions table is created to store tokens and user names</li>
<li>a package will accommodate the code needed to read and write the cookies and perform the autologin</li>
<li>on the login page the autologin procedure is called to read the cookie. If the cookie points to a valid user a session is created for this user and the session is redirected to the starting page of the application</li>
<li>on the login page a <b>Stay logged in</b> checkbox is added</li>
<li>an Before Header application process writes the token to the <b>Stay logged in </b>cookie and creates an entry for the token and the user name of the current user</li>
</ul>
<div>
<br /></div>
<h2>
Creating the database objects</h2>
<div>
You can download the file to create the tables <a href="https://www.speech2form.com/assets/projects/ttm/blog8/create_aut_objects.sql" target="_blank">here</a>. </div>
<div>
Execute the script in your favorite SQL console. </div>
<div>
In your schema you should see:</div>
<div>
<ul>
<li>the table <b>aut_users</b></li>
<li>the table <b>aut_sessions</b></li>
<li>the package <b>aut_pck</b></li>
</ul>
<div>
The table <b>aut_users </b> contains one record for a user <b>user</b> with a password <b>secret</b>. You can use this data to login to the application. Add your own users in this table.<br />
<i><b>The implementation of the authentication is very basic and just for demonstration purposes. For serious use at least the passwords should be stored encrypted!</b></i><br />
<i><b><br /></b></i></div>
</div>
<h2>
<b>Creating a new authentication scheme</b></h2>
<div>
To obtain autologin functionality we need to create a custom authentication scheme:</div>
<div>
<br /></div>
<div>
<ul>
<li>go to the <b>Shared Components > Security > Authentication Schemes</b></li>
<ul>
<li>press <b>Create</b></li>
<li>chose <b>Based on a pre-configured scheme from the gallery</b></li>
<li>press <b>Next</b></li>
<ul>
<li><b>Name</b>: Custom</li>
<li><b>Scheme Type</b>: Custom</li>
<li><b>Authentication Function Name</b>: aut_pck.authenticate</li>
</ul>
<li>hit <b>Create Authentication Scheme</b></li>
</ul>
</ul>
<div>
After creating the scheme it is automatically the current scheme.<br />
<br /></div>
</div>
<h2>
<b>Changing the login page</b></h2>
<div>
Now we will adapt the login page:</div>
<div>
<ul>
<li>open the login page <b>9999</b></li>
<li>add a new process:</li>
<ul>
<li><b>Name</b>: autologin</li>
<li><b>PL/SQL code</b>:</li>
</ul>
</ul>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">begin</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> aut_pck.autologin</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ( p_app_id => :APP_ID</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , p_page_id => 10</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> );</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">end; </span></div>
</div>
<ul><ul>
<li><b>Executing Options > Point</b>: Before Header</li>
<li><b>Server-side Condition</b>:</li>
<ul>
<li><b>Type</b>: Request != Value</li>
<li><b>Value</b>: LOGOUT</li>
</ul>
</ul>
</ul>
<ul><ul>
</ul>
<li>add a another process:</li>
<ul>
<li><b>Name</b>: autologout</li>
<li><b>PL/SQL code</b>: </li>
</ul>
</ul>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">begin</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> aut_pck.autologout;</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">end; </span></div>
</div>
<ul><ul>
<li><b>Executing Options > </b></li>
<ul>
<li><b>Sequence</b>: 0</li>
<li><b>Point</b>: Before Header</li>
</ul>
<li><b>Server-side Condition</b>:</li>
<ul>
<li><b>Type</b>: Request = Value</li>
<li><b>Value</b>: LOGOUT</li>
</ul>
</ul>
</ul>
<ul>
<li>select the Login Region</li>
<ul>
<li>select the item <b>P9999_REMEMBER</b></li>
<li>Change <b>Label </b>to <b>Stay logged in</b></li>
</ul>
</ul>
<div>
The last step in processing is clearing the page's session state. We need to limit that to the username and password items in order to have the value of the <b>Stay logged in</b> checkbox available on subsequent pages. </div>
<ul>
<li>Go to the processing tab</li>
<ul>
<li>Open the <b>Clear Page(s) Cache</b></li>
<li>In the attributes change <b>Settings</b>:</li>
<ul>
<li><b>Type</b>: Clear Items</li>
<li><b>Item(s)</b>: P9999_USERNAME,P9999_PASSWORD <i>This is done to keep the value of the <b>P9999_REMEMBER </b>in session state</i></li>
</ul>
</ul>
<li>Save the page</li>
<ul><ul><ul>
</ul>
</ul>
</ul>
</ul>
<div>
<b><i><br /></i></b></div>
<h2>
<b>Adding the Application Process</b></h2>
</div>
<div>
We will create an application process that will fire on each page before the header on condition that the user is authenticated and P9999_REMEMBER = 'Y'. In this process the <b>Stay logged in </b>cookie will be written, unless there is a valid cookie.<br />
Go create the application process:<br />
<br />
<ul>
<li>go to the <b>Shared Components > Application Processes</b></li>
<li>click the button <b>Create</b></li>
<li>Enter </li>
<ul>
<li><b>Name</b>: Write autologin cookie</li>
<li><b>Point</b>: On Load: Before Header</li>
<li>Press <b>Next</b></li>
</ul>
<li>Enter the PL/SQL code:</li>
</ul>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">begin</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> -- set autologin cookie</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> aut_pck.set_username_in_cookie</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ( p_username => :APP_USER</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , p_remember => :P9999_REMEMBER</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> );</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> :P9999_REMEMBER := 'N';</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">end;</span></div>
</div>
<ul>
<ul>
<li>Press <b>Next</b></li>
</ul>
<li>Enter the condition:</li>
<ul>
<li><b>Condition Type</b>: PL/SQL Expression</li>
<li><b>Expression 1: </b></li>
</ul>
</ul>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">:</span><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">APP_USER != 'nobody' and</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">:P9999_REMEMBER = 'Y'</span></div>
</div>
<ul>
<li>Press <b>Create Process</b></li>
<ul>
</ul>
</ul>
<div>
After writing the cookie the value of P9999_REMEMBER is set to 'N'. This ensures that the process only fires once after login. </div>
</div>
</div>
<div>
<br /></div>
<h2>
Enabling logging out</h2>
<div>
As all is set up now you will be automatically logged in until the cookie or the <b>aut_session</b> record expires. To give the user the possiblity to end the autologin we will adapt the logout URL. </div>
<div>
<br /></div>
<div>
<ul>
<li>go to <b>Shared Components > Navigation > Navigation Bar List</b></li>
<li>select <b>Desktop Navigation Bar</b></li>
<li>click on S<b>ign Out </b></li>
<li>change <b>Target</b>:</li>
<ul>
<li><b>Target Type</b>: Page in this Application</li>
<li><b>Page</b>: 9999</li>
<li><b>Request</b>: LOGOUT</li>
</ul>
<li>press <b>Apply Changes</b></li>
</ul>
<div>
Now chosing <b>Sign Out</b> will result in navigating to the login page with the request <b>LOGOUT</b>.</div>
</div>
<div>
Previously we have created a logout process on page 9999 which is triggered by the request <b>LOGOUT</b>. This process erases the cookie and removes the corresponding record from <b>aut_sessions </b>thus disabling the autologin. </div>
<div>
<br />
<h2>
Testing the autologin functionality</h2>
</div>
<div>
Now you can test the functionality by logging in with the <b>Stay logged in</b> item checked. </div>
<div>
You can test the autologin by changing the session ID in the URL. Normally the session would be recognized and you would be returned to the login page. Now you just stay logged in. </div>
<div>
<br /></div>
<div>
Behind the screen you can check on the existence of the cookie <b>STAY_LOGGED_IN_xxx</b>, where <b>xxx</b> is the application number using developer tools like the Chrome Inspector. Likewise you can inspect to content of the table <b>aut_sessions</b>, where a record should exist with the token stored in the cookie and the name of the user. </div>
<div>
<br /></div>
<div>
Test the <b>Sign out</b> functionality. You should be returned to the login page. </div>
<div>
<br /></div>
Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com10tag:blogger.com,1999:blog-4312362131290962824.post-70222950225284233422019-07-21T10:54:00.000+02:002019-07-21T10:54:01.667+02:00Creating a mobile app with APEX - Part 7: Working with Time itemsIn part 5 of the Mobile App series the Form page was refined.<br />
On this page there are two items containing a time value. This post will cover how to deal with these time values.<br />
<br />
Time values are stored in the database as time fraction of a Date column. The time values in TTM are stored in the column <b>act_start_date</b> and <b>act_end_date</b>.<br />
<br />
Three aspects will be discussed:<br />
<ul>
<li>retrieving time values</li>
<li>processing time input</li>
<li>entering time values</li>
<li>special Time Input Control</li>
</ul>
<h2>
Retrieving Time values</h2>
<div>
Oracle APEX does not have an item to display time values, like the Date picker does for date values. </div>
<div>
So we will have to display the formatted time in a Text Item. </div>
<div>
And the time values are not available directly in the table. So we will need a view to expose the time values from the Date columns:</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">create or replace force view ttm_activities_vw as </span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">select act.act_id</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , trunc(act_start_datetime) as act_start_date</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , act.act_prj_id</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , prj.prj_name as act_prj_name</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , ttm_alg.date2time(act_start_datetime) as act_start_time</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , ttm_alg.date2time(act_end_datetime) as act_end_time</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , act_description</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , act_location</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">from ttm_activities act</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> join ttm_projects prj</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> on prj.prj_id = act.act_prj_id;</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
<div>
This view (which is already created) exposes the start and end time in the format <b>hh24:mi</b>.<br />
<i>You can find the source of the package ttm_alg <a href="https://www.speech2form.com/assets/projects/ttm/blog5/ttm_alg.sql" target="_blank">here</a>.</i></div>
<div>
<br /></div>
<h2>
Processing time input</h2>
The view of the last paragraph is the base table for the APEX form page.<br />
In order for this to function an Instead of trigger needs to be defined, performing the insert/update/delete action. Below the code of the trigger:<br />
<div>
<br /></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">create or replace trigger act_io</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> instead of insert or update or delete</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> on ttm_activities_vw</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> for each row</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">begin</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if inserting then</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> insert into ttm_activities</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ( act_prj_id</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , act_start_datetime</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , act_end_datetime</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , act_description</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , act_location</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> )</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> values</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ( :new.act_prj_id</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , <b>ttm_alg.time2date(:new.act_start_date,:new.act_start_time)</b></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , <b>ttm_alg.time2date(:new.act_start_date,:new.act_end_time)</b></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , :new.act_description</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , :new.act_location</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> );</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> elsif updating then</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> update ttm_activities</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> set act_prj_id = :new.act_prj_id</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , act_start_datetime = </span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><b> ttm_alg.time2date(:new.act_start_date,:new.act_start_time) </b></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , act_end_datetime = </span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><b> ttm_alg.time2date(:new.act_start_date,:new.act_end_time)</b></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , act_description = :new.act_description</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , act_location = :new.act_location</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> where act_id = :new.act_id</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ; </span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> elsif deleting then</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> delete ttm_activities</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> where act_id = :old.act_id</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ;</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> end if;</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">end;</span></div>
</div>
<div>
<br /></div>
<div>
As you see the code is much of a one-on-one conversion from view to table except for the time values. The bold code converts the time values into dates with time fraction. </div>
<div>
<br />
With this trigger insert, update and delete can be performed on the view and therefore the view can be source of an APEX form. So all logic is performed in the database. </div>
<div>
<br /></div>
<h2>
Entering time values</h2>
<div>
The time values are displayed in APEX using a Text Item.</div>
<div>
The time values can be entered using the format <b>hh24:mi</b> for example <b>9:15</b>. </div>
<div>
There is no need to add preceding zeros, so <b>9:05</b> can be entered as <b>9:5</b>. </div>
<div>
Also for the hours the minutes can be totally omitted, so <b>9:00 </b>can be entered as <b>9</b>.</div>
<div>
<br /></div>
<div>
Time values can also be entered with touch gestures using the Touch Time Input control. </div>
<h2>
Special Time Input control</h2>
<div>
Back in 2009, when I had my first Android phone, I wanted to do my time registration using the phone. Thinking about this I got the idea for a graphical time input control where a clock image is displayed on the phone and the user would use the touch screen to draw the hands on the clock, thus entering the time.<br />
<br /></div>
<div>
It would take some time before I would create this control. In february 2015 I wrote a <a href="https://dickdral.blogspot.com/2015/02/a-ahover-avisited-text-decoration-none.html">blogpost</a> about my Time Input Control, and in 2017 I published the Touch Time Input APEX plug-in on <b>apex.world:</b></div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGtr7kjXgB5ClrfqW7hPCHfx0mMc7yTVhV_XmI3EjY_Uu861Bd-Iv41yZ-YJyAL5HWzurAa0YsXN6XszZa66_F99GVaZoen4uar3ZPE2tkYwRU7J-FZlx2MvvwI6EJn_tBT5_At2SKiCw5/s1600/Schermafbeelding+2015-02-12+om+11.41.36.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="788" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGtr7kjXgB5ClrfqW7hPCHfx0mMc7yTVhV_XmI3EjY_Uu861Bd-Iv41yZ-YJyAL5HWzurAa0YsXN6XszZa66_F99GVaZoen4uar3ZPE2tkYwRU7J-FZlx2MvvwI6EJn_tBT5_At2SKiCw5/s320/Schermafbeelding+2015-02-12+om+11.41.36.png" width="280" /></a></div>
<div>
</div>
<div>
<br />
<br />
Recently I published a new version of the plug-in with support for floating labels. It is also easier to implement as the time picker icon is created by the plugin. The changes are described in this <a href="https://dickdral.blogspot.com/2019/07/touch-time-input-new-version.html" target="_blank">blogpost</a>.<br />
<br />
We will add a time picker buttons to the time items and connect the Time Input DA to these buttons:<br />
<br />
<ul>
<li>Open <b>Page 15</b></li>
<ul>
<li>Goto the tab <b>Dynamic Actions</b></li>
<li>Right click on <b>Page Load</b> and chose <b>Create Dynamic Action</b></li>
<li>Select the new dynamic action and set the <b>Name </b>to <b>Page Load</b></li>
<li>Select the action and set <b>Identification > Action </b> to <b>Touch Time Input V2 [Plug-In]</b></li>
</ul>
</ul>
<div>
<br /></div>
<div>
This will take care that all items with the class <b>has-time-picker</b> will be added with a time picker icon. Now we will mark the time items:</div>
<div>
<br /></div>
<div>
<ul>
<li>On <b>Page 15 </b>open the region <b>Activity</b></li>
<li>Select the item <b>P15_ACT_START_TIME</b></li>
<li>Set the value of <b>Advanced > CSS classes </b>to <b>has-time-picker</b></li>
<li>Repeat for item <b>P15_ACT_END_TIME</b></li>
</ul>
</div>
</div>
<ul><ul>
</ul>
</ul>
<div>
That's it!<br />
<br />
<br />
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com0tag:blogger.com,1999:blog-4312362131290962824.post-27770148035770385762019-07-17T12:33:00.001+02:002019-07-17T12:35:35.908+02:00Touch Time Input - New versionIn the cause of writing my blog series on mobile development I needed to adapt the Touch Time Input plug-in for the floating labels in APEX 18.1.<br />
So I created a new version for APEX 18.1 and up.<br />
This version:<br />
<br />
<ul>
<li>supports APEX 18.1 floating labels</li>
<li>is called from a Page Load Dynamic Action</li>
<li>applies to all items with the class <b>has-time-picker</b> </li>
<li>some bugfixes</li>
</ul>
<div>
This makes the plug-in even easier to use as the marked items automagically receive a clock icon analog to the date picker:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgw9yfEw6f2zEoBhRl30yWP8L-eGJaFdCdP0TyoFeW6nWOdiX4SBGaKNHWSWvk5fZ2IIMs1KOz3nT-BkSvlQ4vntPYzgodLbfLLm9cckeQpbJVU4XxA79mdJ1Kgs3X2pq2fvcfmzLwf_okk/s1600/apex_touch_time_input_v2_example.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="550" data-original-width="309" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgw9yfEw6f2zEoBhRl30yWP8L-eGJaFdCdP0TyoFeW6nWOdiX4SBGaKNHWSWvk5fZ2IIMs1KOz3nT-BkSvlQ4vntPYzgodLbfLLm9cckeQpbJVU4XxA79mdJ1Kgs3X2pq2fvcfmzLwf_okk/s320/apex_touch_time_input_v2_example.gif" width="179" /></a></div>
<div>
<br /></div>
<div>
<br />
You can download the plugin from <a href="http://apex.world/" target="_blank">apex.world</a>.<br />
<br /></div>
<div>
<i>For pre 18.1 APEX versions the old version of the plug-in can still be used.</i></div>
<br />
<br />
Happy APEXing :-)<br />
<br />Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com0tag:blogger.com,1999:blog-4312362131290962824.post-91448747188582761742019-07-11T12:49:00.000+02:002019-07-28T12:24:10.835+02:00Creating a mobile app with APEX - Part 5: Refining the Form pageAfter refining the List View now the form will be improved for mobile use:<br />
<br />
<ul>
<li>screen space usage will be optimized</li>
<li>the buttons will be reorganized</li>
<li>default values will be provided</li>
<li>success messages will be made to disappear after a few seconds</li>
<li>validations for time items are created</li>
</ul>
<div>
<br /></div>
<h2>
Bugfix for trigger</h2>
<div>
There was an error in the original trigger on the view. Download a new create script <a href="https://www.speech2form.com/assets/projects/ttm/blog5/create_trigger.sql">here</a> and run the script to replace the trigger.<br />
<br /></div>
<h2>
Screen space optimization</h2>
<div>
The previous actions to improve the use of screen space also have effect on the form page. The only thing that remains to be done here is to hide the region title:</div>
<div>
<ul>
<li>Open region <b>Activity</b></li>
<li>Hide header by settting <b>Appearance > Template Defaults</b> <b>> Header</b> to <b>Hidden</b></li>
</ul>
</div>
<br />
<h2>
Buttons</h2>
<div>
For this step a new version of the CSS file is needed. Download <a href="https://www.speech2form.com/assets/projects/ttm/blog5/apex_mobile.css" target="_blank">apex_mobile.css</a> and upload to the Application Static Files.<br />
<br />
Below the form are three buttons, the Cancel, Delete and Create/Apply Changes button. </div>
<div>
The Cancel button is not needed because the List View can be reached using the Menu Bar. So this button can be Deleted.</div>
<div>
<ul>
<li>delete the <b>Cancel</b> button</li>
</ul>
</div>
<div>
The Create/Apply Changes button will be replaced by the Save action in the Menu Bar. <i>The CREATE and SAVE buttons are mutually exclusive. The CREATE button is shown for a new rows, the SAVE button for existing rows. </i><br />
The connect the Menu Bar Save action to the buttons a CSS <b>save_button</b> class is applied:</div>
<div>
<ul>
<li>select the <b>SAVE</b> button</li>
<li>set <b>Appearance > CSS Classes </b>to <b>save_button</b></li>
<li>select the <b>CREATE </b>button</li>
<li>set <b>Appearance > CSS Classes </b>to <b>save_button</b></li>
</ul>
</div>
<div>
The action on Menu Option <b>Save </b>is defined as: <span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">javascript:$('.save_button').click();</span></div>
<div>
This means that the element with class <b>save_button</b> is selected and the click action associated with this element is performed. This way either the SAVE or the CREATE button action is executed depending on which one is present on the page. </div>
<div>
<br /></div>
<div>
So only the Delete button remains. This buttons will be styled in a more mobile fashion:</div>
<div>
<ul>
<li>select <b>DELETE</b> button</li>
<li>change <b>Layout > Button Position</b> to <b>Below Region</b></li>
<li>set <b>Appearance > Template Options</b>:</li>
<ul>
<li><b>Size</b>: Large</li>
<li><b>Type</b>: Danger <i>White font on red background</i></li>
<li><b>Width</b>: Stretch <i>Makes button use full width</i></li>
</ul>
<li>set <b>Appearance > CSS Classes</b>: delete_button</li>
</ul>
<div>
Run the application and notice the changes. Save a new or existing activity using the Menu Bar <b>Save</b> option. </div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYpapQaRQb3whbvaf9vHqqfc-IbY143wptbRzSt9621eCs9I5VCjJkdwQqB6OdE9cQoUI6gMwJoLsfqUw3DBA7uLekqhbxp1BT9Njka3KQqT5rqh0phr5j-sJBGCPG8S_lTLEm7HhOttK0/s1600/ttm+after+blog+5+buttons.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1118" data-original-width="626" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYpapQaRQb3whbvaf9vHqqfc-IbY143wptbRzSt9621eCs9I5VCjJkdwQqB6OdE9cQoUI6gMwJoLsfqUw3DBA7uLekqhbxp1BT9Njka3KQqT5rqh0phr5j-sJBGCPG8S_lTLEm7HhOttK0/s320/ttm+after+blog+5+buttons.png" width="179" /></a></div>
<div>
<br /></div>
<h2>
Default values</h2>
<div>
Entering data on the on-screen keyboard is not easy or fast, so it is an advantage when data is prefilled. We will provide some defaults for the items.<br />
<br />
We will use functions from the package <b>ttm_alg</b>. For this functionality a new version of the package <b>ttm_alg </b>is needed. Download the create file <b><a href="https://www.speech2form.com/assets/projects/ttm/blog5/ttm_alg.sql">ttm_alg.sql</a> </b>and execute this file on the schema.<br />
<br /></div>
<div>
The first one is the date. The obvious default for this is the current date:</div>
<div>
<ul>
<li>open <b>P15_ACT_START_DATE</b></li>
<li>in the <b>Default</b> section:</li>
<ul>
<li><b>Type </b>: PL/SQL Expression</li>
<li><b>PL/SQL Expression</b>: sysdate</li>
</ul>
</ul>
<div>
The Project of the last entered Activity can serve as default value for Project:</div>
</div>
<div>
<ul>
<li>open <b>P15_ACT_PRJ_ID</b></li>
<li>in the <b>Default</b> section:</li>
<ul>
<li><b>Type </b>: PL/SQL Expression</li>
<li><b>PL/SQL Function Body</b>: ttm_alg.last_used_project</li>
</ul>
</ul>
<div>
The Start time can be filled with the last End time of today, or a default value like <b>9:00</b> if this is your usual starting time. </div>
</div>
<div>
<br /></div>
<div>
<ul>
<li>open <b>P15_ACT_START_TIME</b></li>
<li>in the <b>Default</b> section:</li>
<ul>
<li><b>Type </b>: PL/SQL Expression</li>
<li><b>PL/SQL Function Body</b>: ttm_alg.default_start_time</li>
</ul>
</ul>
<div>
Run the application and enter a new activity. </div>
</div>
<div>
<br /></div>
<div>
<i>We will review the possibility to use the GPS location to determine a default value for Location in a coming post. </i></div>
<div>
<i><br /></i></div>
<h2>
Success message</h2>
<div>
Standard the APEX success messages that are displayed at the top of the page must be removed manually. On mobile this is not logical. The success message indicates that there were no errors so after a short time the message can disappear. </div>
<div>
For this function a new version of <b><a href="https://www.speech2form.com/assets/projects/ttm/blog5/apex_mobile.js" target="_blank">apex_mobile.js</a></b> must be uploaded to the Application Static Files. After this:<br />
<br />
<ul>
<li>open Page 10</li>
<li>add to <b>JavaScript > Execute when Page loads</b>: <span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">set_success_message_fade();</span></li>
</ul>
<br />
Now if an activity is created or changed the success message will show for 2 seconds and then disappear.<br />
<br /></div>
<h2>
Checking input</h2>
<div>
Of course the user input has to be checked.<br />
<br />
The date value will be checked automatically.<br />
Because the project is implemented as a select list it need not be checked.<br />
The Description and Location are character items and need not be checked.<br />
<br />
That leaves us both the Time items. They should be filled with a string in the form <b>hh24:mi</b>. Furthermore the End Time should be after the Start Time.<br />
<br />
After this the validations can be created:<br />
<ul>
<li>Open the Processing column on page 15</li>
<li>Right click on <b>Validating > Validations</b> and select <b>Create Validation</b></li>
<li>Change the <b>Validation</b>:</li>
<ul>
<li><b>Name</b>: Check start time</li>
<li><b>Validation > Type</b>: PL/SQL Function (returning Error Text)</li>
<li><b>Validation > PL/SQL Function returning Error Text: </b></li>
<ul>
<li><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">return(ttm_alg.check_time(:P15_ACT_START_TIME,'Start time'));</span></li>
</ul>
</ul>
<li>Right click again on <b>Validating > Validations</b> and select <b>Create Validation</b></li>
<li>Change the <b>Validation</b>:</li>
<ul>
<li><b>Name</b>: Check end time</li>
<li><b>Validation > Type</b>: PL/SQL Function (returning Error Text)</li>
<li><b>Validation > PL/SQL Function returning Error Text: </b></li>
<ul>
<li><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">return(ttm_alg.check_time(:P15_ACT_END_TIME,'Start time'));</span></li>
</ul>
</ul>
<li>Right click another time on <b>Validating > Validations</b> and select <b>Create Validation</b></li>
<ul>
<li><b>Name</b>: Check Start Time before End Time</li>
<li><b>Validation > Type</b>: PL/SQL Function (returning Error Text)</li>
<li><b>Validation > PL/SQL Function returning Error Text: </b></li>
</ul>
</ul>
</div>
<div>
<div>
</div>
</div>
<div>
<textarea style="font-family: "courier"; font-size: 12px; height: 142px; margin: 0px; width: 520px;">begin
if ttm_alg.time2date(sysdate,:P15_ACT_START_TIME)
> ttm_alg.time2date(sysdate,:P15_ACT_END_TIME) then
return('Start time "'||:P15_ACT_START_TIME
||'" must be before End time "'||:P15_ACT_END_TIME||'".');
end if;
return(null);
exception
when others then null;
end;
</textarea>
<br />
<br />
Now the format of the time items and the relation between start and end time are checked. If an invalid value is entered on a meaningful error message is displayed.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpJF3GiKD3HG0W0VbcfRjKirbWzkG3hMf-Yf_LCSUkBSMTiK4Uvq0rfgAQrIOzLmSkAQ-4nrN90TT_SeqnuKUq4NIQTtanWbp6HDY1MsIoIbUZzFyJCIkr5FyU8gmDye49K_RnDS4mJQQI/s1600/ttm+blog+5+Error+message.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1120" data-original-width="630" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpJF3GiKD3HG0W0VbcfRjKirbWzkG3hMf-Yf_LCSUkBSMTiK4Uvq0rfgAQrIOzLmSkAQ-4nrN90TT_SeqnuKUq4NIQTtanWbp6HDY1MsIoIbUZzFyJCIkr5FyU8gmDye49K_RnDS4mJQQI/s320/ttm+blog+5+Error+message.png" width="180" /></a></div>
<br />
<br /></div>
Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com3tag:blogger.com,1999:blog-4312362131290962824.post-2521325452258314632019-07-10T15:35:00.005+02:002019-08-05T10:41:21.139+02:00Creating a mobile app with APEX - Part 6: Avoid autmatic zooming on iOSIf you use an iPhone to access our application you probably will have noticed the automatic zooming.<br />
If the focus is set to an item, Safari (and Chrome also) will zoom in to this item. After zooming not all of the page is visible any more. When for example the date item is selected after zooming the date picker icon shifts out of sight. So it is desirable to avoid the auto zooming.<br />
<br />
After some googling it is clear that the autozoom is applied to items with a font size less than 16px.<br />
<br />
The easiest solution to prevent this zooming is to size up the font size of all items to 16px. We can attain this by applying the following CSS:<br />
<br />
input, select, textarea {<br />
font-size: 16px!important;<br />
}<br />
<br />
Now all font size 16px is applied to all items. You can see the differences in the images below.<br />
Left is the page with autozoom, right is the page with the fix applied.<br />
<br />
<table><tbody>
<tr>
<td><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHWoT8A0YoOek64w1xEpRwkdWQTr5Iso5MuwLVcEG8M4DdQSSgjPzP4IB6_5ueF9HomyDlf__OJD7neHMKeSh5l4EGVoUOvjYZg8zs7ERAR_HPhyn1tIXWfwPkykD8s7ya0jKMdDKvdmJ-/s1600/autozoom.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1600" data-original-width="900" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHWoT8A0YoOek64w1xEpRwkdWQTr5Iso5MuwLVcEG8M4DdQSSgjPzP4IB6_5ueF9HomyDlf__OJD7neHMKeSh5l4EGVoUOvjYZg8zs7ERAR_HPhyn1tIXWfwPkykD8s7ya0jKMdDKvdmJ-/s320/autozoom.PNG" width="180" /></a></div>
</td>
<td><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh4OgpFefxkdw4ryeDQOCrzm0YKmqVACczUzcQHR7qrrX6p6lsruyTRyB_X53UfbcAgBzJ_IPU4GSJB6SBbB68K_wpbtNxQIhbTILOwUEhF7MfPy_0nXZSVzAi9aM2LhBnnn9F1DEMPBBk/s1600/no+autozoom.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1600" data-original-width="900" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh4OgpFefxkdw4ryeDQOCrzm0YKmqVACczUzcQHR7qrrX6p6lsruyTRyB_X53UfbcAgBzJ_IPU4GSJB6SBbB68K_wpbtNxQIhbTILOwUEhF7MfPy_0nXZSVzAi9aM2LhBnnn9F1DEMPBBk/s320/no+autozoom.PNG" width="180" /></a></div>
</td>
</tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
As you can see after autozoom the page is partly invisible. The user needs to pinch to view the full page.<br />
<br />
<i>The <b>!important</b> postfix in the CSS is a bit blunt. It is used to avoid complex and extensive CSS specifications with references to Universal Theme classes.</i><br />
<br />
<h2>
Solution specific for iOS</h2>
Now putting this CSS on the page just like that it will affect the application for all platforms.<br />
A more elegant solution is to apply this CSS only on iOS. That can be done by creating a region on the global page 0 and creating a PL/SQL Dynamic Region that only writes the style specification for iOS:<br />
<br />
<ul>
<li>open <b>Page 0</b></li>
<li>right click on <b>Content body</b> and select <b>Create Region</b></li>
<li>select the new region</li>
<ul>
<li><b>Name</b>: Prevent Autozoom</li>
<li><b>Type</b>: PL/SQL Dynamic Content</li>
<li><b>Template</b>: - Select -</li>
<li><b>PL/SQL Code</b>:</li>
</ul>
</ul>
<br />
<pre>
declare
l_agent varchar2(1000);
begin
l_agent := lower(owa_util.get_cgi_env('user-agent'));
if l_agent like '%iphone os%' then
htp.p('<style>input,select,textarea {font-size:16px!important;}</style>');
end if;
end;
</pre>
With this solution the CSS is only applied to iOS. The font sizes on other platforms are unaffected.<br />
<br />
<i>The <b>user-agent</b> CGI variable contains information about the browser and platform used. When the string <b>iphone os</b> is present in the lower case user-agent string the page is displayed on iOS. </i><br />
<br />
<h2>
Alternative solution</h2>
There is another smart, but rather complex solution:<br />
<br />
<ul>
<li>setting the font size to 16px</li>
<li>sizing the item relatively to the change in font size</li>
<li>use CSS scaling to resize the items back to the original size</li>
</ul>
<div>
This solution is described in the blog <a href="https://thingsthemselves.com/no-input-zoom-in-safari-on-iphone-the-pixel-perfect-way/">No input zoom in Safari on iPhone, the pixel perfect way</a> by Jeffry To. </div>
<div>
<br /></div>
The first solution is sufficient for our purpose, in mobile APEX applications the layout usually remains good enough.<br />
<br />
<br />
<br />Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com0tag:blogger.com,1999:blog-4312362131290962824.post-5197843357525160612019-07-09T09:01:00.000+02:002019-07-29T12:05:58.528+02:00Creating a mobile app with APEX - Part 4: Refining the List View<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
After having taken care of the general layout this post will focus on the refining of the List View page. The following actions will be performed:<br />
<br />
<ul>
<li>loading a new version of the CSS file</li>
<li>adding a date picker to filter the activities</li>
<li>changing the List View query to react on the date picker</li>
<li>adding a Dynamic Action to synchronize the List View</li>
<li>optimizing screen space</li>
<li>removing the create button</li>
<li>applying advanced formatting on the List View elements</li>
</ul>
<div>
<br /></div>
<h2>
Loading new version CSS</h2>
<div>
A new version of the <b>apex_mobile.css</b> file should be downloaded <a href="https://www.speech2form.com/assets/projects/ttm/blog4/apex_mobile.css">here</a> and uploaded to the <b>Application Static Files</b>.</div>
<div>
<br /></div>
<h2>
Adding the date picker</h2>
<div>
We will create a new region and add a date picker item. The background color of the date picker region will be green. </div>
<div>
<ul>
<li>open Page 10</li>
<li>right click on <b>Content Body</b> and select <b>Create Region</b></li>
<li>change the region:</li>
<ul>
<li><b>Name</b>: Parameters</li>
<li><b>Appearance > Template Options</b>:</li>
<ul>
<li>check <b>Remove Body Padding</b></li>
<li>set <b>Header</b> to <b>Hidden</b></li>
</ul>
<li><b>Advanced > Static ID</b>: parameters <i><span style="font-size: x-small;">this will style the region</span></i></li>
</ul>
<li>drag this region above the Activities region</li>
<li>right click on the region <b>Parameters </b>and chose <b>Create Page Item</b></li>
<li>make the following changes to the new item:</li>
<ul>
<li><b>Name</b>: P10_DATE</li>
<li><b>Type</b>:<b> </b>Date Picker</li>
<li><b>Label</b>: Date in week</li>
<li><b>Default</b>:</li>
<ul>
<li><b>Type: </b>PL/SQL Expression</li>
<li><b>PL/SQL Expression</b>: sysdate</li>
</ul>
<li><b>Warn on unsaved changes</b>: Ignore <i><span style="font-size: x-small;">Prevent warning when leaving page</span></i></li>
<ul>
</ul>
</ul>
</ul>
<h2>
Changing the List View query</h2>
</div>
<div>
The source query of the List View should be changed to reflect the values of the date item. </div>
<div>
Add the following where-clause to the query:</div>
<div>
<br /></div>
<br />
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">from ttm_activities_vw act</span></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><b>where trunc(act_start_date,'iw') = trunc(to_date(:P10_DATE,'dd-mm-yyyy'),'iw')</b></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">order by trunc(act_start_date) desc, act_start_time</span></div>
<div>
<br /></div>
</div>
With this where clause only activities in the same week as P10_DATE are shown.<br />
<br />
<h2>
Dynamic Action to synchronize List View</h2>
When at this point the date is changed there is no effect on the List View.<br />
We will need to define this behavior with a dynamic action:<br />
<br />
<ul>
<li>Right click on the item <b>P10_DATE</b></li>
<li>Pick <b>Create Dynamic Action</b></li>
<li>Change the <b>Name</b> of the DA to <b>P10_DATE change</b></li>
<li>Change the first <b>True</b> Step:</li>
<ul>
<li><b>Action</b>: Execute PL/SQL Code</li>
<li><b>PL/SQL Code</b>: null;</li>
<li><b>Items to Submit</b>: P10_DATE <i>this is needed to load the value into session state</i></li>
</ul>
<li>Right click on this step and chose <b>Create Action</b></li>
<li>Change the new action:</li>
<ul>
<li><b>Action</b>: Refresh</li>
<li><b>Affected Elements > Selection Type</b>: Region</li>
<li><b>Affected Elements > Region</b>: Activities</li>
</ul>
</ul>
<div>
Now a change of the date item results in a requery of the <b>Activities</b> region.</div>
<div>
<br /></div>
<h2>
Optimizing screen space</h2>
<div>
There still is some screen space that is not used effectively. We do not need the region title as it shows in the Top Bar. The <b>Create</b> button can be deleted as this function is provided in the Menu Bar. And there still is excess of white space...</div>
<div>
So let's get going:</div>
<div>
<ul>
<li>Open the region <b>Activities</b>:</li>
<ul>
<li>Edit <b>Template Defaults</b>:</li>
<ul>
<li>Check <b>Remove Body Padding</b></li>
<li>Set <b>Header</b> to <b>Hidden</b></li>
<li>Set <b>Style </b>to <b>Remove borders</b></li>
</ul>
<li>Edit the region's <b>Attributes</b></li>
<ul>
<li>Uncheck <b>Settings > Inset List</b> </li>
</ul>
</ul>
<li>Delete the <b>Create </b>button</li>
</ul>
<div>
You can clearly see the difference between the page now and before:</div>
<div>
<br /></div>
<table><tbody>
<tr>
<td align="center"><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPbOVmkmq07a4lrJ5tMzh7TDGk5CtyQFqSb-WOBkJakZrArlC497NavYdum5oODoWCOfCwQ3S89sM0RQcEo7L8RhLloynhRlpC-N7-DpK9M6DYRx4WMSQKSM76WqnllrQZSCkHCrJq8iLJ/s1600/TTM+List+View+after+styling.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1116" data-original-width="631" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPbOVmkmq07a4lrJ5tMzh7TDGk5CtyQFqSb-WOBkJakZrArlC497NavYdum5oODoWCOfCwQ3S89sM0RQcEo7L8RhLloynhRlpC-N7-DpK9M6DYRx4WMSQKSM76WqnllrQZSCkHCrJq8iLJ/s320/TTM+List+View+after+styling.png" width="180" /></a></div>
<br /></td>
<td align="center"><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVUSZ5yaZXNjUs50w-PElBGOlavJOXcA-ffxzdny4OxFzzDq3bPMnzJCLGHlVRY9Ed2UJRP_ojwcdC6TETQiAn71CFBAZOnxVa1v6WFy2C-MuDC1f0wkdxOdEsQ7W3ybo8lWYltCrKyQEm/s1600/TTM+List+View+after+reclaiming+space.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1122" data-original-width="630" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVUSZ5yaZXNjUs50w-PElBGOlavJOXcA-ffxzdny4OxFzzDq3bPMnzJCLGHlVRY9Ed2UJRP_ojwcdC6TETQiAn71CFBAZOnxVa1v6WFy2C-MuDC1f0wkdxOdEsQ7W3ybo8lWYltCrKyQEm/s320/TTM+List+View+after+reclaiming+space.png" width="179" /></a></div>
<br /></td>
</tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: center;">
</div>
Even though the date item has been added the vertical space taken up by the List View is less than before.<br />
<br />
<h2>
Advanced formatting</h2>
<div>
In the design in Part 1 of this series we have defined that the List View elements should have a colored bar left of the text. </div>
<div>
<br /></div>
<div>
Also the Location should be part of the information shown, behind the Project name. But location is not as important as the Project, so we need to decrease the emphasis on Location, or increase it on Project. </div>
<div>
Increasing emphasis can be done by using <b>Bold</b> text, decreasing can be accomplished by a lighter font color, usually a grey tone or a smaller font size.</div>
To apply this on one line we need to use Advanced Formatting within List View.<br />
<br />
The base query for the List View has to change because we need separate elements which were combined in the previous view:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">select act.act_id</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , to_char(act_start_date,'fmDay, fmMonth fmddth yyyy ') </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> as start_date</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , act_prj_name as project_name</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , case act_prj_id</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> when 1 then 'cornflowerblue'</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> when 2 then 'green'</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> when 3 then 'yellow'</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> end as project_color</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , act_location as location</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , act_start_time</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> || ' - ' ||act_end_time as period</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , act_description as description</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">from ttm_activities_vw act</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">where trunc(act_start_date,'iw') = trunc(to_date(:P10_DATE,'dd-mm-yyyy'),'iw')</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">order by trunc(act_start_date) desc, act_start_time</span><br />
<div>
<br /></div>
To apply the advanced formatting:<br />
<br />
<ul>
<li>Open the <b>Activities </b>region</li>
<li>Apply the new query</li>
<li>Go to <b>Attributes</b></li>
<li>In <b>Settings</b>:</li>
<ul>
<li>check <b>Advanced Formatting </b><i>You see that the available items change</i></li>
<li>Fill in the values from the table below:</li>
</ul>
</ul>
</div>
<table border="1">
<tbody>
<tr><td><b>List Entry Attributes</b></td><td><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">s</span><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">tyle="margin-left:15px; border-left:5px solid &PROJECT_COLOR."</span></td></tr>
<tr><td><b>Text Formatting</b></td><td><textarea style="height: 36px; margin: 0px; width: 492px;"><span>&PROJECT_NAME.</span>
<span style="color:#999999; font-size:12px;">&LOCATION.</span>
</textarea></td></tr>
<tr><td><b><b>Supplemental Information Formatting</b></b></td><td><textarea style="height: 54px; margin: 0px; width: 492px;"><span style="font-size:13px;color:rgba(0,0,0,.75);">&PERIOD.</span>
<span>&DESCRIPTION.</span>
</textarea></td></tr>
</tbody></table>
<br />
<br />
The columns from the query are referenced by name preceeded by an ampersand and with a trailing dot, like <b>&PROJECT_NAME.</b>. <i>For clarity the styling is done in-line, normally you would CSS classes. </i><br />
When running the application you can notice the difference between the various parts of the List View elements.<br />
The colored bar is created by the <b>List Entry Attributes</b>. Note that you can also reference query columns here.<br />
<br />
The List Divider has been styled. In order to provide more contrast with the elements it has received a white font on a dark grey background. This is done by applying CSS in the <b>apex_mobile.css </b>file:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">li.a-ListView-divider.ui-bar-inherit {</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> background-color: rgba(0,0,0,.55);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> color: white;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">}</span><br />
<br />
<i>The Universal Theme classes are used to reference the List Divider. </i><br />
<br />
The List View page now looks and functions according to the specifications:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjW-ixbFH3kPS_ptvTCdPV97knFBq_03QLr4s1l2Ag3eP1BJew3rJ5nrTN_B6di6lRVV7BtN58DXNfO400llVa7PkQJLpLo5moEUSk-O_PCfY5QmTKGZZ-YRy5uJko9G0y1B8PQlCZ0oarn/s1600/TTM+List+View+page+after+styling+the+elements.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1120" data-original-width="634" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjW-ixbFH3kPS_ptvTCdPV97knFBq_03QLr4s1l2Ag3eP1BJew3rJ5nrTN_B6di6lRVV7BtN58DXNfO400llVa7PkQJLpLo5moEUSk-O_PCfY5QmTKGZZ-YRy5uJko9G0y1B8PQlCZ0oarn/s320/TTM+List+View+page+after+styling+the+elements.png" width="181" /></a></div>
<br />
<br />
In the next Episode we will refine the Form page.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div>
<br /></div>
<div>
<br /></div>
Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com1tag:blogger.com,1999:blog-4312362131290962824.post-68191395803965181112019-07-07T21:42:00.003+02:002019-07-08T19:08:11.725+02:00Creating a mobile app with APEX - Part 3: Adapting the layoutIn this post the general layout of the application will be tweaked:<br />
<br />
<ol>
<li>Another Theme Style will be chosen</li>
<li>The landing page for the application is changed</li>
<li>The menu will be replaced by a top/bottom menu bar. The menu will be adapted to the pages</li>
<li>The List View page is set as landing page</li>
<li>Unused space will be reclaimed</li>
<li>The page title will be shown in the top bar</li>
</ol>
<h2>
Theme Style</h2>
<br />
We will select another Theme Style for the application:<br />
<br />
<ul>
<li>Run the application from the <b>App Builder</b></li>
<li>Select <b>Theme Roller</b> from the <b>Developer Toolbar</b></li>
<li>Select <b>Vita - Slate</b> for the <b>Style</b></li>
<li>Press the button <b>Set as Current</b></li>
</ul>
<br />
Now we have other (darker) colors in our application. The dark grey top bar will enable us to use other colors as background in the application. <i>The background color for the date picker region in the List View will be set in the post 'Refining List View'.</i><br />
<h2>
Landing page</h2>
The default landing page is Page 1. We created our Activity List on Page 10 and this should be the new landing page:<br />
<br />
<ul>
<li>Open the <b>Shared Components > User Interface Attributes > User Interface > Desktop</b></li>
<li>In the tab <b>Attributes</b>:</li>
<ul>
<li>Set <b>Home URL </b>to <b>f?p=&APP_ID.:10:&SESSION.</b></li>
</ul>
</ul>
Run the application and you are taken directly to page 10.<br />
<h2>
Menu</h2>
The default left menu is not so very useful for an smartphone app with limited pages. Therefore we will use a top/bottom menu bar:<br />
<br />
<ul>
<li>Open the <b>Shared Components > User Interface Attributes > User Interface > Desktop</b></li>
<li>In the tab <b>Navigation Menu</b>:</li>
<ul>
<li>Set <b>Position </b>to <b>Top</b></li>
<li>Select <b>Top Navigation Tabs</b> for <b>List Template</b></li>
<li>In <b>Template Options</b> set <b>Mobile </b>to <b>Do not display labels</b></li>
</ul>
<li><b><br /></b></li>
</ul>
<div>
With these changes the Left Menu is replaced by a menu bar. This bar is shown just below the top bar on desktop. On mobile it is displayed at the bottom of the screen.</div>
<div>
With the last option the Menu Bar will only show icons, thus preserving vertical space. </div>
<h2>
Menu items</h2>
<div>
Now we will add menu items:</div>
<div>
<ul>
<li>Go to <b>Shared Components > Navigation Menu > Desktop Navigation Menu</b></li>
<li>Delete the first menu item <b>Home</b></li>
<li>Open the option <b>Activities</b>:</li>
<ul>
<li>Set <b>Image/Class</b> to <b>fa-home</b></li>
</ul>
<li>Create new List Entry:</li>
<ul>
<li><b>Image/Class</b>: fa-plus</li>
<li><b>List Entry Label</b>: New Activity</li>
<li><b>Target > Page</b>: 15</li>
<li><b>Target > Clear Cache</b>: 15 <i><span style="font-size: x-small;">Empty the page items for new record</span></i></li>
<li><b>Conditions > Condition Type</b>: Current Page = Expression 1</li>
<li><b>Conditions > Expression 1</b>: 10 <i><span style="font-size: x-small;">Only show on page 10</span></i></li>
</ul>
<li>Create new List Entry:</li>
<ul>
<li><b>Image/Class</b>: fa-save</li>
<li><b>List Entry Label</b>: Save</li>
<li><b>Target > Target type</b>: URL</li>
<li><b>Target > URL Target</b>: javascript:$('.save_button').click();</li>
<li><b>Conditions > Condition Type</b>: Current Page = Expression 1</li>
<li><b>Conditions > Expression 1</b>: 15 <i><span style="font-size: x-small;">Only show on page 15</span></i></li>
</ul>
</ul>
</div>
<div>
<style type="text/css">
p.p1 {margin: 0.0px 0.0px 7.0px 36.0px; text-indent: -36.0px; line-height: 19.0px; font: 14.0px 'Helvetica Neue'; color: #000000; -webkit-text-stroke: #000000}
p.p2 {margin: 0.0px 0.0px 7.0px 36.0px; text-indent: -36.0px; line-height: 19.0px; font: 12.0px 'Helvetica Neue'; color: #000000; -webkit-text-stroke: #000000}
span.s1 {font-kerning: none}
</style>
These menu options will be shown in the menu bar. Some options will only be shown on one page.<br />
Run the application and observe the behavior of the menu bar. The Save action will be enabled when the Form page is refined. The Save option will execute the action connected to the button with the class <b>save_button</b>.<br />
<h2>
JavaScript and CSS</h2>
<h2>
<span style="font-family: inherit; font-size: small; font-weight: normal;">In following steps some JavaScript and CSS code is added. This code is wrapped in files which are loaded into the Static Files. Download the <a href="https://www.speech2form.com/assets/projects/ttm/blog3/apex_mobile.js">JavaScript</a> and <a href="https://www.speech2form.com/assets/projects/ttm/blog3/apex_mobile.css">CSS file</a> using the links and upload them to the Application Static Files in the Shared Components. Then create references to these files in order for the code to be available on all pages:</span></h2>
</div>
<ul>
<li>Open the <b>Shared Components > User Interface Attributes > User Interface > Desktop</b></li>
<li>In the Tab <b>JavaScript</b>:</li>
<ul>
<li>Set <b>File URLs</b> to <b>#APP_IMAGES#apex_mobile.js</b></li>
</ul>
<li>In the Tab <b>Cascading Style Sheets</b>:</li>
<ul>
<li>Set <b>File URLs</b> to <b>#APP_IMAGES#apex_mobile.css</b></li>
</ul>
</ul>
<h2>
<span class="s1"><b><span style="font-family: inherit; font-size: large;">Reclaiming unused space</span></b></span></h2>
<div class="p1">
The Universal Theme regularly uses white space. On the desktop this avoids a clogged interface. </div>
<div class="p1">
On mobile however screen space is valuable and should be used as effective as possible.</div>
<div class="p1">
Therefore we will reclaim unused screen space as much as possible. One of the possibilities is to </div>
<div class="p1">
remove the padding of the inner content of the body: </div>
<div class="p1">
<br /></div>
<div class="p1">
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">.t-Body-contentInner { padding: 0; }</span></div>
<div class="p1">
<br /></div>
<div class="p1">
With this CSS we remove the white border around the regions. </div>
<div class="p1">
The CSS is included in the CSS file <b>apex_mobile.css</b>. As we have loaded this file in the last paragraph</div>
<div class="p1">
the change is already active. </div>
<div class="p1">
In the next posts will reclaim more unused space using the <b>Region Template Options</b>.</div>
<h2>
<b>Page title in Top Bar</b></h2>
<div>
Another space user is the Breadcrumb which is used to indicate which page is shown. This information can also be shown in the Top Bar. </div>
<div>
For this we need to apply some JavaScript. We will place the code in a Page Load Dynamic Action on the Global Page. This way the code will be executed on each page:</div>
<div>
<ul>
<li>Open page 0 (Global Page - Desktop)</li>
<li>Goto Dynamic Actions <i><span style="font-size: x-small;">The tab with the Lightning Icon in the Left Pane</span></i></li>
<li>Left Click on <b>Page Load</b> and chose <b>Create Dynamic Action</b></li>
<li>For this Dynamic Action change the title to <b>Page Load - General</b></li>
<li>Change the True action <b>New</b>:</li>
<ul>
<li><b>Action</b>: Execute JavaScript Code</li>
<li><b>Code</b>: TitleInTopbar();</li>
</ul>
</ul>
<div>
The referenced code gets the page title from the page header and displays it in the Top Bar. </div>
</div>
<div>
<br /></div>
<div class="p1">
<b><br /></b></div>
<div class="p1">
<br /></div>
<div class="p1">
<br /></div>
<br />
<style type="text/css">
p.p1 {margin: 0.0px 0.0px 7.0px 36.0px; text-indent: -36.0px; line-height: 19.0px; font: 14.0px 'Helvetica Neue'; color: #000000; -webkit-text-stroke: #000000}
p.p2 {margin: 0.0px 0.0px 7.0px 36.0px; text-indent: -36.0px; line-height: 19.0px; font: 12.0px 'Helvetica Neue'; color: #000000; -webkit-text-stroke: #000000}
span.s1 {font-kerning: none}
</style>Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com2tag:blogger.com,1999:blog-4312362131290962824.post-21839812270994555012019-07-07T10:46:00.001+02:002019-07-28T12:30:44.009+02:00Creating a mobile app with APEX - Part 2: The basic appIn the second part of this series we will create the basic app. This is the out of the box app which is already fully usable to insert, update and delete data.<br />
<br />
Before creating the app we need to create the database objects. The <a href="https://www.speech2form.com/assets/projects/ttm/create_objects.sql" target="_blank">object creation script</a> should be run in a schema with at least the privileges to create tables, views, sequences, procedures and triggers. In Oracle APEX add a workspace assignment for this schema.<br />
<br />
<div class="p1">
<h2>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Creating the application</span></h2>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Now we can create the application:</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">log into the APEX workspace</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">open the <b>App Builder</b></span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">hit the <b>Create</b> button</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Chose <b>New Application</b></span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Enter a <b>Name</b> for the application</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Change the <b>Application ID</b> if desired</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Select the schema with the objects as value for <b>Schema</b></span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Hit <b>Create Application</b></span></li>
</ul>
<br />
<h2>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Creating the pages</span></h2>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Create the List View and Form pages using the <b>create page wizard</b>:</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Press <b>Create Page</b></span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">chose <b>Forms</b></span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">hit <b>Report with List View on Table</b></span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Fill in the following data:</span></li>
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Report Page Number</b>: 10</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Report Page Name</b>: Activities</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Form Page Number</b>: 15</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Form Page Name</b>: Activity</span></li>
</ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Press <b>Next</b></span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Check <b>Create a new navigation menu entry</b> and hit <b>Next</b></span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Chose the table <b>ttm_activities_vw</b> and press <b>Next</b>. <i>We will replace this with a query later on.</i></span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Check <b>Select Primary Key Column(s)</b> and select all columns in the shuttle. </span></li>
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Select <b>act_id</b> for <b>Primary Key Column 1</b></span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Remove the column <b>act_prj_name</b></span></li>
</ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Hit the <b>Create</b> button</span></li>
</ul>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">We can run the application and see how it looks:</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbErkSwQFeizDTHM78aeH203jqGKF8jFeoVnaYRP0JybsxKSjUfOKVYZ5E1mHSSRG-F8EL6iQ7oU_geCjPCFrsfQ5gvRWE5-pOg3Dh3_hId034kXlAxVGURIct2d-REDipR0U4oPtvHycm/s1600/Schermafbeelding+2019-07-06+om+11.36.51.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbErkSwQFeizDTHM78aeH203jqGKF8jFeoVnaYRP0JybsxKSjUfOKVYZ5E1mHSSRG-F8EL6iQ7oU_geCjPCFrsfQ5gvRWE5-pOg3Dh3_hId034kXlAxVGURIct2d-REDipR0U4oPtvHycm/s400/Schermafbeelding+2019-07-06+om+11.36.51.png" width="236" /></span></a></div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZ9sdczevXn-r49v_k0RUgsHTbpWG_aJgAymWQ9BVeHgGIh5E_J2dvO2lfK0P0iqKix1RWo0ghNO2XItcCzxZeZ2QN6A2GqwfDEwKblkc3XcBPkWHq-xxxFMAHNLRk2VQC3cN4D1plcOEh/s1600/Schermafbeelding+2019-07-06+om+11.36.34.png" imageanchor="1"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZ9sdczevXn-r49v_k0RUgsHTbpWG_aJgAymWQ9BVeHgGIh5E_J2dvO2lfK0P0iqKix1RWo0ghNO2XItcCzxZeZ2QN6A2GqwfDEwKblkc3XcBPkWHq-xxxFMAHNLRk2VQC3cN4D1plcOEh/s400/Schermafbeelding+2019-07-06+om+11.36.34.png" width="225" /></a></span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Now the pages have been created, but they do not function very well:</span><br />
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">the list view only shows ID’s</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">the form page contains large fields with bad labels and a numeric project reference</span></li>
</ul>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Note that it is already possible to insert and update activities using the form page.</span></div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<h2>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Improving the List View</span></h2>
<span style="font-family: "arial" , "helvetica" , sans-serif;">To refine the list view open page 10:</span><br />
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Select the region <b>List view</b></span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Change the following values:</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Title:</b> Activities</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Source > SQL Query: </b></span></li>
</ul>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">select act.act_id</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , to_char(act_start_date,'fmDay, fmMonth fmddth yyyy ') </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> as start_date</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , act_prj_name as project_name</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , act_start_time</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> || ' - ' ||act_end_time</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> || ' ' ||act_description as details</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">from ttm_activities_vw act</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">order by trunc(act_start_date) desc, act_start_time</span><br />
<ul><span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
</span>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Open the <b>Attributes</b> of the <b>Activities</b> region. In <b>Settings</b>:</span></li>
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">check <b>Show List Divider</b></span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">uncheck <b>Enable Search </b><i>Search will be implemented later</i></span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Text Column: </b>PROJECT_NAME</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Supplemental Information Column: </b>DETAILS</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>List Divider Column: </b>START_DATE</span></li>
</ul>
</ul>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Run the application and notice how much better the content looks already. </span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVAH8srij8qZ4-4gP_DTNRUTJMyG2GnH3PpwJZk66xwNTcy_yS6B12FN8iLVe0p_eCgTKzZcrp_Nu1EkIWjrEgdK_juaNQhLRLEYQDZ7SnTtTFGoBo5mD6nfjZsf_ggXDFghhAgq13x9-N/s1600/Schermafbeelding+2019-07-06+om+11.43.30.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVAH8srij8qZ4-4gP_DTNRUTJMyG2GnH3PpwJZk66xwNTcy_yS6B12FN8iLVe0p_eCgTKzZcrp_Nu1EkIWjrEgdK_juaNQhLRLEYQDZ7SnTtTFGoBo5mD6nfjZsf_ggXDFghhAgq13x9-N/s400/Schermafbeelding+2019-07-06+om+11.43.30.png" width="236" /></span></a></div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<span style="font-family: "arial" , "helvetica" , sans-serif;">We have clear information about the activities and they are grouped and sorted in a useful way. </span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Some remarks about the changes on the list view page: </span><br />
<br />
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">the base query of the List View region provides the elements to be shown. For the second line the start time, end time and description are combined into one string, supplying a lot of information about the activity</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">it shows how easy it is to define a list divider. Just check the Show List Divider box and supply the column</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">the Search option is also very easy to define. Just check Enable Search and provide the column to be searched.It is possible to define whether the search is exact and how it handles case. The search column can be an existing column or a new column combining to be searched. <i>The search option is disabled because the List View will show one week of activities based on a date picker item. </i></span></li>
</ul>
<br />
<h2>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Refining the form page</span></h2>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Now we will improve the form page:</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Open Page <b>15</b>.</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Select the region Form on <b>TTM_ACTIVITIES_VW</b></span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Set <b>Title</b> to <b>Activity</b>.</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Change values for the items:</span></li>
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">P15_ACT_START_DATE:</span></li>
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Label: </b>Date</span></li>
</ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">P15_ACT_PRJ_ID:</span></li>
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Type:</b> Select List</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Label:</b> Project</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>List of Values:</b></span></li>
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Type: </b>SQL Query</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>SQL Query: </b>select prj_name, prj_id from ttm_projects order by 1</span></li>
</ul>
</ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">P15_ACT_START_TIME</span></li>
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Type:</b> Text Field</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Label: </b>Start time</span></li>
</ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">P15_ACT_END_TIME</span></li>
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Type:</b> Text Field</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Label: </b>End time</span></li>
</ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">P15_ACT_DESCRIPTION</span></li>
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Label:</b> Description</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Appearance </b>> <b>Height:</b> 2</span></li>
</ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">P15_ACT_LOCATION</span></li>
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Type:</b> Text Field with autocomplete</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Label: </b>Location</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>List of Values:</b></span></li>
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>Type: </b>SQL Query</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><b>SQL Query: </b>select distinct act_location from ttm_activities order by 1</span></li>
</ul>
</ul>
</ul>
</ul>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><i>Added on 28-07-2019: In APEX 19.1 we need to change the Save process because the base table is a view:</i></span><br />
<br />
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><i>On page 15 open the <b>Process Tab</b></i></span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><i>Select <b>Process form Activity</b></i></span></li>
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><i>In <b>Settings</b>:</i></span></li>
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><i><b>Lock Row</b>: No</i></span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><i><b>Return Primary Key(s) after insert</b>: No</i></span></li>
</ul>
</ul>
</ul>
<br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">When the form page is run the improvements become clear:</span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwgIDrtOojRYW4tWcIVszdJvX8Npq8chOhW-kFJSOIrzCGowe_yRsu1qr3SHs0NGyNzKbDYRIX-D42CCKx66PkW3l40reNYPANPYctk2ANTN7SVy6deLocsmN5OyWGsKFi9SCX2aIF6duY/s1600/Schermafbeelding+2019-07-06+om+11.52.35.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: "arial" , "helvetica" , sans-serif;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwgIDrtOojRYW4tWcIVszdJvX8Npq8chOhW-kFJSOIrzCGowe_yRsu1qr3SHs0NGyNzKbDYRIX-D42CCKx66PkW3l40reNYPANPYctk2ANTN7SVy6deLocsmN5OyWGsKFi9SCX2aIF6duY/s400/Schermafbeelding+2019-07-06+om+11.52.35.png" width="236" /></span></a></div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">The labels are improved and the page fits on the screen. The Project name is shown and can be chosen using a select list. Entering the Location is supported by the auto complete functionality. </span><br />
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "arial" , "helvetica" , sans-serif;">Some remarks about the changes on the form page: </span><br />
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">APEX uses the column names as labels. They usually need to be changed to some more useful text</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">The form wizard creates Textarea items of 4 lines for most <b>varchar2</b> columns. This takes a lot of vertical space and is not needed in most cases. In this case only the description is expected to need 2 lines. The type of the other items is changed to <b>Text Field</b>.</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">To show the project name for selecting the project the item type is changed to <b>Select List</b>. <i>For reasons of simplicity the LOV query is defined with the item instead of in the Shared Components.</i></span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;"><span style="font-family: "times" , "times new roman" , serif;">To implement the specified auto complete functionality the type of the location item is changed to </span><b>Text Field with autocomplete</b>. Now the location can be chosen from previously entered locations filtered by the text already entered. <i>Note that the query only returns one column.</i></span></li>
</ul>
<h2>
<span style="font-family: "arial" , "helvetica" , sans-serif;">About the next post</span></h2>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;">In the next episode we will look at the general layout of the pages. </span></div>
<div>
<ul>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Our own Theme Style will be applied</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">The menu will be changed to a navigation bar</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Screen space will be used more efficient</span></li>
<li><span style="font-family: "arial" , "helvetica" , sans-serif;">Page title will be placed in the top bar</span></li>
</ul>
</div>
<div>
<span style="font-family: "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<i><br /></i></div>
</div>
<style type="text/css">
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px 'Helvetica Neue'; color: #000000}
p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px 'Helvetica Neue'; color: #000000; min-height: 12.0px}
p.p3 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px 'Helvetica Neue'; color: #000000; min-height: 13.0px}
li.li1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px 'Helvetica Neue'; color: #000000}
span.s1 {font: 13.2px 'Helvetica Neue'}
span.s2 {font-kerning: none}
ul.ul1 {list-style-type: disc}
</style>Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com2tag:blogger.com,1999:blog-4312362131290962824.post-54679822969752522042019-07-04T12:20:00.001+02:002019-08-06T03:25:04.378+02:00Creating a mobile app with APEX - Part 1: Introduction and design<style type="text/css">
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px 'Helvetica Neue'; color: #000000}
p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px 'Helvetica Neue'; color: #000000; min-height: 12.0px}
span.s1 {font-kerning: none}
</style>
<br />
<div class="p1">
<span class="s1">Coming back from my 5 month sabbatical in South East Asia I am refreshed and ready to resume blogging ;-).<span class="Apple-converted-space"> </span></span></div>
<div class="p2">
<span class="s1"></span><br /></div>
<div class="p1">
<span class="s1">The first task is have assigned to myself is to write a series of blogposts about mobile development with APEX 18.1 (and up). Since this version support for mobile applications is included in the Universal Theme which makes it even easier to develop small screen applications.</span></div>
<div class="p2">
<span class="s1"></span><br /></div>
<div class="p1">
<span class="s1">I have a number of personal productivity apps like registering expenses and keeping track of working hours. These apps are (of course) built using APEX and targeted for large screen/keyboard use.<span class="Apple-converted-space"> </span></span></div>
<div class="p2">
<span class="s1"></span><br /></div>
<div class="p1">
<span class="s1">In a series of blogposts I will describe the process of building a client smartphone app for an existing time registration application.<span class="Apple-converted-space"> </span></span></div>
<div class="p2">
<span class="s1"></span><br /></div>
<div class="p1">
<span class="s1">The report and data entry screen for the desktop app is shown below:</span></div>
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjK_pv0fsTIts8nbL5q48HuT95OzBj59PERugMlsvZtMRot3iybM6Odzx9O79BpkCIyN_3TczqaD-hmarp6Tw16p0hysRa_30RMTMdTFafgIo3v4jKvk4xJBNZHOlf-mRUqLx1sB7kX-I9F/s1600/Schermafbeelding+2019-07-02+om+15.18.42.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="175" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjK_pv0fsTIts8nbL5q48HuT95OzBj59PERugMlsvZtMRot3iybM6Odzx9O79BpkCIyN_3TczqaD-hmarp6Tw16p0hysRa_30RMTMdTFafgIo3v4jKvk4xJBNZHOlf-mRUqLx1sB7kX-I9F/s400/Schermafbeelding+2019-07-02+om+15.18.42.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjK_pv0fsTIts8nbL5q48HuT95OzBj59PERugMlsvZtMRot3iybM6Odzx9O79BpkCIyN_3TczqaD-hmarp6Tw16p0hysRa_30RMTMdTFafgIo3v4jKvk4xJBNZHOlf-mRUqLx1sB7kX-I9F/s1600/Schermafbeelding+2019-07-02+om+15.18.42.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzc_XsWAzl4YfNz5z8ifLIaTqyk8rAUMyR6fyJKryBWRpfgN-ulIh5nkbTqHrAw6TEGiNUM5YAp0z0meJ8zbAqhTn3azJw8LtNba9uc6hqIJZu5XvX44ebJ0j8Jaa5bX4EXz-0x6pGilfF/s1600/Schermafbeelding+2019-07-02+om+15.21.46.png" imageanchor="1"><img border="0" height="143" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzc_XsWAzl4YfNz5z8ifLIaTqyk8rAUMyR6fyJKryBWRpfgN-ulIh5nkbTqHrAw6TEGiNUM5YAp0z0meJ8zbAqhTn3azJw8LtNba9uc6hqIJZu5XvX44ebJ0j8Jaa5bX4EXz-0x6pGilfF/s320/Schermafbeelding+2019-07-02+om+15.21.46.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="p1">
<span class="s1"></span></div>
<div class="p1">
<span class="s1">I want to have access this data also on my phone. I also want to enter a new activity or change existing activities.</span></div>
<div>
<span style="font-family: inherit;">The desktop pages are not suitable for use on a smartphone.<span class="Apple-converted-space"> They take up to much screen space or when zoomed the text is too small and the text boxes can not be handled. </span></span></div>
<div class="p2">
<span class="s1"></span><br /></div>
<div class="p1">
<span class="s1">I want to have access this data also on my phone. I also want to enter a new activity or change existing activities.</span></div>
<div class="p1">
<span class="s1">The design of forms and especially reports needs to take into account the small size of the screen. I have the need to have an overview of activities and a form page to enter or edit an activity.<span class="Apple-converted-space"> </span></span></div>
<div class="p1">
<span class="s1"><span class="Apple-converted-space"><br /></span></span></div>
<div class="p1">
<span class="s1">Using the mockup tool Balsamiq the layout of the pages is created:</span></div>
<div class="separator" style="clear: both; text-align: left;">
<style type="text/css">
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px 'Helvetica Neue'; color: #000000}
p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px 'Helvetica Neue'; color: #000000; min-height: 12.0px}
span.s1 {font-kerning: none}
</style>
</div>
<div class="p2">
<span class="s1"></span><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipWDlfx23uFB2UqLnMDEAKTqdVih7QY9FTN9GpA0NC0kOSMwX4kaMPsOm753eS7vVS11AOfTXt43U3RuQQzsO62v6N-8kyRHnYvtDiHtAOhpqeHgpv3qBU-2WC6XYXWUz9rjNnawS2pQP4/s1600/TTM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="294" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipWDlfx23uFB2UqLnMDEAKTqdVih7QY9FTN9GpA0NC0kOSMwX4kaMPsOm753eS7vVS11AOfTXt43U3RuQQzsO62v6N-8kyRHnYvtDiHtAOhpqeHgpv3qBU-2WC6XYXWUz9rjNnawS2pQP4/s320/TTM.png" width="320" /></a></div>
<br />
<div class="p1">
<span class="s1">The Activities page will serve as the Home page. It shows a list of all the activities for one week. The activities are grouped by day, ordered by time. The days are ordered descending, meaning the most recent day is shown on top. <span class="Apple-converted-space"> </span></span></div>
<div class="p1">
<span class="s1">The date picker at the top of the screen is default set to today. Another week can be accessed by changing the date by entering another date, using the date picker or the arrows to move to the previous or next week.<span class="Apple-converted-space"> </span></span></div>
<div class="p1">
<span class="s1">Each activity is shown as a list item with project name and location as main text and time and description as details.<span class="Apple-converted-space"> </span></span></div>
<div class="p2">
<span class="s1"></span><br /></div>
<div class="p1">
<span class="s1">The Activity page can be used to enter a new activity or update an existing one.<span class="Apple-converted-space"> </span></span></div>
<div class="p1">
<span class="s1">The projects can be chosen from a list.<span class="Apple-converted-space"> </span></span></div>
<div class="p1">
<span class="s1">The date can be entered using the date picker.<span class="Apple-converted-space"> </span></span></div>
<div class="p1">
<span class="s1">The time fields can be entered using the format HH24:MI.<span class="Apple-converted-space"> </span></span></div>
<div class="p1">
<span class="s1">The location will be implemented with Autocomplete functionality.<span class="Apple-converted-space"> </span></span></div>
<div class="p2">
<br />
<span class="s1"></span></div>
<div class="p1">
<h2>
<span class="s1">Coming blog posts in this series</span></h2>
<br />
<ol>
<li><span class="s1">Introduction and design</span></li>
<li><span class="s1"><a href="https://dickdral.blogspot.com/2019/07/creating-mobile-app-with-apex-part-2.html" target="_blank">The basic app</a></span></li>
<li><span class="s1"><a href="https://dickdral.blogspot.com/2019/07/creating-mobile-app-with-apex-part-3.html" target="_blank">Refining the layout</a></span></li>
<li><span class="s1"><a href="https://dickdral.blogspot.com/2019/07/creating-mobile-app-with-apex-part-4.html" target="_blank">Refining the List View</a></span></li>
<li><span class="s1"><a href="https://dickdral.blogspot.com/2019/07/creating-mobile-app-with-apex-part-5.html" target="_blank">Refining the Form</a></span></li>
<li><span class="s1"><a href="https://dickdral.blogspot.com/2019/07/creating-mobile-app-with-apex-part-6.html" target="_blank">How to prevent auto-zoom on iOS</a></span></li>
<li><span class="s1"><a href="https://dickdral.blogspot.com/2019/07/creating-mobile-app-with-apex-part-7.html" target="_blank">Input and processing of time items</a></span></li>
<li><span class="s1"><a href="https://dickdral.blogspot.com/2019/07/creating-mobile-app-with-apex-part-8.html" target="_blank">Autologin</a></span></li>
<li><span class="s1"><a href="https://dickdral.blogspot.com/2019/07/creating-mobile-app-with-apex-part-9.html" target="_blank">Create app icon on smartphone</a></span></li>
<li><span class="s1"><a href="https://dickdral.blogspot.com/2019/08/creating-mobile-app-with-apex-part-10.html" target="_blank">Using your GPS location</a></span></li>
</ol>
</div>
Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com18tag:blogger.com,1999:blog-4312362131290962824.post-5722685031830392162018-12-13T11:59:00.001+01:002018-12-13T15:54:46.129+01:00Applying a style to a line of a classic reportAPEX is a powerful tool, but even powerful tools have their shortcomings. One of the functionalities I need regularly is the styling of (parts of) a row of a Classic report based on a value from the base query of the report.<br />
<br />
In my example I have a summary line of averages. This line is generated by the base query of the report. I would like to indicate the summary line by using bold characters and a light grey background. The bold characters could be applied in the query but it would imply mixing data and styling and would result in less readable query. The background has to be applied on the <b>td</b> element that cannot be influenced using the declarative features.<br />
So how can this be solved elegantly?<br />
<br />
The solution I chose is to include a hidden input element in the query that holds the name of a class to be applied to the <b>tr</b> element. The hidden element is appended to a column of the query for which escaping of special characters should be disabled.<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">select ename</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> || </span><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">apex_item.hidden(0,'summary-line','data-name="row_class" ')</span> <span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">as ename</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">...</span><br />
<br />
It is important to have the attribute data-name <b>row_class</b>. The value is the class name to be applied in the row. The first argument in the apex_item.hidden call is not important, unless it is a tabular form.<br />
<br />
In the After Refresh Dynamic Action on the report, this class is applied to the row using this line of JavaScript code:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">$('input[data-name="row_class"]').each(function() { $(this).closest('tr').addClass($(this).val());});</span><br />
<br />
Now you can define whatever styling you want for the row. For the example I mentioned:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">tr.summary-line td {</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> font-weight: bold;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> background-color: #D0D0D0!important; </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">}</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "times" , "times new roman" , serif;">Note that defining the background color does not work for <b>tr</b> elements, but should be applied on the <b>td </b>elements. Unfortunately the use of !<b>important</b> is needed because the alternate row coloring of APEX has very specific selectors, which would make our CSS very complex. </span><br />
<span style="font-family: "times" , "times new roman" , serif;"><br /></span>
<span style="font-family: "times" , "times new roman" , serif;">It is also possible to style individual columns, for example showing the salary cell in bold red when the salary is too low:</span><br />
<span style="font-family: "times" , "times new roman" , serif;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">select ...</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , sal </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> || case when sal < 1000</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> then apex_item.hidden(0,'sal-to-low','data-name="row_class" ') </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> end </span><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> as sal</span><br />
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">...</span></div>
<span style="font-family: "times" , "times new roman" , serif;"><br /></span>
<span style="font-family: "times" , "times new roman" , serif;">Combined with the following CSS it yields the desired result:</span><br />
<span style="font-family: "times" , "times new roman" , serif;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">tr.sal-to-low td[headers="SAL"] {</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> color: red;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> font-weight: bold;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">}</span><br />
<div>
<br /></div>
<span style="font-family: "times" , "times new roman" , serif;">The JavaScript supports multiple row_class items per row so you can apply multiple classes to a row. </span><br />
<span style="font-family: "times" , "times new roman" , serif;"><br /></span>
<span style="font-family: "times" , "times new roman" , serif;">Happy APEXing</span><br />
<span style="font-family: "times" , "times new roman" , serif;"><br /></span>Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com0tag:blogger.com,1999:blog-4312362131290962824.post-4499396819238884422018-05-29T17:17:00.000+02:002018-05-29T17:17:02.989+02:00Limitations of Oracle Exadata Express X20For my current customer in the agricultural business I have created an application to plan the growths of Chrysanthemum flowers. The current users were used to work in Excel so I had to build in all kind of Excel like features, like navigation with arrow keys and exotic layouts (everything is possible in Excel). The application makes extensive use of Ajax calls to provide an interactive user interface.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiz1E0YlT82qbn4lIKmqGql2vAaQlbCJSOjyx7_gLs4wtd9QZe6-FuQTg14hqPBtL_QEZZBLKUl9UVtrDEW8Qm7G6sZJPXbhbgXNHuHk0NVtmnvCChutyxl2a_Q7Vv5gJU4ZKPxzAx_-OzM/s1600/Schermafbeelding+2018-05-29+om+17.09.00.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="820" data-original-width="1600" height="328" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiz1E0YlT82qbn4lIKmqGql2vAaQlbCJSOjyx7_gLs4wtd9QZe6-FuQTg14hqPBtL_QEZZBLKUl9UVtrDEW8Qm7G6sZJPXbhbgXNHuHk0NVtmnvCChutyxl2a_Q7Vv5gJU4ZKPxzAx_-OzM/s640/Schermafbeelding+2018-05-29+om+17.09.00.png" width="640" /></a></div>
<br />
The interface cannot be called a typical APEX Universal Theme interface.<br />
Our development enviroment was a virtual Windows server with an Oracle XE database. This environment provided a reasonable performance.<br />
<br />
<h2>
The Exadata Express Environment</h2>
The application should be deployed on an Exadata Express cloud server. After some struggles and hickups with the provisioning the management console of the Exadata is easy to use. With the right privileges a user has access to both the APEX administration environment and all workspaces.<br />
<h2>
Access from SQL Developer</h2>
It took some time to figure out how to get access with SQL Developer. You need to download a ZIP file with TNS configuration files and a keystore. During the download process you need to provide a password to protect the keystore. The download process was quite picky about the passwords provided, and a lot of passwords were rejected while they adhered to the password rules given. It took some time to find an appropriate password, and I still do not know what made the difference (I did not want to spend more time on it). The zip file can be referred to when defining the SQL Developer connections.<br />
<h2>
Migrating the database</h2>
The initial database was migrated using a script generated with SQL Developer. After that the base data was migrated with drag-and-drop from the development schema to the cloud schema.<br />
Of course the migration of the APEX schema was a piece of cake. Just import the application and run.<br />
<h2>
Referencing the JS and CSS files</h2>
The only thing that needed to be changed was the reference to the JS and CSS files. In the development environment those files are placed on the webserver and referenced through virtual directories, like <b>/assets/js</b> and <b>/assets/css</b>.<br />
As there is no webserver or OS access on Exadata the files can only be loaded into the workspace static files. And the reference to those files has the form <b>#WORKSPACE_IMAGES#</b>. And the references to the virtual directories were all over the place.<br />
The solution for this was to edit the application export (<b>do not do this at home</b> or just if your know what you are doing). The references to virtual directories were replaced by references to substitution variables <b>&JS_DIR.</b> and &<b>CSS_DIR.</b>. Import the application in the development environment and define the substitution variables <b>JS_DIR</b> and <b>CSS_DIR</b> with values <b>/assets/js/</b> and <b>/assets/css/ (do not forget the last slash)</b>.<br />
After import of the changed application on the Exadata instance all that should be done is change the value of the substitution parameters to <b>#WORKSPACE_IMAGES. </b> And of course loading the files.<br />
<h2>
Loading the JS and CSS files</h2>
<div>
Some 25 JS and CSS files should be loaded into the database. Normally a lot of pointing and clicking. But luckily APEX can load and extract zip files. So zip all the files and load the ZIP file into the static workspace files. APEX will automatically unzip the files when you set<b> Unzip File</b> to <b>Yes</b>:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfe9IZd_qLawOLp_NuvbE_1qYwolYM1ZozJiGcWuJzuDvJAlOgUQGv4EKRKLQCetgXhC1JyM4-m0411SnRR5FFkFsdRvJS8VZf9rDAvFzMFK1SUgfmFaW9yzpn4cvleW6rf7OGTCBVbH36/s1600/Unzip.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="376" data-original-width="1342" height="111" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfe9IZd_qLawOLp_NuvbE_1qYwolYM1ZozJiGcWuJzuDvJAlOgUQGv4EKRKLQCetgXhC1JyM4-m0411SnRR5FFkFsdRvJS8VZf9rDAvFzMFK1SUgfmFaW9yzpn4cvleW6rf7OGTCBVbH36/s400/Unzip.png" width="400" /></a></div>
<div>
<br /></div>
<h2>
Running the application => Error</h2>
<div>
Now everything was ready to run the application. So one person started to test the application, and after half an hour suddenly the application issued an error message: </div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLr2oom53niYdFB37vHdOnEmAcX7pQN9gCRQQP1e9m0QAHN3qOhdgwP4mu0hp1rd4mWWtiAZGTcwdsS90V3PnslkRj7NE2o30lnaC8kD3EqAyjwRapMjWpX0qm9t-kZ6RhWWEftqBVtZlW/s1600/image003-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="276" data-original-width="960" height="115" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLr2oom53niYdFB37vHdOnEmAcX7pQN9gCRQQP1e9m0QAHN3qOhdgwP4mu0hp1rd4mWWtiAZGTcwdsS90V3PnslkRj7NE2o30lnaC8kD3EqAyjwRapMjWpX0qm9t-kZ6RhWWEftqBVtZlW/s400/image003-1.png" width="400" /></a></div>
<div>
<br /></div>
<div>
Also the APEX Builder was not reachable. After an hour or so the application suddenly became available again. This happened a few times until I tried to connect using SQL Developer and got the message:</div>
<div>
<br /></div>
<div>
<span style="background-color: #f6f6f6; color: #333333; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px;">ORA-00018: Maximum number of sessions exceed.</span></div>
<div>
<span style="background-color: #f6f6f6; color: #333333; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px;"><br /></span></div>
<div>
Using this clue it turned out the the Exadata X20 has a MAX_OPEN_SESSIONS parameter set at 30. For a normal APEX application you can host a good number of users because they will not use these sessions simultaniously. But this application used a lot of Ajax calls. </div>
<div>
The user reported to have used a specific page just before the error occurred. And on this page changing the value of a select list fired a dynamic action with 5 refreshes. If you change that select list a few times fast, you hit the limit of 30 sessions on your own! So this page was rebuilt to fire a submit on change so that only one new page was requested instead of numerous refreshes. Less elegant but much more efficient. </div>
<h2>
Performance</h2>
<div>
What was also surprising was, that the application performed less fast on the Exadata than on out development server. It is a heavy application with tough queries but I would have expected the Exadata to outrun Oracle XE on a virtual server. But no, overall the development server was 20-30% faster than the Exadata. </div>
<div>
<br /></div>
<h2>
Conclusion</h2>
<div>
The conclusion is that the Exadata X20 seemed to be too good to be true, and indeed it did not turn out to be the solution for this problem. The limited of the number of sessions proved absolutely too small for this application. Also the performance was not adequate for this type of application. The next Exadata offers also have a limited number of sessions, so my customer moved to Oracle DBAAS. </div>
<div>
<br /></div>
<div>
Happy APEXing</div>
<b><br /></b>
<br />Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com3tag:blogger.com,1999:blog-4312362131290962824.post-41468982234195531862018-05-17T14:21:00.002+02:002018-05-19T08:49:08.418+02:00Create formatted Excel file from your APEX cloud applicationThis post describes generating an Excel file with formatting from an Oracle APEX webpage without the use of printing engines. That makes it perfectly suitable for any cloud solution because you only need Oracle APEX.<br />
<br />
At the moment I am redeveloping an Excel application into an Oracle APEX application. One of the pages generates an order list to be sent to a supplier. The report is created in APEX using a PL/SQL region, so the HTML is generated in PL/SQL.<br />
This is the result for one week:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGdriGdZ1tB9fSSq00UufOq9REudSSqOp5jaUeSogqZa16t60f6tu7dcb1s5d-hDPM4GDoP_wa74Z8htvn9dVwg1xwDvkPI0xrzkiJaXurfpeFYHjSC9cFqjHQDSmmeiqYsMzATh-hYzOf/s1600/Schermafbeelding+2018-05-17+om+13.10.23.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="562" data-original-width="1294" height="172" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGdriGdZ1tB9fSSq00UufOq9REudSSqOp5jaUeSogqZa16t60f6tu7dcb1s5d-hDPM4GDoP_wa74Z8htvn9dVwg1xwDvkPI0xrzkiJaXurfpeFYHjSC9cFqjHQDSmmeiqYsMzATh-hYzOf/s400/Schermafbeelding+2018-05-17+om+13.10.23.png" width="400" /></a></div>
<br />
An order list can contain several weeks.<br />
<br />
<br />
The original Excel application generates order lists as Excel files. That was a piece of cake, because the page was an Excel file. These files were sent to suppliers and looked a lot like the image above. You see the use of background colors, larger cells, colored fonts, cell width.<br />
<br />
A PDF was not acceptable, because the suppliers could not cut and paste the data easily.<br />
So it had to be a 'real' Excel file. There are lots of solutions on the internet to generate Excel files. But most of them require a good deal of coding. And I had done my coding in PL/SQL already, and I did not want to duplicate that.<br />
<h3>
Generate Excel from APEX application</h3>
Standard it is not possible to generate an Excel file with formatting from an APEX application.<br />
Yes, you can download a CSV file and import that file into Excel. But you will get a plain list of data without any colors, borders or bold characters. You will not get near to the requested output.<br />
<br />
After some time searching the internet I stumbled upon a blogpost of Tobias Arnhold: "<a href="http://www.apex-at-work.com/2012/06/easy-table-export-to-xls-based-on-html.html" target="_blank">Easy table export to XLS (based on HTML)</a>".<br />
He describes how you can create an XLS file containing an HTML table. Opening this file from Excel will show the table including formatted headers.<br />
<br />
So I took this direction: create an HTML table with the formatting needed and write it to an XLS file.<br />
JavaScript will be needed to assemble the HTML table to be written.<br />
Then this content will be written to a file as described in the post of Tobias:<br />
- put the content into a hidden APEX item<br />
- use JavaScript to change the content om the hidden APEX item<br />
- submit the page (saving the value of the hidden item to session state)<br />
- write the file in a before header process, getting the content from the hidden item<br />
<br />
As base for the table I used the HTML table for the week orders show above.<br />
And it worked partly. The result I got was a bit disappointing:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5NeICnBPqa067BdJP4QohGWreXF93VDXvUK1uT7h-xGhv1X3n1BcXDujQc-nUWwoJjRoa9OAwVRd6Le2IqOy51sDIom-TDBtFyTar26sdDyD5f-1itMtrcsbYpemgoI7ZyjfCgOR8IKsv/s1600/Schermafbeelding+2018-05-17+om+13.42.27.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="414" data-original-width="556" height="237" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5NeICnBPqa067BdJP4QohGWreXF93VDXvUK1uT7h-xGhv1X3n1BcXDujQc-nUWwoJjRoa9OAwVRd6Le2IqOy51sDIom-TDBtFyTar26sdDyD5f-1itMtrcsbYpemgoI7ZyjfCgOR8IKsv/s320/Schermafbeelding+2018-05-17+om+13.42.27.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
So the most of the data was there, but without formatting. Data in input items was not shown.<br />
The width of some cells is too small so the content is wrapped. A good point was that Excel did recognize the row and col span of the top left cell.<br />
<h3>
HTML to Excel limitations</h3>
With trial and error I found:<br />
<ol>
<li>you can add styles at the beginning of the file</li>
<li>Excel only recognizes the first style assigned to an object</li>
<li>You can define CSS on element types like <b>td</b> or <b>th</b></li>
<li>Excel does not recognize input items</li>
<li>Excel does apply width and height as style attributes</li>
<li>Excel does not support named colors ( only black and white) so use the RGB hex notation to define colors</li>
</ol>
<h3>
Formatting the Excel</h3>
1. Styles. When you embed the HTML table in a normal framework of <b>html</b>, <b>head</b>,<b> </b>and <b>body</b> elements, you can define your styles in the <b>styles</b> element in the <b>head</b>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTKdb3ovT2-VgpVRXFQRZpRKFaDkC1TLo_JTC9nRjRkJF8Q6yhiDEznQYguyX-hUxBLZJABVtHK0P95WwDiEVOgocvSL_dElunLTSSCCR9E2_NRPOr3Pox9rR9Y-vnD8rBrsVLeZ2_AMKM/s1600/Schermafbeelding+2018-05-17+om+14.06.16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="322" data-original-width="654" height="157" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTKdb3ovT2-VgpVRXFQRZpRKFaDkC1TLo_JTC9nRjRkJF8Q6yhiDEznQYguyX-hUxBLZJABVtHK0P95WwDiEVOgocvSL_dElunLTSSCCR9E2_NRPOr3Pox9rR9Y-vnD8rBrsVLeZ2_AMKM/s320/Schermafbeelding+2018-05-17+om+14.06.16.png" width="320" /></a></div>
<br />
2. Multiple styles: you can only use a single style. Remove 'unused' styles, in my case <b>t-Report-cell</b> and <b>t-Report-colHead</b> used in the APEX universal theme. Having only one style per object means that you will probably have some duplicates in your style definitions.<br />
<br />
4. Input items: input items need to be converted to plain text.<br />
<br />
5. Dimensions: for sizing a cell apply the style, for example: <b>style=</b>"<b>width:100px</b>; <b>height:30px;"</b><br />
<br />
Above points mean that the HTML captured from the page should be changed.<br />
<br />
This can be done using JavaScript. In order to be able to use jQuery a copy of the table is created in a div in a hidden APEX region. The ID of the table is changed. Now the table and its elements can be processed.<br />
<br />
With the above knowledge and some JS coding the result is:<br />
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwRPTnNVe9ogzHx2tiBt303o7mSfMhw4tqvLpYz84vsvj5UWWgHRtEEIqpmeF88fmQgPYvnSxZ6bbMqXo3uHtZi_ko9oI_1aO_14Ynolor7KmBXNtuEZLN3mTxy96iad6oCfWUpk5lRiZ_/s1600/Schermafbeelding+2018-05-17+om+14.17.15.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="193" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwRPTnNVe9ogzHx2tiBt303o7mSfMhw4tqvLpYz84vsvj5UWWgHRtEEIqpmeF88fmQgPYvnSxZ6bbMqXo3uHtZi_ko9oI_1aO_14Ynolor7KmBXNtuEZLN3mTxy96iad6oCfWUpk5lRiZ_/s400/Schermafbeelding+2018-05-17+om+14.17.15.png" width="400" /></a></div>
<br />
<br />
<h3>
JavaScript snippets</h3>
Remove all instances of a class:<br />
<br />
<pre>function remove_class(table,p_class)
{
$(table).find('.'+p_class).each( function() { $(this).removeClass(p_class);})
}
</pre>
<br />
Convert input items into plain text:<br />
<br />
<pre> $(table).find('input').each(
function() {
var td = $(this).closest('td');
var value = $(this).val();
$(td).text(value);
}
);
</pre>
<br />
<div>
<b>Happy APEXing,</b><br />
<br /></div>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com7tag:blogger.com,1999:blog-4312362131290962824.post-87972202134922902682018-02-14T16:53:00.000+01:002018-02-16T07:31:29.477+01:00Easier maintenance of standard APEX buttonsOne of the strong points of APEX is the use of templates. Combining templates with template options and attributes you can end up with HTML objects that are declarative and in most cases do exactly what you want. When you want to make general changes to these objects just change the template or the underlying CSS. This is a very good implementation of the DRY (Don't Repeat Yourself) principle.<br />
<br />
However when generating pages with APEX you generate many identical buttons (CREATE,SUBMIT,DELETE,CANCEL,NEW) for which the label is a hard coded property for each instance of the button (not so DRY). If you there is the need to change the labels ( maybe for a translation ), you will have to go through the entire application and change all the relevant instances of the buttons.<br />
It gets even harder (or more nagging) when after finishing the application you are asked to apply another look-and-feel including a left-side icon.<br />
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVSapg78Gg4spTu0C66cVfAoFRFsuG5SNzV1SLjMZolx0rQy7dzts3uHVVM5cPqXi07vINmBknLyvBF5T2ivss2swuadFDtTmKYYSfZVemZdhrClyjPurDzX0BwHP9ePEkc0_6Kyx5zbeT/s1600/Schermafbeelding+2018-02-14+om+16.43.42.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="112" data-original-width="420" height="85" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVSapg78Gg4spTu0C66cVfAoFRFsuG5SNzV1SLjMZolx0rQy7dzts3uHVVM5cPqXi07vINmBknLyvBF5T2ivss2swuadFDtTmKYYSfZVemZdhrClyjPurDzX0BwHP9ePEkc0_6Kyx5zbeT/s320/Schermafbeelding+2018-02-14+om+16.43.42.png" width="320" /></a></div>
<br />
So you have to change the button on the left to the button on the right.<br />
For each button you need to:<br />
- select the button<br />
- apply the CSS class<br />
- apply the icon CSS class<br />
- open the template options (default icon position is right)<br />
- change the icon position<br />
- close the template options<br />
A lot of work, and that times the number of buttons in your application. That is a lot of work and by its repetitive nature very error prone.<br />
All these scattered definitions are not very DRY either.<br />
<br />
A solution I use is to create specific button templates for the most used buttons (CREATE,SUBMIT,DELETE,CANCEL,NEW). The button templates are based on the <b>Text with icon</b> button:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgisQ_C35DyolziisDns29BMGxvB1ftpbBVF0VQySKwZ0sObYK0rmrsORsv84ax8eLltoGdpHYKmFuuVUcilZfaWQ6ZtgDampu_gdXYutaPsI4uGhUH4YdOiP8bL4TdPeNudO1bxXiqsg0K/s1600/Schermafbeelding+2018-02-14+om+16.14.09.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="236" data-original-width="1600" height="94" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgisQ_C35DyolziisDns29BMGxvB1ftpbBVF0VQySKwZ0sObYK0rmrsORsv84ax8eLltoGdpHYKmFuuVUcilZfaWQ6ZtgDampu_gdXYutaPsI4uGhUH4YdOiP8bL4TdPeNudO1bxXiqsg0K/s640/Schermafbeelding+2018-02-14+om+16.14.09.png" width="640" /></a></div>
<br />
<br />
New button templates are created by copying the <b>Text with icon </b>template and adapting it to your needs.In my applications for example for the Save Button, this looks like:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjG80WZILw0jEB9t6yVRm644GinKcspf7eEibv0i0-5uXhR5H2kVe20_6qsEO0NUWHYdaH3rMn6PjMiG0lIbI3h8tJJfxN_UwQ5ECJhiyNkI9nDs0cAC7r8-z_VUuOWnnioz41frBMyKEj_/s1600/Schermafbeelding+2018-02-14+om+16.26.10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="306" data-original-width="1600" height="122" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjG80WZILw0jEB9t6yVRm644GinKcspf7eEibv0i0-5uXhR5H2kVe20_6qsEO0NUWHYdaH3rMn6PjMiG0lIbI3h8tJJfxN_UwQ5ECJhiyNkI9nDs0cAC7r8-z_VUuOWnnioz41frBMyKEj_/s640/Schermafbeelding+2018-02-14+om+16.26.10.png" width="640" /></a></div>
<br />
The label is hard coded to <b>Save</b> and the left icon class <b>fa fa-save</b> refers to the save icon (the floppy disk). As we are using the left icon, the code for the right icon is omitted. The class <b>t-Button--iconLeft</b> is needed to display the left icon. And finally the class <b>delphy-hot</b> defines the specific look-and-feel for this application suite.<br />
<br />
Now all that needs to be done is to apply these specific templates to the buttons. This took me about half an hour for a medium sized application.<br />
Instead of the list of actions mentioned above you just need to:<br />
- select the button<br />
- change the button template<br />
Look at the animated GIF below to see how fast the change can be applied.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirkyLYoUgewtSBXJrQeGBRrMgT8uENKOc9v9w9lfVdNH9mroK8QXcpfjVwTJouzVKy3kZIULgO8rbVGa9lhU1qHVbFsZC0uh4kBA48p5jdcQknK1ZBYr4YCRYJFfT5BqRel9Y54GDNvI-6/s1600/9cb98f0437d474004f3df709598782e0.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="546" data-original-width="866" height="251" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirkyLYoUgewtSBXJrQeGBRrMgT8uENKOc9v9w9lfVdNH9mroK8QXcpfjVwTJouzVKy3kZIULgO8rbVGa9lhU1qHVbFsZC0uh4kBA48p5jdcQknK1ZBYr4YCRYJFfT5BqRel9Y54GDNvI-6/s400/9cb98f0437d474004f3df709598782e0.gif" width="400" /></a></div>
<br />
<br />
Once you have got all the button templates referenced, you can easily change the look-and-feel of all the buttons just by changing the corresponding templates.<br />
<div>
<br /></div>
If you have several applications with the same look-and-feel you can copy the templates from one application to another. After copying you can adapt to the application, for example by translating the labels.<br />
<br />
Happy APEXing ;-)<br />
<br />
<br />
<br />
<br />Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com0tag:blogger.com,1999:blog-4312362131290962824.post-39546945113608153192018-01-24T07:22:00.002+01:002018-10-24T22:36:24.877+02:00Dynamic Navigation BarIn my current assignment I work on a large number of applications. These applications all share the same UI theme. Once logged in you can switch applications through an application menu.<br />
<div>
The navigation bar in all these applications is (or should be) the same. Until today each application had his own copy of the Desktop Navigation Bar which is in fact an Oracle APEX list. </div>
<div>
As the number of applications is approaching double digits you can imagine I would like one code base for the navigation bar content. </div>
<div>
<br /></div>
<div>
In the <b>Shared Components > User Interface Attributes > User Interface Desktop </b>you can find the details of the Navigation Bar:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaxht__6vcwcAnBVIBZlXVM3Gor6bOJmH4CxKLhwEEzbJJ-ZXGCZSPBtB0JCykdlhP0QGoQiaTz-FpWZWlpdMa1Zn4TNXP-Y77cwc-bNU9mANCr9A5whZt0vP4tEF01sp7Yb1Ej_ghuxh4/s1600/Schermafbeelding+2018-01-24+om+06.27.54.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="646" data-original-width="1600" height="256" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaxht__6vcwcAnBVIBZlXVM3Gor6bOJmH4CxKLhwEEzbJJ-ZXGCZSPBtB0JCykdlhP0QGoQiaTz-FpWZWlpdMa1Zn4TNXP-Y77cwc-bNU9mANCr9A5whZt0vP4tEF01sp7Yb1Ej_ghuxh4/s640/Schermafbeelding+2018-01-24+om+06.27.54.png" width="640" /></a></div>
<div>
Here you see that the list on which the Navigation bar is based can be changed. The template can also be chosen, but the current template is sufficient for this moment. </div>
<div>
<br /></div>
<div>
Unfortunately we cannot reference lists in other applications, only copying is possible. </div>
<div>
But we can define a dynamic list to populate the Navigation Bar. This opens the door to defining the list as a query or view in the database, which can be shared between applications. </div>
<div>
I could not find the definition of the list elements needed for the Navigation Bar list, so I had to do some experimenting myself. It seems that the meaning of the columns in the query result set is determined by their position. </div>
<div>
The following query results in a correct entry:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">select '1' as lvl</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , 'Logout' as label</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , 'apex_authentication.logout?p_app_id=&p_session_id=' as target</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , null as attr1</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , 'fa-sign-out' as icon_css_class</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , null as attr3</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , null as attr4</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , '' as badge</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , '' as list_item_css_classes</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">from dual</span></div>
</div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">where nvl(v('APP_USER'),'nobody') != 'nobody'</span></div>
<div>
<br /></div>
<div>
In this query the <b>label</b> is the text displayed in the entry. </div>
<div>
The <b>target</b> is the URL to be linked to. </div>
<div>
The <b>icon_css_class</b> can contain a reference to a Font Awesome/Apex icon. If omitted no icon is shown. </div>
<div>
For some columns I did not find any use, but as the meaning is positional these columns are needed anyway. </div>
<div>
The entries are implemented as an HTML UL element (unordered list). The <b>list_item_css_classes </b>can contain CSS classes for the list item that contains the entry. These classes can for example be used to apply a color to the entry text and icon. </div>
<div>
<br /></div>
<div>
You can build a query by connecting a number of selects from dual of the above form. But this results in a long messy query which is hard to maintain. Luckily a APEX list can also be based on a PL/SQL function returning a query. So the query can be composed in PL/SQL which enables more clean coding. A package is defined with procedures to add an navigation bar entry and to return an APEX url. </div>
<div>
The code to generate the query in PL/SQL looks like this: </div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> function navigation_bar_query return varchar2 is</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> l_target varchar2(1000) := null;</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> begin</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ----------------------------------------------------- </span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> -- logout entry</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> l_target := 'apex_authentication.logout'</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ||'?p_app_id='||v('APP_ID')</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ||'&p_session_id='||v('APP_SESSION');</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> add_navigation_bar_entry</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ( p_label => 'Logout'</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , p_target => l_target</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , p_icon_css_classes => 'fa-sign-out'</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> );</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ----------------------------------------------------- </span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> -- user entry</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> add_navigation_bar_entry</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ( p_label => initcap(v('APP_USER'))</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , p_icon_css_classes => 'fa-user'</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> );</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ----------------------------------------------------- </span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> -- applications entry</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> l_target := apex_url</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ( p_app_id => aut_pck.get_aut_app_id</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , p_app_page_id => 'APP_MENU'</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> );</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> add_navigation_bar_entry</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ( p_label => 'Applications'</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , p_target => l_target</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , p_icon_css_classes => 'fa-desktop'</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> );</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ----------------------------------------------------- </span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> -- DEVELOPMENT entry</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if in_development_environment then</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> add_navigation_bar_entry</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ( p_label => 'DEVELOPMENT'</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , p_icon_css_classes => 'fa-exclamation-circle'</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> , p_list_item_css_classes => 'yellow'</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> );</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> end if;</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> end;</span></div>
<div>
<br /></div>
</div>
<div>
<div>
All the messy code is encapsulated in the procedures and functions. </div>
<div>
Two standard entries are defined: the logout entry and the display of the username. Notice that the username is displayed using initcap. It is also possible to display the user's real name here when it is available in the database. </div>
<div>
<br /></div>
<div>
Next a link to the applications menu is supplied. </div>
<div>
<br /></div>
<div>
The last entry is used to notify a user that he is working in the development environment. This entry is only shown when working in the development environment which can usually be determined in SQL, for example by retrieving the database SID or service. In this way the application can be deployed to other environments unchanged. </div>
<div>
<br /></div>
<div>
You can define a dynamic list using this package: </div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzQBMeCblI_zxLdhfmdtZ-jXf5YvL-VqVUIM2AWvgWLItv8jY5HlbDq4bmd7SQouola6-nAFTHA0G0JRmjrXcuBUuP99zN4TK4G84Lo3kFd51SY5PdHBVHPhJr7c0sfWyezGmxOLkGEtcz/s1600/Schermafbeelding+2018-01-24+om+07.11.36.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="510" data-original-width="1180" height="172" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzQBMeCblI_zxLdhfmdtZ-jXf5YvL-VqVUIM2AWvgWLItv8jY5HlbDq4bmd7SQouola6-nAFTHA0G0JRmjrXcuBUuP99zN4TK4G84Lo3kFd51SY5PdHBVHPhJr7c0sfWyezGmxOLkGEtcz/s400/Schermafbeelding+2018-01-24+om+07.11.36.png" width="400" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
This list can be chosen in the User Interface attributes to represent the Navigation Bar. </div>
<div>
<br /></div>
<div>
The resulting Navigation Bar looks like this: </div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSX6SqW5IgTS4_JwVPpQpwxGeiQXrmRoBocbRIxFqqRlhNZswaGBPYU-yqzfg0El2EHzRGtRXKiKPSFVDBEAjOkBzgLvDh38d21b_9CMTXuqY5nCI0Scq7-wP-ikNy_8a0i6e67k6Mg8ru/s1600/Schermafbeelding+2018-01-24+om+07.09.41.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="144" data-original-width="864" height="66" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSX6SqW5IgTS4_JwVPpQpwxGeiQXrmRoBocbRIxFqqRlhNZswaGBPYU-yqzfg0El2EHzRGtRXKiKPSFVDBEAjOkBzgLvDh38d21b_9CMTXuqY5nCI0Scq7-wP-ikNy_8a0i6e67k6Mg8ru/s400/Schermafbeelding+2018-01-24+om+07.09.41.png" width="400" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<div>
You can download the full code of the package <a href="https://www.speech2form.com/assets/downloads/nav_pck.sql" target="_blank">here</a>. </div>
</div>
<div>
<br /></div>
</div>
<div>
Happy APEXing :-)</div>
<div>
<br /></div>
<div>
<br /></div>
Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com11tag:blogger.com,1999:blog-4312362131290962824.post-54660149857109092302017-12-22T16:41:00.000+01:002017-12-22T16:41:56.125+01:00First impressions on mobile support in APEX 5.2EAFor APEX 5.2 the support of JQuery mobile to create mobile applications will be discontinued. The APEX development team have taken action to enable mobile development using the Universal Theme. In APEX 5.2EA you can already find the List View Region that will replace the equivalent component in JQuery Mobile.<br />
<br />
The List View Region is defined with the same properties as in the Query Mobile version:<br />
<br />
<span id="goog_1287782389"></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjm-a20ByxC5fzTQNaMPHHs_MH7MXRJk07TwhpWiSYDQLBYkX6tHMrQzfkubXMLRWB_nY-r0TxOKjKVbNBWr-XnrGFcWjnvwNV7wkDKQ06woNYgN2CGdGAb62z-wX_y1W3nhjw1XZbpe9OG/s1600/Schermafbeelding+2017-12-22+om+16.10.41.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1036" data-original-width="972" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjm-a20ByxC5fzTQNaMPHHs_MH7MXRJk07TwhpWiSYDQLBYkX6tHMrQzfkubXMLRWB_nY-r0TxOKjKVbNBWr-XnrGFcWjnvwNV7wkDKQ06woNYgN2CGdGAb62z-wX_y1W3nhjw1XZbpe9OG/s400/Schermafbeelding+2017-12-22+om+16.10.41.png" width="375" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br />
The region source contains a query and in the Attributes the role of the column is assigned.<br />
After creating a page with a List View it looks like this.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyJJqARbgaBMzzoJ8wg5KBgExwdf1_HCW21Y11h7uJ5e0qaMt37U2imMWD1xXiqeS7Gk2KVAp2QvKTHIahupl7dK2OKQCyHXTVg7yT7blAcaa9K8epXg3G_xxN8fohR3_z3VN977nCx9om/s1600/Schermafbeelding+2017-12-22+om+16.14.43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1478" data-original-width="832" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyJJqARbgaBMzzoJ8wg5KBgExwdf1_HCW21Y11h7uJ5e0qaMt37U2imMWD1xXiqeS7Gk2KVAp2QvKTHIahupl7dK2OKQCyHXTVg7yT7blAcaa9K8epXg3G_xxN8fohR3_z3VN977nCx9om/s400/Schermafbeelding+2017-12-22+om+16.14.43.png" width="225" /></a></div>
<br />
So the data is present, but the formatting is not. So I read the release documentation again, found on the home page of APEX:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4bx19wPyQbmtwE5TRmKlkOz1UTx6c-zN7tpz349pLF182dBlGN8IVRT47k4tztBR6g5GIM3XQW9pYcOL41HRqd4yDJ3UiFTswpTwbVA9XP3qQkMYLz8H46HixAmg4-spuZW0zcU9hbCG4/s1600/Schermafbeelding+2017-12-22+om+16.16.11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="422" data-original-width="880" height="306" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4bx19wPyQbmtwE5TRmKlkOz1UTx6c-zN7tpz349pLF182dBlGN8IVRT47k4tztBR6g5GIM3XQW9pYcOL41HRqd4yDJ3UiFTswpTwbVA9XP3qQkMYLz8H46HixAmg4-spuZW0zcU9hbCG4/s640/Schermafbeelding+2017-12-22+om+16.16.11.png" width="640" /></a></div>
<br />
In the features document there is a paragraph about the List View Region.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi96A8CD19wrwSB2UGtUiraWVUSK9AoMx5wPOT5MUirESZFenkvS48YspgUJkRW5VpwqEqxIr1rxr7RJss2evMlc8UE642bKhRkV-o9MrnQg1CDN627299-da1r164MKDxGAYzGMwmZUDgl/s1600/Schermafbeelding+2017-12-22+om+16.16.38.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="260" data-original-width="1546" height="104" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi96A8CD19wrwSB2UGtUiraWVUSK9AoMx5wPOT5MUirESZFenkvS48YspgUJkRW5VpwqEqxIr1rxr7RJss2evMlc8UE642bKhRkV-o9MrnQg1CDN627299-da1r164MKDxGAYzGMwmZUDgl/s640/Schermafbeelding+2017-12-22+om+16.16.38.png" width="640" /></a></div>
<br />
Apparently we have to look into the Known issues. Here we find:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgt6MlNT9fN461s9j89-UEZFw9B4OwPdLXP_3KvkSPM0TeTpiKb7zR0IGY4feFBLgENrhD08kf9wUYv8V4u7BQW-YeOGqfQ9gKPk8PFbVfQe_y9lITk834H9n0M6-dDcpr0egl1HN0EthBs/s1600/Schermafbeelding+2017-12-22+om+16.16.54.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="358" data-original-width="1548" height="147" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgt6MlNT9fN461s9j89-UEZFw9B4OwPdLXP_3KvkSPM0TeTpiKb7zR0IGY4feFBLgENrhD08kf9wUYv8V4u7BQW-YeOGqfQ9gKPk8PFbVfQe_y9lITk834H9n0M6-dDcpr0egl1HN0EthBs/s640/Schermafbeelding+2017-12-22+om+16.16.54.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
OK, that's easy. So the link is copied and pasted in the page's CSS file attribute. But alas, the file cannot be found :-(.<br />
But it is visible that it is a JQuery mobile file. And of course that file can be found on the internet.<br />
Indeed, a reference is found:<br />
<br />
http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.css<br />
<br />
Unfortunately this does not work, because Chrome does not accept mixed content ( mixed being both http and https). Luckily we get a warning in the Browser Inspector, and the https equivalent is available, so we use:<br />
<br />
<b>https</b>://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.css<br />
<div>
<br /></div>
Now our page looks like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5DbrRIPEwqpuwVG4O0ucQ3gwKEYR5v65MB3Zv5-B5vLt1iSJewIf0X9DMy_ncwOGXayHR9aAqOxwOCt_5dH6aEBEaAeqpVT3V-0riHhKPBCwqIeQQEOq1yvI8p6Vemy8hsbx7CUZnLpMH/s1600/Schermafbeelding+2017-12-22+om+16.25.09.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1472" data-original-width="830" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5DbrRIPEwqpuwVG4O0ucQ3gwKEYR5v65MB3Zv5-B5vLt1iSJewIf0X9DMy_ncwOGXayHR9aAqOxwOCt_5dH6aEBEaAeqpVT3V-0riHhKPBCwqIeQQEOq1yvI8p6Vemy8hsbx7CUZnLpMH/s400/Schermafbeelding+2017-12-22+om+16.25.09.png" width="225" /></a></div>
Much better, isn't it? It can still be improved though:<br />
- there is a bit of wasted space to be recovered<br />
- the search item can be styled<br />
- the divider can use a bit more emphasis<br />
<br />
So a bit of CSS is added:<br />
<br />
<span style="color: #0b5394;">/* remove wasted white space */</span><br />
<span style="color: #0b5394;">.t-Body-contentInner {</span><br />
<span style="color: #0b5394;"> padding: 0;</span><br />
<span style="color: #0b5394;">}</span><br />
<span style="color: #0b5394;">html .ui-filterable + .ui-listview, html .ui-filterable.ui-listview {</span><br />
<span style="color: #0b5394;"> margin-top: 0em;</span><br />
<span style="color: #0b5394;">}</span><br />
<span style="color: #0b5394;">.ui-filterable {</span><br />
<span style="color: #0b5394;"> padding: 4px 12px;</span><br />
<span style="color: #0b5394;">}</span><br />
<span style="color: #0b5394;"><br /></span>
<span style="color: #0b5394;">/* style search item */</span><br />
<span style="color: #0b5394;">.ui-filterable input {</span><br />
<span style="color: #0b5394;"> border: 1px solid lightgrey;</span><br />
<span style="color: #0b5394;"> width: 100%;</span><br />
<span style="color: #0b5394;"> padding: 0px 12px;</span><br />
<span style="color: #0b5394;"> border-radius: 4px;</span><br />
<span style="color: #0b5394;">}</span><br />
<span style="color: #0b5394;"><br /></span>
<span style="color: #0b5394;">/* give list divider background color */</span><br />
<span style="color: #0b5394;">.ui-listview > .ui-li-divider {</span><br />
<span style="color: #0b5394;"> background-color: lightgrey;</span><br />
<span style="color: #0b5394;">}</span><br />
<br />
And now the page looks like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZFoC3RMZ7sOupLnztb7NL0n-9r7tgLDx9RahmXvFbe-ytdWGim7_N7foLfPs2uzJ__uOujcWwwi8pg71rtVDKCFYXmM6rTG6FwHSMlJbmn4hpRWiiTmRZ-fc3pqF-ZfyHvpIJ9DZjZzHc/s1600/Schermafbeelding+2017-12-22+om+16.29.09.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1472" data-original-width="828" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZFoC3RMZ7sOupLnztb7NL0n-9r7tgLDx9RahmXvFbe-ytdWGim7_N7foLfPs2uzJ__uOujcWwwi8pg71rtVDKCFYXmM6rTG6FwHSMlJbmn4hpRWiiTmRZ-fc3pqF-ZfyHvpIJ9DZjZzHc/s400/Schermafbeelding+2017-12-22+om+16.29.09.png" width="225" /></a></div>
<br />
I could not resist also styling the counter as a badge ;-).<br />
<br />
My conclusion is that the APEX development team are on the right track to replace the JQuery Mobile functionality.<br />
<br />
Happy Apexing!<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com3tag:blogger.com,1999:blog-4312362131290962824.post-14057333448607623672017-10-09T07:13:00.000+02:002017-10-09T07:22:59.954+02:00About a plug-in providing native Apex functionalityThis Saturday I published a plug-in on apex.world to create subheaders in a classic report. I got the idea while developing a long report and it seemed like a good idea to create a plugin for it to wrap and parameterize the JavaScript code. I searched the internet for the existence of such a plug-in and found nothing.<br />
So I set out to create the plug-in. After finishing it I wrote <a href="https://dickdral.blogspot.nl/2017/10/adding-subheaders-to-reports-easy-way.html" target="_blank">a blogpost </a>and added it to the plug-in repository on apex.world. And I proudly tweeted about it.<br />
Pretty soon I got a reply from Peter Raganitz: "<span style="background-color: #f5f8fa; color: #14171a; font-family: "helvetica neue" , "helvetica" , "arial" , sans-serif; font-size: 14px; white-space: pre-wrap;">Wouldn’t a control break do just that?". </span><br />
<span style="background-color: #f5f8fa; color: #14171a; font-family: "helvetica neue" , "helvetica" , "arial" , sans-serif; font-size: 14px; white-space: pre-wrap;"><br /></span>
<span style="background-color: #f5f8fa; color: #14171a; font-family: "helvetica neue" , "helvetica" , "arial" , sans-serif; font-size: 14px; white-space: pre-wrap;">I set out to check this and.. he is absolutely right. This functionality is available in Apex in a declarative way! </span><br />
<span style="background-color: #f5f8fa; color: #14171a; font-family: "helvetica neue" , "helvetica" , "arial" , sans-serif; font-size: 14px; white-space: pre-wrap;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCMjcOn4X8OzlbEVdkGnf-TNHzsOHc1Bv7OagYe3Ssjk2zQLcAFqBDUx6Zwe4nmZrDDC1pu7M0F6XkPrtZa2vnfYg962JGfIhTpgL8voXlwYIBzkX9FojdhIgvnapcRwOeuqzV6jXhbxQn/s1600/Schermafbeelding+2017-10-09+om+06.44.59.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="394" data-original-width="1086" height="116" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCMjcOn4X8OzlbEVdkGnf-TNHzsOHc1Bv7OagYe3Ssjk2zQLcAFqBDUx6Zwe4nmZrDDC1pu7M0F6XkPrtZa2vnfYg962JGfIhTpgL8voXlwYIBzkX9FojdhIgvnapcRwOeuqzV6jXhbxQn/s320/Schermafbeelding+2017-10-09+om+06.44.59.png" width="320" /></a></div>
<br />
<br />
<span style="background-color: #f5f8fa; color: #14171a; font-family: "helvetica neue" , "helvetica" , "arial" , sans-serif; font-size: 14px; white-space: pre-wrap;">The only extra needed is a small CSS definition for the visuals of the headers:</span><br />
<span style="background-color: #f5f8fa; color: #14171a; font-family: "helvetica neue" , "helvetica" , "arial" , sans-serif; font-size: 14px; white-space: pre-wrap;"><br /></span>
<br />
<div class="styles-section-title styles-selector" style="box-sizing: border-box; color: #303942; cursor: text; font-family: Menlo, monospace; font-size: 11px; min-height: 0px; min-width: 0px; word-wrap: break-word;">
<div style="box-sizing: border-box; min-height: 0px; min-width: 0px;">
<span class="selector" style="box-sizing: border-box; color: #888888; min-height: 0px; min-width: 0px;"><span class="simple-selector selector-matches" style="box-sizing: border-box; color: #222222; min-height: 0px; min-width: 0px;">.apex_report_break</span></span><span style="box-sizing: border-box; min-height: 0px; min-width: 0px;"> {</span></div>
</div>
<div class="style-properties matched-styles monospace" style="box-sizing: border-box; clear: both; color: #303942; display: flex; flex: 1 1 0%; font-family: Menlo, monospace; font-size: 11px; list-style: none; margin: 0px; min-height: 0px; min-width: 0px; padding: 2px 4px 0px 0px; white-space: nowrap; width: 573px;">
<div class="tree-outline-disclosure" style="box-sizing: border-box; display: inline-block; min-height: 0px; min-width: 100%;">
<ol class="tree-outline" role="tree" style="box-sizing: border-box; list-style-type: none; margin: 0px; min-height: 0px; min-width: 0px; padding: 0px; position: relative; z-index: 0;" tabindex="-1">
<li role="treeitem" style="align-items: center; box-sizing: border-box; clear: both; cursor: auto; display: block; margin-left: 0px !important; min-height: 14px; min-width: 0px; padding-left: 38px; position: relative; text-overflow: ellipsis; white-space: normal;"><input class="enabled-button" style="background-color: white; float: left; font-family: inherit; font-size: 10px; height: 13px; left: -40px; margin: 0px; min-height: 0px; min-width: 0px; position: relative; top: 1px; vertical-align: top; visibility: visible; width: 18px; z-index: 1;" type="checkbox" /><span class="styles-clipboard-only" style="box-sizing: border-box; display: inline-block; min-height: 0px; min-width: 0px; opacity: 0; pointer-events: none; white-space: pre; width: 0px;"> </span><span class="webkit-css-property" style="box-sizing: border-box; color: #c80000; margin-left: -38px; min-height: 0px; min-width: 0px;">background-color</span>: <span class="value" style="box-sizing: border-box; min-height: 0px; min-width: 0px;"><span is="color-swatch" style="box-sizing: border-box; min-height: 0px; min-width: 0px; white-space: nowrap;"><span class="color-swatch" style="background-image: url("Images/checker.png"); box-sizing: border-box; display: inline-block; height: 10px; line-height: 10px; margin-left: 1px; margin-right: 2px; min-height: 0px; min-width: 0px; position: relative; top: 1px; user-select: none; width: 10px;"><span class="color-swatch-inner" style="background-color: lightgrey; border: 1px solid rgba(128, 128, 128, 0.6); box-sizing: border-box; cursor: default; display: inline-block; height: 10px; min-height: 0px; min-width: 0px; width: 10px;"></span></span><span style="box-sizing: border-box; min-height: 0px; min-width: 0px;">lightgrey</span></span>!important</span>;</li>
<li role="treeitem" style="align-items: center; box-sizing: border-box; clear: both; cursor: auto; display: block; margin-left: 0px !important; min-height: 14px; min-width: 0px; padding-left: 38px; position: relative; text-overflow: ellipsis; white-space: normal;"><input class="enabled-button" style="background-color: white; float: left; font-family: inherit; font-size: 10px; height: 13px; left: -40px; margin: 0px; min-height: 0px; min-width: 0px; position: relative; top: 1px; vertical-align: top; visibility: visible; width: 18px; z-index: 1;" type="checkbox" /><span class="styles-clipboard-only" style="box-sizing: border-box; display: inline-block; min-height: 0px; min-width: 0px; opacity: 0; pointer-events: none; white-space: pre; width: 0px;"> </span><span class="webkit-css-property" style="box-sizing: border-box; color: #c80000; margin-left: -38px; min-height: 0px; min-width: 0px;">font-weight</span>: <span class="value" style="box-sizing: border-box; min-height: 0px; min-width: 0px;">bold</span>;</li>
<li aria-expanded="false" class="parent" role="treeitem" style="align-items: center; box-sizing: border-box; clear: both; cursor: auto; display: block; margin-left: 0px !important; min-height: 14px; min-width: 0px; padding-left: 38px; position: relative; text-overflow: ellipsis; white-space: normal;"><input class="enabled-button" style="background-color: white; float: left; font-family: inherit; font-size: 10px; height: 13px; left: -40px; margin: 0px; min-height: 0px; min-width: 0px; position: relative; top: 1px; vertical-align: top; visibility: visible; width: 18px; z-index: 1;" type="checkbox" /><span class="styles-clipboard-only" style="box-sizing: border-box; display: inline-block; min-height: 0px; min-width: 0px; opacity: 0; pointer-events: none; white-space: pre; width: 0px;"> </span><span class="webkit-css-property" style="box-sizing: border-box; color: #c80000; margin-left: -38px; min-height: 0px; min-width: 0px;">padding</span>: <span class="expand-icon spritesheet-smallicons smallicon-triangle-right icon-mask" is="ui-icon" style="--spritesheet-position: 0px 10px; -webkit-mask-image: -webkit-image-set(url("Images/smallIcons.png") 1x, url("Images/smallIcons_2x.png") 2x); -webkit-mask-position: var(--spritesheet-position); background-color: #6e6e6e; box-sizing: border-box; display: inline-block; flex-shrink: 0; height: 10px; margin-bottom: -2px; margin-left: -6px; margin-right: 2px; min-height: 0px; min-width: 0px; user-select: none; width: 10px;"></span><span class="value" style="box-sizing: border-box; min-height: 0px; min-width: 0px;">8px 12px</span>;</li>
<li aria-expanded="false" class="parent" role="treeitem" style="align-items: center; box-sizing: border-box; clear: both; cursor: auto; display: block; margin-left: 0px !important; min-height: 14px; min-width: 0px; padding-left: 38px; position: relative; text-overflow: ellipsis; white-space: normal;"><input class="enabled-button" style="background-color: white; float: left; font-family: inherit; font-size: 10px; height: 13px; left: -40px; margin: 0px; min-height: 0px; min-width: 0px; position: relative; top: 1px; vertical-align: top; visibility: visible; width: 18px; z-index: 1;" type="checkbox" /><span class="styles-clipboard-only" style="box-sizing: border-box; display: inline-block; min-height: 0px; min-width: 0px; opacity: 0; pointer-events: none; white-space: pre; width: 0px;"> </span><span class="webkit-css-property" style="box-sizing: border-box; color: #c80000; margin-left: -38px; min-height: 0px; min-width: 0px;">border</span>: <span class="expand-icon spritesheet-smallicons smallicon-triangle-right icon-mask" is="ui-icon" style="--spritesheet-position: 0px 10px; -webkit-mask-image: -webkit-image-set(url("Images/smallIcons.png") 1x, url("Images/smallIcons_2x.png") 2x); -webkit-mask-position: var(--spritesheet-position); background-color: #6e6e6e; box-sizing: border-box; display: inline-block; flex-shrink: 0; height: 10px; margin-bottom: -2px; margin-left: -6px; margin-right: 2px; min-height: 0px; min-width: 0px; user-select: none; width: 10px;"></span><span class="value" style="box-sizing: border-box; min-height: 0px; min-width: 0px;">1px solid <span is="color-swatch" style="box-sizing: border-box; min-height: 0px; min-width: 0px; white-space: nowrap;"><span class="color-swatch" style="background-image: url("Images/checker.png"); box-sizing: border-box; display: inline-block; height: 10px; line-height: 10px; margin-left: 1px; margin-right: 2px; min-height: 0px; min-width: 0px; position: relative; top: 1px; user-select: none; width: 10px;"><span class="color-swatch-inner" style="background-color: lightgrey; border: 1px solid rgba(128, 128, 128, 0.6); box-sizing: border-box; cursor: default; display: inline-block; height: 10px; min-height: 0px; min-width: 0px; width: 10px;"></span></span><span style="box-sizing: border-box; min-height: 0px; min-width: 0px;">lightgrey</span></span></span>;</li>
<li role="treeitem" style="align-items: center; box-sizing: border-box; clear: both; cursor: auto; display: block; margin-left: 0px !important; min-height: 14px; min-width: 0px; padding-left: 38px; position: relative; text-overflow: ellipsis; white-space: normal;"><input class="enabled-button" style="background-color: white; float: left; font-family: inherit; font-size: 10px; height: 13px; left: -40px; margin: 0px; min-height: 0px; min-width: 0px; position: relative; top: 1px; vertical-align: top; visibility: visible; width: 18px; z-index: 1;" type="checkbox" /><span class="styles-clipboard-only" style="box-sizing: border-box; display: inline-block; min-height: 0px; min-width: 0px; opacity: 0; pointer-events: none; white-space: pre; width: 0px;"> </span><span class="webkit-css-property" style="box-sizing: border-box; color: #c80000; margin-left: -38px; min-height: 0px; min-width: 0px;">font-size</span>: <span class="value" style="box-sizing: border-box; min-height: 0px; min-width: 0px;">12px</span>;</li>
</ol>
</div>
</div>
<div class="sidebar-pane-closing-brace" style="box-sizing: border-box; clear: both; color: #303942; font-family: Menlo, monospace; font-size: 11px; min-height: 0px; min-width: 0px; white-space: nowrap;">
}</div>
<div class="sidebar-pane-closing-brace" style="box-sizing: border-box; clear: both; color: #303942; font-family: Menlo, monospace; font-size: 11px; min-height: 0px; min-width: 0px; white-space: nowrap;">
<br /></div>
<div class="sidebar-pane-closing-brace" style="box-sizing: border-box; clear: both; color: #303942; font-family: menlo, monospace; min-height: 0px; min-width: 0px; white-space: nowrap;">
<div style="font-size: 11px;">
<br /></div>
</div>
<div class="sidebar-pane-closing-brace" style="box-sizing: border-box; clear: both; min-height: 0px; min-width: 0px;">
<div style="color: #303942; font-family: menlo, monospace; font-size: 11px; white-space: nowrap;">
<span style="background-color: #f5f8fa; color: #14171a; font-family: "helvetica neue" , "helvetica" , "arial" , sans-serif; font-size: 14px; white-space: pre-wrap;">There are just two differences with the plug-in:</span></div>
<ol>
<li>The first heading stays above the break title. You can see this behavior on my demo site</li>
<li>The plug-in functions correctly in combination with my Report2columns plug-in, as shown in the previous blogpost ( this should probably be seen as a flaw in the Report2columns plug-in )</li>
</ol>
<div class="sidebar-pane-closing-brace" style="box-sizing: border-box; clear: both; color: #303942; font-family: menlo, monospace; min-height: 0px; min-width: 0px; white-space: nowrap;">
<div style="font-size: 11px;">
<b style="color: #14171a; font-family: "helvetica neue", helvetica, arial, sans-serif; font-size: 14px; white-space: pre-wrap;">Lessons learned</b></div>
</div>
<div class="sidebar-pane-closing-brace" style="box-sizing: border-box; clear: both; min-height: 0px; min-width: 0px;">
</div>
<div style="color: #303942; font-family: menlo, monospace; font-size: 11px; white-space: nowrap;">
<span style="background-color: #f5f8fa; color: #14171a; font-family: "helvetica neue" , "helvetica" , "arial" , sans-serif; font-size: 14px; white-space: pre-wrap;">What did I learn from this: </span></div>
</div>
<div class="sidebar-pane-closing-brace" style="box-sizing: border-box; clear: both; min-height: 0px; min-width: 0px;">
<ol>
<li><span style="color: #14171a; font-family: "helvetica neue" , "helvetica" , "arial" , sans-serif;"><span style="background-color: #f5f8fa; font-size: 14px; white-space: pre-wrap;">We have got a great Oracle Apex community, where you get almost instant feedback. Thank you Peter for noticing and taking the effort to send me the message :-). </span></span></li>
<li><span style="color: #14171a; font-family: "helvetica neue" , "helvetica" , "arial" , sans-serif;"><span style="background-color: #f5f8fa; font-size: 14px; white-space: pre-wrap;">Research better before building a solution. Because there are a lot of ways to create functionality it is possible to end up with a suboptimal solution. So next time I will look better in the native functionality of Apex</span></span></li>
<li><span style="color: #14171a; font-family: "helvetica neue" , "helvetica" , "arial" , sans-serif;"><span style="background-color: #f5f8fa; font-size: 14px; white-space: pre-wrap;">Oracle Apex has got a lot of great functionality out of the box ( of course I knew that already ;-) )</span></span></li>
</ol>
<div>
<span style="color: #14171a; font-family: "helvetica neue" , "helvetica" , "arial" , sans-serif;"><span style="font-size: 14px; white-space: pre-wrap;"><br /></span></span></div>
</div>
Dick Dralhttp://www.blogger.com/profile/01635691679860836567noreply@blogger.com0