Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
avital_ben-natan
Explorer

In this post I would like to introduce UI5 Playground, which I hope will help UI5 developers as much as it has helped me throughout my projects revolving UI5 (SAPUI5/OpenUI5).

The project aims to make it easier to develop applications with UI5  by abstracting away many of the repetitive programming tasks related to MVC, routing, querying views/DOM, working with data, etc…, and offering solutions to common development scenarios in UI5.

It presents the topics discussed and presented in the TDG (The Definitive Guide) application and extends on them.



Why play?

Avoid CORS for RAD (rapid application development) in client


To start developing simply cmd from project root and run

$ grunt server

This project is built using "generator-sapui5". One of the really cool things you'll find in the grunt.js file is how to use "grunt-connect-proxy" for your local or CI flows inside "connect.proxies", approx. line 77.

The great thing about the connect-proxy is that you can work off a local web server and still talk 3rd party domains, as you would your application host.


This reverse proxying is very useful in development and continuous integration testing where you want to test your new client code against currently deployed services. it allows you to test against real services without deploying a server especially for testing.

In production it allows you great flexibility in that you can host the client and reverse proxy in one server and decide during request time where you'd like to proxy the requests from. This allows for true separation of client from server.

With this project we are using Node.js as the server with grunt-connect-proxy. In a later post I will address the benefits of using Node.js in development and CI, regardless of production environment. In production you can use the great open source component by SAP, cloud-connectivityproxy of you're using a Java server. In Apache there is the "mod_proxy" extension which implements this mechanism.

In this project there is also an experimental fall back method that is browser based (no server proxying) in the index file, line 27 there is a public CORS server request hijacker. This is of course for fun and hacking, I would not use this on a production app. If you're working with Brackets or Webstorm you can just run index.html in browser (these IDEs spin up a local webserver to serve your web application) and it'll just work (accessing NorthWind OData). In fact when you run the project online from GitHub pages, the app will make use of corsproxy.com via the hijacker as I could not reverse proxy from Github. before highjacking the request to 3rd party domains we’re checking if the application host will also serve the 3rd party api calls, and upon failure we fallback to highjacking the requests from the browser.

Search OData


Things like searching on an OData bound list or table become as easy as (demo here). This is instrumented with pure OData, no search provider is applied.

oBinding = oList.getBinding("items"); 

oBinding.x_search(["Name","Description", "Category/Name", "Supplier/Name"], "cheese");

 

"x_search" makes use of another new function on the prototype, "x_addFilter". Filters added to the binding in this way are static, so you may filter the list further, like this for example with the view settings dialog, without losing your previous search query until you execute the same command with an empty query string.

oList.x_search(["..."], ""); 

Avoid boilerplate code (redundant code)

Instead of this boilerplate for each controller in your application

sap.ui.core.mvc.Controller.extend("ui.demo.view.Detail", { 

onInit : function() { 

  var router = sap.ui.core.UIComponent.getRouterFor(this); 

  router.attachRouteMatched(this.onRouteMatched, this); 

  }, 

onRouteMatched : function(oEvent) { 

  var oParameters = oEvent.getParameters(); 

  if (oParameters.name !== "product") { 

       return; 

  } 

// Route matched logic here 

} 

}); 

With the help of  "ui5lib.base.Controller" the above can be written as such:

ui5lib.Controller.extend("ui.demo.view.Detail", { 

     onMyRouteMatched : function(oEvent) { 

       // Route matched logic here 

     } 

}); 


Get i18n texts


Using “ui5lib.base.Controller” use the following call to get the i18n bundle defined on your component.


oController.getText(key, params)


instead of


var sComponentId = sap.ui.core.Component.getOwnerIdFor(oController.getView());

var i18nBundle =  sap.ui.component(sComponentId).getModel("i18n").getResourceBundle();

i18nBundle.getText(key, params);


Use standard model inheritance for fragments and fragment-dialogs


You may have noticed that fragments and dialogs do not enjoy model inheritance like regular controls. And you might have run into a few hicks trying to use a component, while still having models on core (sap.ui.getCore()). The model inheritance is not straightforward in that dialogs do not inherit models and fragments on view within a component do not inherit models from core.


You can achieve straightforward model inheritance in one the following ways:


var oDialogFragment = sap.ui.jsfragment("testdata.fragments.JSFragmentDialog");

oDialogFragment.setModel(this.getView().getModel());

oDialogFragment.open();


or


var oDialogFragment = sap.ui.jsfragment("testdata.fragments.JSFragmentDialog");

this.getView().addDependent(oDialogFragment);

oDialogFragment.open();


Using “ui5lib.ui5x”, this becomes a simple call that will cater to fragments and dialog content, attaching them to the view of the Controller from which they are spawned.


var oDialogFragment = sap.ui.jsfragment("testdata.fragments.JSFragmentDialog", oController);


Data driven tiles UI and dynamic routing


One of the nice things in the UI5Playground app is that it implements a draft of dynamic routing in “MyRouter.js”. Meaning that if you use a sane file structure (in my opinion1,2), you get routing for free. you’ll notice I have only defined the initial route and the notfound route in my component.js, and yet routing works as expected from the tile and from the address bar or history. All of the demos presented in the Playground are routed by convention. It works nicely from hash-change and navigation events alike.


Note that if not used wisely this can impact performance (see performance issues and roadmap below).


This allows for great flexibility in demoing/testing features by just pasting a directory containing all of the files for that feature into the views directory (I often do this to tinker around with the code behind samples in the demokit). The dynamic routing will create a registred resource and the corresponding route on the fly. One thing to note is that naming convention required for dynamic routing to the resources inside your feature directory, “feature-dir-name.resourcename”. For example, “Product.Master” or “Product.ViewSettings” where “Product” is the name of the feature directory.


The reason I’m discussing data driven tiles with dynamic routing is that this encapsulates a pattern I think is important,

keep the client (web app) on a need to know basis.

The UI should be generated with only the information relevant to the current user. Now imagine you have a cloud application with multiple tenants or role based access and each has access to different features. With UI5 Playground the tiles a generated from JSON that can be rendered by a service, and there is no client file that suggests other features that may be available (routes config) such as the best practice presented in “The Definitive Guide

Now, you may argue that the routes config in the component.js can be rendered on server and you would be right, but in the interest of DRY, why repeat yourself in the routes config when you have already defined the root features available to your client for the initial view of the application.

A mixed approach may very well be the way go here in terms of DRY, SoC, and performance. use dynamic routing for all flat navigations from the root of your applications, perhaps one level more where it seems appropriate and render the rest of your routes from a service. Predefined routes are always used first, a dynamic route will only be attempted when there is no matching route for a given navigation.


Etc, etc

Some plumbing

If you’ve dived into the demo you may have noticed that the UI5 Playground uses another repository “ui5lib” intensively. In the SoC spirit the code in your application should only implement your unique business logic the rest should be handled by the framework UI5 or extensions (hence ui5lib). This is a collection of addon libraries and components to assist development of UI5 applications. Keep in mind this a work in progress, script tags in the HTML is not really the best way to consume web resources.


Oh, and

There are more helpers and extensions in ui5lib such as


  • JSONModel with detailed data changed events, delete and add record methods, etc…
  • Helpers like oElement.getBoundProperty() which returns
      var context = this.getBindingContext();
      returns context.oModel.getProperty(context.sPath+(!!sPath? ('/'+sPath): ''));
  • Prototypal extension of ODataListBinding to support static filters
  • More…


Notes

Please refer to the project page at Github

Roadmap

Please refer to the project page at Github

1 Comment