Before I start, this is my first blog so sorry for the poor formatting.


Summary and Background: At a client we were implementing a customized version of the My Timesheets v2. We did not have the latest version of the UI5 libraries and were not able to upgrade them. The standard app uses a DateTimeInput control which renders as a picker on both Desktop and mobile devices. This is a pretty poor UX on desktop since it is much easier to type than try and use the scroll wheel to go through numbers. So the new requirement was to allow text input on the desktop and picker use on the mobile. Very quickly we saw that there was a control for this, TimePicker, and in fact that the control used in the standard app is deprecated. So we made this change in our local apps (running in Eclipse/Tomcat environment) and it worked fine! However as soon we uploaded the app to the Gateway server it would not render. We realized our mistake was that our Eclipse environment had a new library version (which had the Timepicker control) and the gateway server had an older version without the control. We did not want to write a control ourselves, so we tried to extract the control for the library in Eclipse, and include it in our custom app. Obviously we succeeded or this post would just say ‘you can’t do it sorry folks.’ Doing this method has a few benefits over writing a custom control 1) Its less work (and lets be honest that’s what all developers want) 2) When the library version is finally upgraded it is a very simple process to have your app use the standard code again and remove these smalls customizations, with no impact to UI/UX for the end users (assuming you change from a custom control to a standard one upon upgrade)

Note: This post does assume use of a custom theme in order to work some CSS voodoo, you should be able to just include the custom CSS in your app but I have not done that before I just use theme designer so if you try this yourself and don’t use theme designer let me know how you did it.


