Skip to Content
Product Information

What’s new in Mobile development kit client 4.0 SP01

I am happy to announce that a new release of the Mobile Development Kit is available for all Mobile Services customers and can be downloaded on the SAP Software Center (and also available on community Download page).

This release adds incremental functionality over the previous MDK Client 4.0 release.

 

 

Mobile Services Client is also available in Apple AppStore and Google Play Store.

SAP Mobile development kit (MDK) extends SAP Cloud Platform Mobile Services and SAP Web IDE to provide you with a complete set of mobile application development and management tools, onboarding, offline support, and central lifecycle management. It offers a metadata-driven approach to create native supported applications so no experience of creating iOS or Android apps is required.

The main focus of this release are:

New UI control: Profile Header

We now support Profile Header control conforming Fiori for iOS & Android guideline.

Profile header helps the user recognize and learn more about a person.

[Updated 10.01.2020] : Profile Header control is now available under available control in Section page in MDK page editor

"Sections":[ 
   { 
      "ProfileHeader":{ 
         "ActivityItems":[ 
            { 
               "ActivityType":"Phone",
               "ActivityValue":"{PhoneNumber}"
            },
            { 
               "ActivityType":"VideoCall",
               "ActivityValue":"{PhoneNumber}"
            },
            { 
               "ActivityType":"Email",
               "ActivityValue":"{EmailAddress}"
            },
            { 
               "ActivityType":"Detail",
               "ActivityValue":"This is an alert"
            }
         ],
         "Description":"{CustomerId}",
         "DetailImage":"/DemoProj/Images/contact-cell.png",
         "DetailImageIsCircular":true,
         "Headline":"{{#Property:FirstName}} {{#Property:LastName}}",
         "Styles":{ 
            "BackgroundColor":"ProfileHeaderBackgroundColor",
            "Description":"ProfileHeaderDescription",
            "Headline":"ProfileHeaderHeadline",
            "Subheadline":"ProfileHeaderSubheadline"
         },
         "Subheadline":"{City}"
      },
      "Visible":true,
      "_Name":"Control0",
      "_Type":"Section.Type.ProfileHeader"
   }
],
"_Name":"SectionedTable",
"_Type":"Control.Type.SectionedTable"
}

Check the documentation for more details.

Support rendering image via HTTP URL

We now support rendering image via https URL for values in image properties e.g. DetailImage in ContactCell.

 

Control Attribute in control Documentation Reference
ObjectHeader DetailImage, StatusImage, SubstatusImage Reference
ProfileHeader DetailImage Reference
ObjectCell DetailImage, Icons, StatusImage, SubstatusImage Reference
ContactCell DetailImage Reference
GridRowItem Image Row Item
ImageCell Image Reference

Here in ProfileHeader control

 "DetailImage": "https://icon-library.net/images/0234605a9c_92936.png",

We now have introduced a new property “ClearHistory” in Navigation Action to clear the history stack.  It accepts a Boolean value. Default value is false.

If set to true, the navigation back-stack should be cleared and after the navigation action is completed the back button should not be visible anymore in the target page (because the backstack is now empty).

{ 
   "ClearHistory":true,
   "PageToOpen":"/DemoProj/Pages/Customers/Customers_List.page",
   "_Type":"Action.Type.Navigation"
}

Check the documentation for more details.

Support Stateful OData Services

You should be now able to handle and store the stateful services session so that session info can be easily retrieved and can be used.

A new property is available in Service > OnlineOptions > ServiceOptions > StatefulService (type: boolean)

If set to true, the session information returned from the CallFunction API is automatically stored in #Application/#ClientData/#Property:Session/#Property:[SessionPropertyName] for subsequent use in actions or rules.

Example:

#Application/#ClientData/#Property:Session/#Property:SessionID

 

Check the documentation (Online Service Options) for more details.

 

Support OData CreateRelatedMedia action

Create Related Media action is used to create a media enity(an OData EntityType with HasStream=”true” attribute). The new media entity is linked to its parent entity(an entity has a navigation property linking to the new media entity)

 

e.g. “Image” EntityType is a Media Entity and it has an associated media stream:

<EntityType Name="Image" HasStream="true">
<Key>
<PropertyRef Name="id"/>
</Key>
<Property Name="id" Type="Edm.Int64" Nullable="false"/>
<Property Name="label" Type="Edm.String" Nullable="true"/>
<Property Name="created" Type="Edm.Date" Nullable="true"/>
<Property Name="updated" Type="Edm.Date" Nullable="true"/>
<NavigationProperty Name="artist" Type="Self.Artist"/>
</EntityType>

Parent EntityType “Artist” has a navigation property linking to “Image”:

<EntityType Name="Artist">
<Key>
<PropertyRef Name="id"/>
</Key>
<Property Name="id" Type="Edm.Int64" Nullable="false"/>
<Property Name="firstName" Type="Edm.String" Nullable="false"/>
<Property Name="lastName" Type="Edm.String" Nullable="false"/>
<Property Name="dateOfBirth" Type="Edm.Date" Nullable="true"/>
<NavigationProperty Name="images" Type="Collection(Self.Image)"/>
</EntityType>

{
  "_Type": "Action.Type.ODataService.CreateRelatedMedia",
  "Target" : {
    "EntitySet" : "Images",
    "Service" : "/AssetWorkManager/Services/Amw.service"
  },
  "Properties" : {
    "ClassName": "#CurrentPage/#Control:ClassName/#Value",
    "ClassType": "#CurrentPage/#Control:ClassType/#Value",
    "ObjectKey": "#CurrentPage/#Control:ObjectKey/#Value",
    "FileName": "#CurrentPage/#Control:FileName/#Value"
  },
  "ParentLink": {
    "Property": "images",
    "Target": {
      "EntitySet": "Artists",
      "ReadLink": "{@odata.readLink}"
    }
  },
  "Headers" : {
     "slug" : {
        "ClassName": "#CurrentPage/#Control:ClassName/#Value",
        "ClassType": "#CurrentPage/#Control:ClassType/#Value",
        "ObjectKey": "#CurrentPage/#Control:ObjectKey/#Value",
        "FileName": "#CurrentPage/#Control:FileName/#Value"
    }
  },
  "IsOnlineRequest": true,
  "Media":"#CurrentPage/#Control:Attachment/#Value",
  "OnSuccess" : "/AssetWorkManager/Actions/ODataCreateEntitySuccessMessage.action",
  "OnFailure": "/AssetWorkManager/Actions/ODataCreateEntityFailureMessage.action" 
}

 

Note:

CreateMedia action: creates a new media entity against its own entity set. This action does not support linking to another existing entity set.

CreateRelatedMedia action creates the new media entity against the navigation property of an existing entity with which the relationship is to be established.

Check the documentation for more details.

Support OData CreateRelatedEntity action

CreateRelatedEntity action is used to create an entity in the target system, related to a parent entity via a parent navigation property.

{
  "_Type": "Action.Type.ODataService.CreateRelatedEntity",
  "Target": {
    "EntitySet": "SalesOrderHeaders",
    "Service": "/MDKDevApp/Services/OnlineSampleService.service"
  },
  "Properties": {
    "SalesOrderId": "/MDKDevApp/Rules/GetSalesOrderId.js"
  },
  "ParentLink": {
    "Property": "SalesOrders",
    "Target": {
      "EntitySet": "Customers",
      "ReadLink": "{@odata.readLink}"
    }
  },
  "ActionResult": {
  	"_Name": "OData"
  },
  "OnSuccess": "/MDKDevApp/Actions/Messages/Success.action",
  "OnFailure": "/MDKDevApp/Actions/Messages/Failure.action"
}

Note:

CreateEntity (with CreateLinks) action: creates the new entity against its own entity set. The entity is then linked or bound to an existing entity with which the relationship is to be established.

CreateRelatedEntity action creates the new entity against the navigation property of an existing entity with which the relationship is to be established.

Check the documentation for more details.

Add metadata function to choose between platform specific values

You can now format things like date, time, currency etc that returns the appropriate value based on the runtime platform (iOS or Android).

$(PLT, <iOS>, <Android>)

If the platform is iOS the first parameter is returned.

If the platform is Android the second parameter is returned

If the parameter is blank then the property where the function is evaluated should behave as if the property is not specified.

It supports the following value types:

  1. $(PLT, ‘ABC’, true) <- in iOS return string ‘ABC’, in Android return boolean true
  2. $(PLT, 4, ‘false’) <- in iOS return number 123.4, in Android return a string ‘false’
  3. $(PLT, ‘123.4’, null) <- in iOS return string ‘123.4’, in Android return a null
  4. $(PLT, {MyProperty}, {\{#SomeTargetPath/#Property:SomeProperty}}) <- in iOS return string value of MyProperty binding, in Android return value of #SomeTargetPath/#Property:SomeProperty

 

Check the documentation for more details.

Support sending authenticated GET and POST request to SAP Cloud Platform Mobile Services

We already supported to get the deviceID & UserID for the registered user. Now, you can also get the SAP Cloud Platform Mobile Services AppID and server endpoint.

Once you get the Mobile Services URL and AppID, you can construct a full URL to access Mobile Services REST API/destinations via clientAPI

sendMobileServiceRequest(path: string, params?: any): Promise<any>;

 

/**
* Describe this function...
* @param {IClientAPI} context
*/
export default function DoSomePostRequest(context) {
   debugger;
   let userid = context.evaluateTargetPath('#Control:User/#Value');
   let appId = context.evaluateTargetPath('#Application/#ClientData/#Property:MobileServiceAppId');
   let uri = '/my/rest/api/v1/doSomething';
   let body = {
       "FirstName": "John",
       "LastName": "Doe",
       "Address": {
           "Street": "Some Street",
           "City": "AB",
           "Country": "CD"
       },
       "UserId": userid
   };
   let header = {
     "x-smp-appid": appId,
     "Content-Type": "application/json"
   };
   let params = {
       'method': 'POST',
       'header': header,
       'body': JSON.stringify(body)
   };
   return context.sendMobileServiceRequest(uri, params).then((result)=>{
     console.log(Result: ${result});
     console.log(Result Content: ${result.content});
     console.log(Result statusCode: ${result.statusCode});
     if (result && result.statusCode === 201 && result.content) {
         //DO WHAT YOU NEED WITH THE result.content here e.g.
         let data = JSON.parse({ "result": ${result.content.toString()}});
         context.getPageProxy().setActionBinding(data);
         return context.executeAction("/MyApp/Actions/ShowResult.action");
     } else if (result) {
       console.log(Failed Result: ${result.statusCode});
     }
     else {
       console.log(Failed Result: ${result.content});
     }
   });
}

Check the documentation for more details.

Support Offline OData Function Imports

CallFunction action

  • supports POST, PUT, PATCH and DELETE. method on OData v2
  • Will always immediately return empty result because the request is not truly send until you sync to the server
  • Need to sync to backend to commit the transaction
  • Function Import requests cannot/will not be merged with other requests.
  • In V2, function imports can only use primitive types as parameters.

Function Import requests cannot/will not be merged with other requests. They can, however, be automatically bundled into the same ‘transaction’ if the appropriate TransactionID header is used.

In V2, function imports can only use primitive types as parameters.

 

{ 
   "OnSuccess":"/DemoSampleApp/Actions/UpdateStatusSuccess.action",
   "Target":{ 
      "Function":{ 
         "Name":"UpdateSalesOrderStatus",
         "Parameters":{ 
            "id":"{SalesOrderId}",
            "newStatus":"R"
         }
      },
      "Service":"/DemoSampleApp/Services/SampleServiceV2.service"
   },
   "_Type":"Action.Type.ODataService.CallFunction"
}

 

Check the documentation for more details.

 

Support multiple formats with DateFormat and TimeFormat

We have now added new customOptions parameter for formatDate, formatDateTime, formatTime function via IClientAPI to display date and time in the specified format. Supported format style: short, medium, long, full. Default for date is medium. Default for time is short (MDK default). e.g. { format: ‘long’ }

// Display in date format
  formatDate(date: Date, customLocale?: string, customTimeZone?: string, customOptions?: any): string;
  // Display in datetime format
  formatDatetime(date: Date, customLocale?: string, customTimeZone?: string, customOptions?: any): string;
  // Display in time format
  formatTime(date: Date, customLocale?: string, customTimeZone?: string, customOptions?: any): string;

 

You can also use it via Internationalized and Localized Data.

e.g.

  • $(D,’2017-12-25T11:40:00Z’,null,’Europe/Berlin’,{format:’long’})
  • $(DT,’2017-12-25T11:40:00Z’,null,’Europe/Berlin’,{format:’long’})
  • $(T,’2017-12-25T11:40:00Z’,null,’Europe/Berlin’,{format:’long’})

Support Image Collection in Sectioned Table on Android

Image Collection is now fully supported in Android.

New to MDK development?

Follow these tutorials

Many thanks to our dev colleagues to share inputs in writing up this post.

Regards

Jitendra Kansal

Product Management, SAP Cloud Platform Mobile Services
SAP SE

6 Comments
You must be Logged on to comment or reply to a post.
  • Very helpful blog.

     

    Hi Jitendra,

    In mdk matadata, In Web IDE I am using form page in that I need password format like **** when user fill anything in that field.Is there any control so that I can use this. Could you please help me on this?

     

     

    Thanks

    Sani Kali

  • Thanx for your time.

     

    Actually I have designed login page in that I have added two fields i.e., User Id and Password. but in password field I don’t want password(abcdef) should be visible to us. Can we display password like **** in password  field instead of abcdef.

    Please see below screenshot for reference.

     

     

     

    Thanks & Regards,

    Sani Kali.

  • Hi Jitendra,

     

    Thanks for you blog. We were looking for a method like sendMobileServiceRequest.

    We are able to call this method with http method “POST” however we received 403 response from backend. On debugging, I find out that it give error saying “CSRF Token validation failed”. Could you please?

     

    Thanks

    Arvind

    • Arvind looks like you are missing X-CSRF-Token required by your backend.

      You should update your code to:

      1. Fetch X-CSRF-Token with a GET request via sendMobileServiceRequest with header:
        "X-CSRF-Token": "Fetch"

        to the same backend resource.

      2. The token should be returned in the Response Headers (usually in UUID format e.g. aBcDeFghijkLmn==), store it in a variable e.g. myCSRFToken
      3. Take the token and add it as the value for “X-CSRF-Token” header in the POST request with sendMobileServiceRequest. e.g.
        "X-CSRF-Token": myCSRFToken
      4. The server should received something like
        "X-CSRF-Token": "aBcDeFghijkLmn=="
      • Hi Ming,

        I am having same issue as Arvind, I am getting error 403: CSRF Token Validation Failed.

        I tried to fetch the x-csrf-token by doing a GET. However, the returning parameter do not have headers that can retrieve the CSRF token. Any idea?

        Basically I am doing a get $metadata, and it retrieved successfully but I cannot find the Csrf token to do a POST.

         

        cc Jitendra Kansal , please advise.

         

        Thanks