Procedure:

  1. First, we need actual JS files that allow this control to function. To extract them, we added the control to our view in our Eclipse project. In this example all we did was replace the XML which before said ‘DateTimePicker’ with ‘TimePicker’ and modified the attributes slightly so the Timepicker rendered how we wanted (it took 1 new formatting parameter than the old control). We then save our project (which should be deployed on a tomcat server or similar).
  2. We open Google Chrome, and open the developer tools window, then access our app. Navigate to the screen which the new control will be rendered on. Access the network tab in dev tools, and you will see the individual .js files for all the controls being downloaded. You will see one with the same name as your control, in this case TimePicker.js Click in the file to open it in a preview window. Use ‘Pretty Print’ to open a TimePicker.js:formatted file. Copy the code and paste it into your own TimePicker.js
  3. The code should be in one line (if you did not use pretty print as recommended above), you will need to space it out to see from the ‘define’ statements what other js files are using by the main control js file. In this case we can see that MaskInput, MaskInputRule, and TimePickerSliders are needed, as well as some base files which should still be in our older UI5 Lib version. So we need to go back to the network tab and copy those files out and create our own local version of them. If you examine their files you will see some more that are needed. You can also just look at the network tab and see that after TimePicker is loaded there are about 8 other .js files that are loaded. We could determine this by deploying our app with these custom files to our ABAP server and checking the console for errors as each time the app would try and load a new .js file that isn’t there, but we can generally tell what .js files are needed on the first try and if we miss one, simply add it using the steps detailed below. All in all we end up needing the following files: MaskInput, MaskInputRule, TimePicker, TimePickerRenderer, TimePickerSlider, TimePickerSliderRenderer, TimePickerSliders, TimePickerSlidersRenderer. TimePickerRender is the only one not explicitly ‘defined’ in one of the other files, but we see it is downloaded in the network tab, or we could leave it out and try to deploy the app without out to ABAP server and quickly see we need to have it. Regardless we now have local copies of these 8 files (ideally pretty printed to readable format).
  4. Now we need to add these files to our project. We created a new directory called ‘control’ and add these .js files to them.
  5. Now we need to modify the xml view that we added the control to previously. The current setup will look in the default XML namespace which is sap.m library of UI5, we need it to point to our control folder so we add a new namespace to the xml and modify the XML to use it for the TimePicker
    1. Namespace in our example: xmlns:hcm.mytimesheet.Z_HCM_TSH_MAN.control=“hcm.mytimesheet.Z_HCM_TSH_MAN.control
    2. We delete the standard code:<DateTimeInput id=”startTime” change=”validate” type=”Time” value=”{entry>/startTime valueFormat=”HHmmss” placeholder=”{i18n>FROM}”> </DateTimeInput><DateTimeInput id=”endTime” change=”validate” type=”Time” value=”{entry>/endTime}” valueFormat=”HHmmss” placeholder=”{i18n>TO}”> </DateTimeInput>
    3. We add this custom code:<hcm.mytimesheet.Z_HCM_TSH_MAN.control:TimePicker id=”startTime” change=”validate” type=”Time” value=”{entry>/startTime}” displayFormat=”HH:mm” valueFormat=”HHmmss” placeholder=”{i18n>FROM}”> </hcm.mytimesheet.Z_HCM_TSH_MAN.control:TimePicker><hcm.mytimesheet.Z_HCM_TSH_MAN.control:TimePicker id=”endTime” change=”validate” type=”Time” value=”{entry>/endTime}” displayFormat=”HH:mm” valueFormat=”HHmmss” placeholder=”{i18n>TO}”> </hcm.mytimesheet.Z_HCM_TSH_MAN.control:TimePicker>
  6. So now our app will look in our added folder for the TimePicker file! But, due to the structure of the ‘define’ statements in the TimePicker.js (and other files) it will still fail. We need to make two types of modifications. There are files that were located in sap.m in the new library version that are now located in this directory we need to make sure their directory path is ‘./’ There are files that were located in the same directory as TimePicker.js in the newer library version, but we need to continue to fetch from sap.m so we need to change the path to ‘sap/m/’ whereas before they were pointing to current directory which would have been sap/m/ but is now our app’s control folder. So for all 8 files make the below changes:
    1. Is the .js file located in the control directory? Make sure file path is ‘./<X>’
    2. Is the .js file located in the standard library? Make sure the file path is ‘sap/m/<X>
    3. Example from TimePicker.js –  see all custom files are ./ all standards are sap/m : sap.ui.define([ ‘jquery.sap.global’, ‘sap/m/InputBase’, ./MaskInput’, ./MaskInputRule’, ‘sap/m/ResponsivePopover’, ‘sap/ui/core/EnabledPropagator’,  ‘sap/ui/core/IconPool’, ‘sap/ui/model/type/Time’,’./TimePickerSliders’ ],
  7. In added .js files make sure any ‘require’ statements for files that exist in the added ‘control’ directory have the correct namespace this is: ‘hcm.mytimesheet.Z_HCM_TSH_MAN.control.’ This only occurred in the TimePicker.js file, changes below:
    1. q.sap.require(‘hcm.mytimesheet.Z_HCM_TSH_MAN.control.MaskInputRule’);

    2. q.sap.require(‘hcm.mytimesheet.Z_HCM_TSH_MAN.control.MaskInput’);
  8. The TimePickerRenderer is not explicitly required and set as the TimePicker’s Renderer in the TimePicker.js code (as mentioned in step 3), so when the TimePicker is rendered in the browser one of the core UI5 functions generates a require statement based on the TimePicker’s class. The standard class is sap.m.TimePicker, with this the core function generates a require statement for sap.m.TimePickerRenderer which fails due to the renderer being in the custom control folder of the app, not the sap.m lib. So we need to change the class name to be in the namespace of the custom folder. Then the code will auto generate a require statement with the correct path.This took far longer than I care to admit to figure out.
    1. Change this line: var d = M.extend(‘sap.m.TimePicker’, {
    2.   to: var d = M.extend(‘hcm.mytimesheet.Z_HCM_TSH_MAN.control.TimePicker’,{
  9. At this point the view with the TimePicker will load without error, however the .css needed for the control to function properly has not been added to the project yet. The text input works but if you open the picker it is shown as plain text and you cannot select anything. We extract this .css (all classes with sapMTimePicker) and then we add this custom CSS to a custom theme. Check the prerequisite section on how to add a custom theme to your app if you did not already
  10. First we extract the needed css from the standard library
    1. Run the app again on your local server that has the newer library version
    2. Access (in chrome) the view that contains the TimePicker, open the picker and right click and select inspect this will open the html for it in the dev tools window
    3. In the chrome dev tools window click an html line, on the right hand side you can view the css classes related to that html element and there will be a link to library.css where the classes for these elements are. Click that link to view the .css file in Chrome Dev Tools Sources tab and see the file with the classes we need.
    4. Use the chrome pretty print button (‘{}’ in the bottom left of dev tools Sources tab) to view the code in a readable format in a new tab. Side Note: you can use pretty print on single line js files like sap core libs to set breakpoints that can really help debug, this is how I was able to find the issue with TimePickerRenderer having its require statement auto generated
    5. Copy and paste the file into a code editor like NotePad++, extract the classes that are related to sapMTimePicker
      1. I just ctrl+F’d for anything with that in it, which I saw was the class in the html in the screen shot above
    6. Access the NWG server and launch the theme designer
    7. Access your custom theme and open for editing
    8. Click the custom css tab and paste in the classes just extracted
    9. Save and build the theme
  11. Now the control will render when the app is launched with the custom theme parameter in the URL. The only issue is text like ‘Ok,’ ‘Hours,’ etc. will not be displayed correctly. As there are .getText() call which reference entries in the sap.m resource bundle which do not exist in the lower version of the library being used. So the .getText calls need to be replaced with correct strings. Alternatively you can modify the new .js files to point to a custom resource bundle.
    1. TimePicker.js
      1. Remove/Comment:
        1. //O = r.getText(‘TIMEPICKER_SET’);
        2. //C = r.getText(‘TIMEPICKER_CANCEL’);
      2. Add
        1. O = “OK”;
        2. C = “Cancel”;
    2. TimePickerRenderer.js
      1. Remove/Comment
        1. //t = o.getText(‘TIMEPICKER_SCREENREADER_TAG’);
      2. Add
        1. t = “Time Picker”;
    3. TimePickerSliders.js
      1. Remove/Comment
        1. //var r = sap.ui.getCore().getLibraryResourceBundle(

          //’sap.m’), l = r.getText(‘TIMEPICKER_LBL_HOURS’), L = r

          //        .getText(‘TIMEPICKER_LBL_MINUTES’), s = r

          //        .getText(‘TIMEPICKER_LBL_SECONDS’), b = r

          //        .getText(‘TIMEPICKER_LBL_AMPM’);

      2. Add
        1. var r = sap.ui.getCore().getLibraryResourceBundle(‘sap.m’),

          l = “Hours”,

          L = “Minutes”,

          s = “Seconds”,

          b = “AM/PM”;

  12. The control should now render correctly in your app!

How to Undo These Changes When You Upgrade Your Library

  1. This is pretty easy, just remove the custom name space from the XML view, and make sure to change the actual part of the XML view that invokes the control to use the standard namespace.
  2. Remove the ‘control’ directory and all associated files inside
  3. Remove custom .css from the theme
  4. BAM your app is now just rendering the standard control again with no changes to the UI/UX!



This was a specific example but this idea should be applicable to any control you want to use.

If anyone else has accomplished something like this before in an easier way I’d love to hear!

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply