You are on page 1of 21

openSAP

Developing Web Apps with SAPUI5


Week 2 Unit 1
00:00:09

Hello and welcome to Week 2 of our openSAP course "Developing Web Apps with
SAPUI5".My name is Christiane, and I am a UI5 developer.

00:00:20

This week you will become a data binding expert in SAPUI5.You will find out how to connect
your application to a remote service, and you will learn about different binding types.

00:00:32

You will also practice how to format your data with expressions, formatters, and data types,
and how to sort, group, and filter data.And last, you will also get to know the details behind
OData services.

00:00:46

The first unit in this week is called "Using a remote service with aggregation binding".In this
particular unit, we will have a closer look at what data binding in SAPUI5 means

00:00:59

and how to connect your application to a real back-end service and display the data to your
users.Before we do this, let us briefly recap the scenario from the setup in Week 0.

00:01:15

You have already made yourself familiar with the Web IDE, which serves us as an IDE during
this course.We next need to have a Web server or app platform to deploy our app to, which in
our case is the SAP HANA Cloud Platform.

00:01:30

What is still missing in our application is the Back-End System part in this picture.In this unit,
you will learn how to retrieve data from a back-end service on an SAP Gateway, exposed as
an OData service.

00:01:44

We will see how to store and manage this data in a corresponding model in your
application.OData is a REST protocol based on AtomPub.

00:01:54

It has a lot of convenient functionality built in, like sorting and filtering on the server side.The
data we will be using is coming from the Demo Consumption System for the EPM Services
provided by SAP.

00:02:11

You should already have logon credentials you have created in the exercises from Week 0.If
you havent done this exercise yet, or have forgotten your password,

00:02:19

please refer to the page from the preview week in order to get the credentials.Note that you
need access to this system for the remainder of the course.

00:02:31

You can also find the links to this service again in the exercise file for this week.Let us now
have a brief look at the data the service exposes to us.

00:02:50

Here you can see the metadata document of the service.It contains all the metadata describing
the different entities, entity sets, their properties and associations.

00:03:06

More details on the metadata document will be covered in the last unit of this week.For now,
let us only inspect the data we will use in our next steps.

00:03:16

In this unit, our goal is to display a list of products on a separate tab in the app we created last
week.The entity we will use is correspondingly called "product".

00:03:28

So you can see that the metadata for this entity is down here, and it shows us which properties
are available for the product.

00:03:38

You can see, for instance, that the key for this entity is the ProductID, and that it additionally
has properties like a category it belongs to,

00:03:48

a name and a description, and, of course, also a price, which you can see down here.For each
property, there is more information, for instance, the type stored here,

00:04:01

but for now, it is most important for us to know about the structure of the data for a single
product.In an OData service, entities are usually organized in entity collections known as entity
sets,

00:04:14

in order to make it easy to retrieve multiple products.To find out which entity set we need in
order to retrieve all products, we check the entity sets for the EntityType parameter.

00:04:27

We can scroll down here a bit in order to find the entity sets.And we can see that the entity
type for the second entity set here is our product,

00:04:47

so we can determine that the set that we need to use is in this case the product set.Now we
have all the information about our data that we need to enhance our application

00:04:59

we know the entity and its properties, and the entity set.We will now use an OData model in
our application to work with this data.

00:05:08

The OData model is a server-side model in SAPUI5, which means not all data is automatically
loaded into your application right from the start.

00:05:17

Instead, it will trigger a request for the metadata from the service, and then only load the data
that is explicitly required.So enough theory. Let us now get our hands dirty using OData in the
app.

00:05:35

This is the app in the state in which we saw it last week.The first thing we now need to do here
is configure the destination to the service in our application settings.

00:05:45

To do so, we need to create and adapt the Cloud Connectivity Configuration of the application,
which is defined in a new file the neo-app.json file in the root folder of your project.

00:05:57

This neo-app.json file contains all project settings for SAP Web IDE.It is a JSON format file
consisting of multiple configuration keys.

00:06:08

The most important setting is the path where the SAPUI5 runtime is located when starting the
To create this file, right-click on your project folder, then click on New, and then on Cloud
Connectivity Configuration.

00:06:26

You can see that the file is generated here below.When you open it, you can see that there are
already some predefined settings added,

00:06:34

namely the path under which the SAPUI5 resources are registered.Next we need to add a new
configuration for the destination ES4.

00:06:47

The destination itself is already configured inside the SAP HANA Cloud Platform Cockpit.With
this configuration entry in the neo-app.json file, you are able to use it inside your application
project.

00:07:02

I will just copy over the code for this configuration part from the exercise file...because it makes
it just a bit easier and... most probably, this is the part where I would otherwise put typos in
here.

00:07:26

So, back to our Web IDE.So we'll just add a new root here, so I need to add it within the array
of roots that we already have here.

00:07:38

I'll just add it below here. Oh, there's one line too much.Let's remove this here again, and
maybe also format it a bit more nicely.

00:07:49

Here you can see that we now have the path to the destinations, we have a target of type
"destination" whose name is "ES4", and we have a description here.

00:08:01

When a request to the path "/destinations/ES4" is sent from your app, the path will be
translated to the actual destination we have set up in the SAP HANA Cloud Platform Cockpit.

00:08:14

I need to save this now.Next, we want our model to be instantiated with our Component.

00:08:21

For this purpose, we need to adapt the App Descriptor, the manifest.json file.In this file, we
need to add two settings:

00:08:29

the data source under the sap.app namespace, and the model under the sap.ui5 namespace.

00:08:40

I will switch to the code editor now because it makes it a bit easier for me.So the first thing that
we need as mentioned is the dataSources part here.

00:08:53

I'm copying this over again from the exercise file that you will also get when you start on the
exercises yourselves.So I'm copying this over, and I will just add it right here.

00:09:14

Again, here you can see a new data source with a path with a URI to the destination itself, the
way it is configured in the cockpit.And we are telling our application that this is of type "OData",
and it's OData version 2.0.

00:09:33

The other part that I need to configure is that we want to use a new model here, and this new
model also needs to know what data source it is.

00:09:47

In this case, it is our destination ES4, and it will not have a name in order to make it the default
model in our application.I just need to scroll down a bit here, and there you can see our other
models are here, so I will just be adding the new model below.

00:10:08

Again, I have one line too much.Okay, so here's our new model.

00:10:19

Now it's giving me one error... because...Ah, okay, I can see that I've closed this part too early
and it needs to be closed... below the data sources? "applicationVersion"...

00:10:35

Oh no, this line is just too much. I'm sorry, I copied one line too much.So now when I save this,
we have actually configured everything that we need in order to use this model in our
application.

00:10:51

So the data source parameter will make the model map to the data source specified
above.This way, the model will automatically be instantiated and set to the component of the
app.

00:11:03

All children of the component will automatically inherit the model of their respective parent.This
means that views will inherit from the component, controls from views, and child controls from
their parent controls.

00:11:17

You can also set models on the lower levels in this hierarchy, however, they will always be
propagated to their own children only.

00:11:25

So as mentioned, we now have everything ready we need for using the data in our app.Let us
enhance our UI now and see how the data can be displayed in our app.

00:11:36

We will open our App.view.xml file now, and add a new icon tab filter to our icon tab bar.So as
you remember from last week, we have added an icon tab bar here.

00:11:50

And we have added several icon tab filters, and I will just add a new one below the last one.So
I need to type in "IconTabFilter" here.

00:12:04

And this icon tab filter is supposed to have some text on the tab so we know what to click So I
will add a property that is called "text".

00:12:16

This text should usually come from the resource model, but to keep this short, I will now just
add a hard-coded text here for the title.So let's call this "Data Binding".

00:12:29

What else do we need? Oh, we also need a key, of course, so that the app can determine
which tab I clicked on.So this is our icon tab filter, which is currently empty.

00:12:43

Within this icon tab filter, we will create a list which should display all products stored on the
server.So I will create the list skeleton first now.

00:12:54

I am creating an instance of the List control.Again, this list gets a header text that should come
from a resource model.

00:13:06

However, we are using hard-coded text here, as mentioned, so it should just contain a title,
and this title can be Products at the moment here.It should have a width that should be
determined automatically by the application.

00:13:25

And it will be bound to all our products.So you remember that we determined in our service
that the entity set we wanted to use to retrieve all products was called "product set".

00:13:35

The list has an aggregation of items, and we will now bind these items to our product set.So
now I need to close the list tag again.

00:13:55

Oh, I'm sorry, there's a small typo in here. Okay.Note that the binding path starts with a slash.
It is an absolute path, so it points to a node at the root of our model.

00:14:11

The request triggered for the product set as it is the entity set of all products will retrieve all
products from the service once this view is loaded.

00:14:23

As the "Items" happen to be an aggregation at the list, we are making a use of a new binding
type here: the aggregation binding.You have already learned that controls can have
aggregations of one to many child controls.

00:14:38

An aggregation binding can correspondingly contain multiple entries, too.For our path to the
product set, the aggregation binding loops over each product.

00:14:49

It will generate a control for each product and then add this to the items aggregation at our
list.To tell the binding which control it should generate, we have to specify a template.

00:15:01

We need to create this template within the items node at our list.For every product, we want to
have an object list item created.

00:15:12

So I will add the code here now for the items aggregation.So I need to specify the aggregation
I want to use. Oops, sorry.

00:15:25

I'll scroll down a bit here so you can see better what I'm doing.And now within this aggregation,
we need to define the object list item that we want to use.

00:15:35

So I'm creating an object list item here... "ListItem".This list item should then have the name of
the product as the title, it should have the price as a number,

00:15:51

and maybe as an intro also the product ID, because I want to see that, too, and I also want to
see the number unit, which is the currency code.

00:16:00

So let me briefly see whether I've forgotten anything.I'll just copy that over from here because
it's conveniently already there.

00:16:18

Okay, let's add these three. And then the number unit is still missing, so I'll also add this in a
second."numberUnit",

00:16:30

which is the currency code that we could see on the metadata document... "CurrencyCode",
like this.So now I have my object list item.

00:16:44

In order to automatically fill these properties with the value from the right entry in our binding,
we now need to make use of relative binding paths.You can see that these paths that I've been
using within the properties of the object list item do not contain a slash at the beginning.

00:17:03

While the aggregation is created and the framework loops over the products, the binding
context will always be the respective current product for each object list item.

00:17:13

So I can save this and just run the app now.And you can see that we have a new tab called
Data Binding.

00:17:26

And here you can actually already see the list of products we have just produced.If you
encounter problems due to a slow connection to the server,

00:17:37

there is a fallback available where the responses are simulated from the mock server.Details
on how to use this fallback can be found in the handout.

00:17:46

In the background, when we loaded our app, the connection to the service has automatically
been made by the OData model, retrieving the metadata from the service.

00:17:55

When the App.view.xml gets parsed, the binding to the product set triggers the model to
request the corresponding data from the service.When the response comes back, the model
stores the data and the view can display it.

00:18:17

We have just made use of different ways to specify binding paths.One way you already knew
from the previous week is the absolute path, starting with a slash, and always resolving from
the root of the model.

00:18:29

In our list, however, we have now also made use of relative paths, which will resolve to their
respective parent.On the slide, you can see some samples for absolute and relative binding
paths, the latter pointing to child nodes of the company.

00:18:45

So you can see here that we have an absolute binding path, starting with a slash again,
pointing to this node in the model data, the company,

00:18:53

and then we have relative binding paths, like "info/employees", which would be resolved
relatively to the company, so actually, this path points to the 3 down here.

00:19:09

That's it for this unit.You should now have an idea how data binding in SAPUI5 generally
works, what property binding and aggregation binding are,

00:19:18

and how to connect an application to an OData service.Of course, there is a lot more
functionality behind it, and a lot more details to learn.

00:19:27

But you already know the most important basics: How to instantiate a model, and how to use
bindings in your views.

00:19:34

We have also used a new type of binding: the aggregation binding.As you have seen,
aggregation binding can be used to automatically create child controls according to model
data.

00:19:45

You are now ready for the next level on your way to mastering SAPUI5 data binding.Looking
forward to seeing you in the discussion forums. Thank you!

Week 2 Unit 2
00:00:11

Hello and welcome to Week 2, Unit 2 of our openSAP course "Developing Web Apps with
SAPUI5".My name is Thilo, and in this unit, we want to learn how we can form text for the UI

00:00:23

to convert more technical representation of data into human-readable text.For this, we will use
formatters and inline expressions.

00:00:31

Based on our current example from the previous unit, where we learned how to use
aggregation binding to display a complete list of items, we now want to enhance this with some
information that might be useful for our application users.

00:00:47

First we want to indicate to our users something besides the price that is already displayed,
Assume that you want to indicate that a price higher than a specific threshold is not accepted.

00:00:58

We can do this by adding a status to the list item in our case, we will set the number state on
the object list item to "error", which results in the number itself being displayed in red.

00:01:10

Let's look this property up in the Explored application.In here, we see a property
"numberState" that expects an sap.ui.core.ValueState, with Error, Warning, Success, or None
as options.

00:01:29

And now we want to implement this using an inline expression in our coding.Let's go to the
Web IDE and open our view from the last lessons.

00:01:48

Let's scroll down to the list binding, and in here, we see our object list binding.Next to the
number, we will add the property "numberState" and start with the expression.

00:02:05

We type "numberState" with double quotes, we open a new object for the data binding, and to
indicate that we want to use an expression binding here, we start with a "=".

00:02:16

Now we can add a reference to the data in our model, starting with a "$" and referencing the
price value in the model.Now we write a ternary expression to indicate that we have an error or
a success state.

00:02:51

The condition we need for this let's use the right quotes here so that it really works the
condition we want to set here is that a price higher than 500 should be assigned the error
state,

00:03:14

while a price lower than 500 should be assigned the success state.Let's save it and run the
application.

00:03:25

We now see that every price higher than 500 is displayed in red, while prices lower than 500
are displayed in green, so we achieved this.Expression binding in SAPUI5 is a pretty powerful
tool.

00:03:40

You can look it up in the developer guide.There are a lot of options here to use inline
expressions also for more complex operations.

00:03:50

Now we want to use some custom formatter to display some more detailed information about
the status of the items.Formatters can be used to do more complex operations.

00:04:01

Assume that we have different products on the screen now.It is important for our users to know
how heavy each product is because that decides how it will be shipped.

00:04:14

We can achieve this more complex transformation automatically by using a formatter


function.Let's first create a file called "formatter.js" in the "model" folder of our application.

00:04:29

We say New, File, call it "formatter.js", and it gets created.We now instantiate a new module
using the sap.ui defined syntax.

00:04:49

We don't load any dependencies here, and we create the "function" callback.We will add "use
strict" here to be good citizens.

00:05:08

Now we will return an object, and in there, our formatter function.We call it "delivery", and it is
a function that takes two arguments here.

00:05:23

There are two things we want to achieve in here: first, we have kilogram and gram weight units
in our service, so we have to ensure it is all transferred into the same unit format,

00:05:38

and the other thing we want to achieve is make a decision on how the preferred delivery
method should be displayed.So we add two arguments here: one is the "Weight", which is in
integer or number format,

00:05:55

and second is the "WeightMeasure", or let's just call it "Measure", which is in string format.So
we create a new variable here that should carry our return value.

00:06:14

Let's call it "sResult" because it will be a string.We instantiate it as an empty string.

00:06:20

And now, for the first part, we want to do the transformation for weights represented as grams
in the service.For that we write a simple "if" statement, and we say "if(sMeasure = "G"",

00:06:44

we say "iWeight = iWeight / 1000" to ensure the same formatting here.Of course, we need to
write the "=" signs correctly.

00:07:05

And now we can start making the decision about the delivery method.We say "if(iWeight <
0.5", "sResult = "Mail Delivery"".

00:07:33

We add another else/if statement here, where we say "else if(iWeight < 5)", then "sResult =
"Parcel Delivery"".And everything else, for all the heavy things, we will say "sResult = "Carrier
Delivery"".

00:08:14

Finally, we return our sResult: "return sResult".That's it for the formatter. Now we have to do
two more things here:

00:08:27

We have to add the formatter as a variable to our controller, and then use it in our view.For
that, we open our App.controller.js and load the formatter as a dependency.

00:08:43

We add the namespace for our application we need slashes here.We go to the "model"
folder, where we get the formatter.

00:08:59

We add the formatter as an alias to our callback, and finally create a new variable here called
simply "formatter" and is assigned with the loaded dependency formatter.

00:09:17

Now we can use the formatter in our view.We need a comma here. Luckily, Web IDE tells us if
we are making mistakes.

00:09:29

Now go back to the view, and use the formatter in here.The object list item has an aggregation
that is used to indicate statuses. Let's quickly look it

00:09:44

We see that the object list item in the Aggregations tab has an aggregation firstStatus.This one
takes an object status. Let's quickly look up what we can do with an object status.

00:10:01

Let's go back. Here we see we can have a text, and we'll use that within our firstStatus
aggregation.Let's go back to Web IDE, to our app view, and achieve this.

00:10:17

We see our object list item, we can take the firstStatus aggregation, we add it here, and in
here, we use the object status.Let's make it a proper tag, and we can close it.

00:10:39

Now the object status, as you can see, has a text property... "text="...and now we need a more
complex object for the data binding and usage of the formatter.

00:10:54

As we have several model properties we want to use within our formatter, we need "parts"
here.So we add a new "parts" object, which expects an array of "path is" to our model.

00:11:14

We create a new array, and in here, objects for our "path is".Here we say the path is, and now
we have to look up the sequence in the formatter again.

00:11:26

We first take the weight and then the measure.So we have the let's use the right quotes here
we have the weight unit as a first path

00:11:44

and then, separated by a comma, the second path, where we say "path:
'WeightMeasure'".Now with that in place, we need to use our formatter.

00:12:05

For that, we add a formatter setting here, and we say we reference the formatter variable on
the controller.I'm starting with a dot: ".formatter.delivery".

00:12:22

And with that, we should be good to go.Let's check if everything's right: we have a formatter
delivery function, let's add the semicolons here, and now give it a try.

00:12:41

We now see that, based on our model data, we have a decision here on how this should be
delivered based on the weight and the weight unit without our user having to make a complex
transformation.

00:12:59

That's it for Unit 2, Lesson 2.We've seen how we can convert technical data into humanreadable texts,

00:13:07

and we've seen how we can use custom formatters to do more complex transformations.

Week 2 Unit 3
00:00:09

Hello and welcome to Week2, Unit 3 of our openSAP course "Developing Web Apps with
SAPUI5".My name is Thilo, and in this unit we will learn how to use SAPUI5 types with data
binding for automatic formatting and validation of values.

00:00:27

SAPUI5 comes with built-in types to allow automatic formatting and validation of the most
frequently used data types, like specific number types, for example, float, as well as more
complex patterns like dates.

00:00:41

You can see a complete list of types on this slide.The difference between types and formatters
seems to blur at first sight,

00:00:49

but actually it's very clearly defined.While a formatter only allows you to format what will be
displayed on the UI while leaving the actual value in the model untouched,

00:01:00

the type serves us in two ways.We will see how this works later when we have a look at the
development of custom types.

00:01:09

For now, it is enough if you understand that a type does conversion for the correct display on
the UI, but will also write back the original value format into the model.

00:01:20

Let's start testing the one-way conversion the types provide to our list to achieve nice
formatting of the currency display.If we open our app, we see that we have different currencies
in our service.

00:01:39

This means that we should also display currency in a specific format.Now assume you have to
do all this from scratch.

00:01:47

Luckily we can use the sap.ui.model.type.Currency type on that price for our products on the
list.Now let's go to the coding and edit our App view.

00:02:02

Where we have the number bound, we see that we are currently only displaying the price from
the model.Let's now add the type here.

00:02:16

The currency type expects two values: the price as well as the currency.So we will need
"parts" as well, as we saw in the last lesson.

00:02:28

So we add a new "parts" key here, and again, we need an array of "path is".So the first path
would be our price value path: 'Price',

00:02:43

and the second path is the currency code path: 'CurrencyCode'.Now with that, we will add
our type.

00:03:04

We need a fully qualified namespace here: sap.ui.model.type.Currency.Types can be used


with special format options, so we add a format option here that tells the type not to display the
currency code

00:03:31

because we've already bound it to the NumberUnit property on the object list item.So here we
now add "showMeasure" and set it to "false".

00:03:48

Now save it, do a correction check, it looks good so let's give it a try.We did something wrong
here, so let's have a look at what the problem is.

00:04:09

The number is bound with parts we have a "Price" path and a "CurrencyCode" path.We
added a type here, it's sap.ui.model.type.Currency with "formatOptions".

00:04:28

I think we missed the "s". That should do.There we go. Now it looks nice, and we see that, for
example, Japanese Yen is well formatted,

00:04:43

and euros and dollars are as expected.But to understand the capabilities of one type in all
detail, let's use it for form validation.

00:05:00

From the last sessions we have an input field.Now let's assume we want to build a simple field
validation based on one of the SAPUI5 types.

00:05:10

Let's use the Float type for now in order to make this work.We now have to make some
changes to our binding in the XML view.

00:05:19

Let's go back to the coding, back to our App.view.xml, and scroll up to where we have the
Input field.We need to make some changes here.

00:05:30

Now we have the value here bound against our view model.We have to extend the syntax
here, adding the path here.

00:05:48

Put it in single quotes.And now, as before, we add the type.

00:05:58

In our case, we use the Float type, so it's sap.ui.model.type.Float.And here we go again with
"formatOptions".

00:06:20

Let's not miss the "s" again.Let's format it nicely.

00:06:32

We say we have a minFractionsDigits as an option here, where we say it's 2.And we can also
use constraints to validate our value, where we now add a constraint that is a new object.

00:06:58

And we define the maximum that should be allowed to "3000".Finally, let's remove the
valueLiveUpdate event here because this makes the change more transparent for display.

00:07:21

Let's now give it a try and run it.We have to get back to our Getting Started tab here, and down
here, we see our input.

00:07:36

If we now add a value like 300... it's not working again. Let's go back.We have the value, we
have the path, we added the type sap.ui.model.type.Float, we have the formatOptions, and we
have the constraints.

00:08:09

This is in quotes, this is in quotes."minFractionDigits" I think it is. Let's give it a try.

00:08:25

Let's go back to our view here, add a value, and now we see that it gets formatted. We have
the fraction digits as defined.But if we now add a value higher than our threshold, we don't get
any form validation here.

00:08:39

So SAPUI5 has built-in validation handling.We can do this on individual controls, but also on
the runtime core itself.

00:08:50

So now we get our core and say we attach the validation error here:
sap.ui.getCore().attachValidationError().In this one, a callback can be added, where we add a
callback function, we add the event parameter to it, and add a debugger statement.

00:09:19

So if we do this now, and we add a higher value, we see that this gets gripped now.And if we
now have a look at what comes back from the event handler,

00:09:37

we see we had a validation error, and in the parameter list, we also see there's a message,
some predefined text here.We see the changes, and we see the control this was issued from.

00:09:50

We could build our own form validation from scratch, but luckily, SAPUI5 provides this out of
the box.For this, we go to our manifest.json in the Code Editor view, and in the sap.ui5
namespace,

00:10:10

we add a setting let's use the right codes here: handleValidation and assign it to
"true".Don't forget the comma. No complaints, so let's start this.

00:10:30

If we now run the application and add a value higher than our threshold, we see that it gets
automatically validated, and if we focus this field here, we see that we exceeded our threshold.

00:10:53

The last thing we want to have a look at in this unit is building up a custom type.There might
be a point in time when you develop an application where you feel that the types provided by
SAPUI5 are not sufficient.

00:11:10

Implementing your own custom type is pretty simple.I prepared a file here that is basically the
skeleton for your own custom type.

00:11:20

We need to extend SimpleType here, and implement these three functions on it.So first is a
formatValue that handles the formatting for the UI representation.

00:11:32

Second is a parseValue that handles writing back the two-way thing we had into the
model.And lastly, a validateValue, which is where your constraints defined in the XML are
handled.

00:11:48

And that concludes today's lesson on types in SAPUI5.Thanks for listening.

Week 2 Unit 4
00:00:09

Hello, my name is Martin. Welcome to Unit 4 of Week 2 of our openSAP course "Developing
with SAPUI5".In this unit, we would like to make the content of our products list more
accessible

00:00:23

by making it searchable and sortable and by grouping its entries by invoice amount
categories.So let's go.

00:00:32

To make lists and tables searchable, SAPUI5 provides filters which are applied to the list
binding.In our example application, we will use a filter to retrieve only those entries containing
the search field string.

00:00:48

We begin by adding the sap.m.SearchField to our invoice list tab strip.So let's have a look at
our sample application. Here's the list. Okay.

00:01:05

So we will put it into the list's header toolbar, where we can also place the header texts, so
let's do that now.I'm going to add a header toolbar here.

00:01:19

I need another toolbar.Okay, and here I can now add a title.

00:01:37

Texts I can take from the headerText property.Okay, now I would like to add a toolbar spacer
because I'd like to make sure that the title is left-aligned while the search field is right-aligned.

00:01:59

And I want to make sure that the space in between is just clear.Okay, now finally I can add the
search field.

00:02:33

I'd like to make sure that the search field's width is no bigger than 50% of the available
space.And now I'd like to define which handler function should be executed when the search
field's search event is triggered.

00:03:03

And a function called onFilterProducts should be executed in this case, which still needs to be
implemented in the controller, but we'll come to that in a minute.

00:03:19

So now I have the title within the toolbar, so I do not need it any more as a headerText
property in the list, so I can remove this.

00:03:34

Okay, so let's now have a look at our change.Ah, there we go. Okay.

00:03:45

The search field is available, but nothing happens yet because the controller has not been
implemented yet.Okay, so let us move on to the controller.

00:03:58

We would like to be able to filter for the product name and the product ID.Since we are working
with an OData model, let us first find out whether that is possible

00:04:08

and have a look at our OData services metadata.So for that I'm first going to copy the domain
of our application.

00:04:30

Next I'm going to the application's manifest file.Here there's a Data Sources tab where you find
the Services, where I just take the URI of our data service, copy it, and I append it to the URL.

00:04:57

All I need to do now is add "$metadata" to the So this should be our metadata URL, let's see if
it works... Aha, okay.

00:05:09

So here we have the metadata file with the information on our fields.We are dealing with
products, so let's have a look at the product and name field.

00:05:24

The product field has no flags that involve filtering, so it should be okay.But unfortunately,
when we look at the name field, we find that it is not filterable.

00:05:40

Unfortunately, we cannot filter for names.So let's go back to our controller file, where we can
now start implementing the controller.

00:05:56

This time, I'm just going to copy the code over I'll explain it in a second.So here's the
onFilterProducts function.

00:06:14

What does it do? It has as a parameter an event, which is generated by the search field and
contains the string that is ended in the search field.

00:06:28

We can get this by the parameter "Query".Next we need to retrieve the list control from the
view, which can be done in this fashion.

00:06:42

In a controller, you have access to the view, which can give you all the contain controls by their
IDs.We assume that the list has an ID that ends with invoiceList, so let's check first whether
this is really true.

00:07:02

So we need to add an ID here... okay.Let's go back to the controller.

00:07:12

Once we have retrieved our list, we can now get the binding, which we need to apply the
filter.We do this by using the getBinding method, but we have to specify which binding we
mean,

00:07:27

and we are interested in the binding of the items aggregation.When we have retrieved all this,
we can prepare our filter array,

00:07:41

and in our case, we just create a new filter with an Operator.Contains.The filter shall be
applied to the field "ProductID", and it uses the query string from the search field.

00:07:57

As you see here, this must also be provided.We are using Filter and FilterOperator, so we
must make sure that these classes are also available and defined.

00:08:12

This is already done here, and they are in here as parameters so we can use them down
there.So let us now see if this works.

00:08:22

Let's go back to our application, refresh it, okay.These are the filter IDs, so let's now try it and
see if our controller function works.

00:08:42

No, it doesn't.So let's have a look at what may have gone wrong here.

00:08:55

Ah, maybe I should have saved it. Always helps. So let's give it another try. Okay.So now I
expect only those products to be shown whose ID contains the number one thousand.

00:09:17

Aha, this time it worked. Okay.We implement the filtering, so let's move on to the next topic.

00:09:34

Next we would like to sort the products by their category.Again, we first check whether
Category is sortable.

00:09:47

So for this, we again have a look at our metadata file, which is still available here.And here's
the Category property.

00:09:59

There's no indication that sorting is not allowed for this field, so let's move on.Okay, so now
we're going to modify a view.

00:10:22

So we're going to replace this simple aggregation binding with a complex binding, so here
we're going to... "path= '/ProductSet'".

00:10:52

Okay.And next we can define a sorter.

00:11:01

"sorter" this is an object, too.And inside this object, we specify the sort path, which in our
case, is the Category field.

00:11:20

Okay, this is already it. Let's see if it works.So let's first execute it without being sorted.

00:11:37

Now we refresh it and the order should be different.Oh, it seems like something's wrong.

00:11:52

So let's have a look at the items binding again.It seems like I used an "=" sign, which should
not be used. Okay, so this should be better.

00:12:13

Let's try again. Okay, and now you see there's a different sort order.Okay, it works now, the list
is sorted but the list items do not show the category yet.

00:12:28

We'd like to see that information, but it would be nice if we didn't have to show it in every row,
right? This is where grouping comes in handy.

00:12:37

So now I'm going to group this list... simply by adding this flag here.Okay, let's see if it works.

00:13:03

Ah, okay, so now you see the different categories as groups, and this is what we'd like to
achieve.Thank you, that's it for this unit.

00:13:14

In this unit, we learned about filtering, sorting, and grouping.In the next unit, you will learn
about how to bind contexts with element binding.

00:13:24

Thank you, and bye bye.

Week 2 Unit 5
00:00:10

Hello, my name is Christiane.Welcome to Week 2, Unit 5: Binding Contexts with Element


Binding.

00:00:18

In this unit, you will learn about a third important binding type in SAPUI5.Let us briefly go over
the binding types you have already used.

00:00:29

You already know about property binding, which allows you to make a connection from the
properties of a control to particular paths in the model.

00:00:40

When this connection is established, these properties automatically get initialized and updated
from model data.If you have a two-way binding for input controls, for instance, the mechanism
works bi- directionally,

00:00:55

so updates on the properties will also be reflected in the model.You have also learned about
aggregation binding in Unit 1 of this week.

00:01:06

Remember, aggregation bindings are used to automatically create controls for an aggregation
at a parent control from model data.In our sample application, we did so for the list of products.

00:01:20

We have bound the parent control the list to an entity set in the model.Then we used a
template control, which gets cloned for every corresponding entry in our entity set in our
case, the products.

00:01:37

Let us have a look at the code again.Our object list item, our template control, which is located
at the bottom of our App.view.xml file,

00:01:49

contains relative paths for its properties.Thus, these resolve relatively to the respective entry
for which this template control is currently cloned.

00:02:00

So you can see that, for instance, the path to the price does not contain a slash; and neither
does the path to the object status below here.

00:02:11

When we scroll up a bit, you can see again that this is the list that contains these type controls,
and this list is bound to the product set.Aggregation binding is perfect when you need to create
the same representation on the UI for multiple children of a parent.

00:02:28

But there is one binding type missing.We will have a look at the desired outcome of this unit
first in order to make it a bit easier to understand what we are trying to achieve.

00:02:42

I have therefore prepared the final version of the app that we want to create this week in this
unit, and I will just briefly open it now so you can see what we are trying to do here.

00:03:01

Again, this is our data binding tab, and now we want to be able to select one of these products
and then have a panel to display product details about the respective product below.

00:03:16

And, of course, the information that is displayed here needs to be changed according to the
product that is currently selected.So depending on which element a user clicks on, the data
here has to be updated.

00:03:34

For this display, we need to choose a container control as parent, and a layout so it looks
prettier.Here we have a panel control containing a grid layout.

00:03:47

Now you want to display the detail information for the selected list item inside.But you cannot
use aggregation binding, as you dont have a list-like structure here

00:03:56

where you can just clone a template for each entity.However, we still want the bindings of the
child controls here to resolve to a dynamically changing element.

00:04:07

We need to change this with every user selection.So we need relative paths for the child
controls as they always need to get the data from the selected item.

00:04:17

Here is where element binding comes to the rescue.Element binding allows you to create a
binding context for any parent control,

00:04:26

allowing the children to have relative paths resolving to this context.The binding context itself
points to a specific object in the model data, and can easily be changed on user interaction.

00:04:40

Let us have a look at an example.Here, on our slide, you can see our binding path examples
from the first unit of this week.

00:04:52

You already know the property binding and the aggregation binding from this unit.When you
now want to bind the contents of a control to a particular object not property in the model,
you can use element binding.

00:05:07

In the example on the slide, you can see that the panel showing data in the last screenshot
currently displays information from the object under /company/contacts/1.

00:05:20

The phone and name paths can now be relative to this.So you can see that you have a
property binding without a slash here saying "phone" and "name",

00:05:28

and these both resolve relatively to this object in the model.When I change the element
binding to /company/contacts/0,

00:05:39

the phone and name information will automatically be replaced with the information of the
object under that path, and would now become "phone": "873", and "name": "Barbara".

00:05:52

Oops, sorry.To switch between elements and update the parent control's element binding, you
can use the bindElement method.

00:06:02

Switching from one path to the other just requires you to specify the new path for the element
binding and pass that into the bindElement method.

00:06:13

On this slide, you can see we have a function expecting an index, and when it gets triggered, it
calls bindElement on the panel shown here, concatenating the index to a new path.

00:06:26

So, for instance, when I pass the index 0 in here, this path would be concatenated to
/company/contacts/0.And we're then calling bindElement to create the binding context for this
panel.

00:06:41

That's all it takes.Now you know the theory, let us apply this and create the code in our app for
the behavior that we have initially seen.

00:06:54

Let us close the "Final" app for now because, of course, we want to do this together.We will
create the panel for detail information below the list,

00:07:05

and update the element binding whenever someone clicks on an element in the list.Before
that, however, we will limit the number of list items and introduce a simple paging mechanism.

00:07:17

Otherwise, we would have to always scroll down so far to get to our detail panel.So I need to
edit my list here.

00:07:27

And the list has two properties we can easily use to achieve this paging mechanism.We will
just add these properties here, and set "growing" to "true",

00:07:43

then we add another property that is called "growingthreshold", and we will set this to "5", and
we will set "growingScrollToLoad" to "false".

00:08:08

This means that we are now introducing this paging machanism with a growing="true"
parameter.The "growingThreshold" set to "5" oh, I'm sorry, there's a typo in here, it needs to
be a capital "T".

00:08:21

The "growingThreshold" set to "5" makes the list load only five items in the beginning.And
growingScrollToLoad="false" just means that we explicitly have to click on a button to load
more items instead of just scrolling down.

00:08:37

So I can save this, and when I run the app now, you can already see this in action.So again we
are on our Data Binding tab, and when I scroll down now, you can see that only five products
are displayed at once.

00:09:00

And I have a More button I can click on when I really want to load more products.Now we can
add our panel below our list, so I go back to my view,

00:09:15

and I just want to add a panel right below my list here.So I will create the instance of the panel
now.

00:09:26

I want the panel to have an ID because it will be easier to determine what needs to be
changed later on, so I'll type "productDetailsPanel" as an ID.

00:09:39

It will have a header text again, and as with the previous units, you know that these texts
should usually come from a resource model.In this case, we're just using hard-coded text to
make the unit a bit shorter, so it will get the header text "Product Details".

00:10:01

To begin with, we will set the visibility of the panel to "false".We do that because we don't want
to display an empty panel to begin with.

00:10:12

We only want to display the panel when somebody clicks on a product.We can set the visibility
back to "true" later on, so "false" is the setting that we want to begin with.

00:10:27

Inside our panel, we will now display the detail information.To make it look a little prettier, we
will use a layout control inside the panel.

00:10:37

Let us pick the GridLayout.Inside the grid, we will have four text fields, where we display a
label text and the actual information.

00:10:48

Remember that you can concatenate values from bindings by just specifying two separate
paths within the corresponding property, and you can also combine hard-coded texts, which
we'll do in a minute, and the actual information coming from the model.

00:11:04

I need to create the grid here first, and it should go into the panel.And within the grid, I can
now specify my content.

00:11:16

I want to have these four text fields, as mentioned, so I'll just create an instance of text
here.And in this first case, the text itself the property text will be the product ID as a label,

00:11:31

and I need the bound value, so I need the value coming from the model, which in this case, is
also "ProductID" in the curly braces.Now I will just close this text here.

00:11:47

Actually, I can also make it a self-closing tag because it doesn't contain anything else.And now
I can just copy this because I need four of them, and then I only need to replace the
information inside.

00:12:01

So what is next is maybe our product name, which is actually bound to a value from the model
called "Name".

00:12:11

Then we want it to have the product description, and this is actually also a value in the model
called "Description".

00:12:25

And last we want it to have the product price, so the label will be "Price", and the value from
the model is also called "Price" here.Now what is left to do is change the element binding on
user selection.

00:12:45

We need to add two properties to our list so we can call an event handler when the user clicks
on one entry.In particular, these two properties are mode, single-select master mode,

00:12:58

which will allow us to trigger a certain event when somebody clicks and selects an item.And
we also need to specify the event handler name on selection change that should be triggered,
and we will call this "onItemSelected".

00:13:15

So the mode in my list I'll type this below the other properties that are already there mode
will be "SingleSelectMaster".You can refer to the API documentation to find out which other
modes are there.

00:13:35

And "selectionChange" is an event that we can use now.And the "selectionChange" event
handler that we will create in a minute will be called "onItemSelected" camel case used here.

00:13:56

Okay.So why is it showing me all this beautiful red...?

00:14:05

"IconTabFilter does not match..." what?

00:14:33

Okay, as mentioned, the "SingleSelectMaster" mode will make items selectable, but only one
at a time.And now we need to implement the event handler in our controller.

00:14:44

We create the corresponding method in the App.controller.js.And, as mentioned, we will call it


"onItemSelected".

00:14:55

So I'll just create a new function here, "onItemSelected", it is a "function".And, as always, event
handlers get the "Event" object passed as a parameter.

00:15:13

From this "Event" object, we can now get to the selected element by using a function called
"oEvent.getParameter".And from this parameter, in this case, we can get the selected list item.

00:15:28

So I will achieve this here now by typing... I'll call it "SelectedItem", and I'll just use, as
mentioned, "oEvent.getParameter".

00:15:41

This parameter, this selected list item, is getting passed into the event and is then made part of
the "Event" object.And I know from the API that it's called "listItem", so this way I can retrieve
the item that is actually selected.

00:16:01

And now we can actually also simply determine the context of the list item and ask for its path.I
will create another variable here for the context.

00:16:16

And asking the list item for the context, I will just use the "getBinding" context method on the
selected item... like this.And the path can actually now be retrieved from the binding context
that we have just set to our "oContext" variable.

00:16:42

So I can say "var sPath = oContext", and there is a method to retrieve this path called
"getPath".So now I have the path to the corresponding selected item, and I can use this in the
element binding for my panel.

00:17:02

So as last steps, the only things we still need to do are retrieve the control instance of our
panel by its ID, and then call "bindElement" on this instance.

00:17:13

So our panel control was called "productDetailsPanel".So now I can say I want to have my
panel here, and I can retrieve it from the view by calling "this.byId" as I know the ID,

00:17:31

and as mentioned, the ID was "productDetailsPanel".And now I can call "bindElement" on this
instance, so I can say "oPanel.bindElement",

00:17:47

and I just have to pass the path into the "bindElement" going as a parameter.So I need to
create this object here, and it just contains one property, and this property is called "path",

00:18:02

and I'll just pass the sPath we have already retrieved in here.Now we can also set the panel to
"visible" because we can be sure there is a list item that has been selected.

00:18:14

There's one more step I need to do, and again I can just use the panel instance that I've
already retrieved.And now just set the property with the corresponding setter, so now I can just
say "setVisible" to "true".

00:18:35

So now I need to save, and we can run the app again.Do I have a typo somewhere?

00:18:55

This looks good.Ah, okay.

00:19:11

I'm sorry, we need to go one step back because what I forgot is that I have used a grid layout
in our App.view.xml file, but I haven't yet specified the namespace for this grid.

00:19:26

So the system doesn't currently know where to load this grid from.So here I need to add the
corresponding namespace for the layout library,

00:19:37

so I'll add it as "l" here, "sap.ui.layout".And then, of course, I need to scroll down here and
pass the information to our view

00:19:53

that the grid we're using in the panel is actually coming from a different namespace, namely
the "Grid".So I need to use this "l" to refer to the correct namespace before.

00:20:05

Okay, now we should be able to see our app again when we reload it. Here it is. This we don't
need any more.And you can see that, in the beginning, no panel is visible below the list.

00:20:16

Now when I click on one of these items and scroll down a bit, you can see that our panel
becomes visible and it also displays the correct information.

00:20:27

Now when I change to a different product here, the information below is adapted
correspondingly.Okay.

00:20:37

So the panel content will be updated whenever we select a different item, and we can even
see how the binding changes by looking at it in the support popup.

00:20:47

You should remember the support popup from one of the previous units, so I just need to type
Ctrl + Shift + Alt + S to open it.And then when I have a look at the control tree, I can scroll
down to my panel, for instance

00:21:02

we need to scroll down a bit because it's one of the last items we added here are all the
object list items, and here's my panel.

00:21:08

You can see it was briefly highlighted green in the background.And when I have a look at the
Binding Infos here and, for instance, select one of my texts,

00:21:18

you can see that this path is actually relative, pointing to the product ID, but it resolves to an
absolute path that points to a particular product in our product set,

00:21:30

and then to its product ID itself.And when I change to a different product... and I go back to my
support panel again...

00:21:41

then you can see that when I reload this... Do I have to reload? Yes, of course, I have to
reload.Open the control tree again, scroll all the way down, and pick the text, for instance, the
product ID again,

00:22:01

then I can see that this changes correspondingly on Binding Infos to a different product.So you
can see that this is the product 1119, and now I change to the different product, reload this,
open the control tree once more,

00:22:22

and now my binding info points to ProductSet, the ID of HT-9994.That's it for this unit.

00:22:33

You have learned all about the last important binding type that is available in SAPUI5.This
binding type is mostly used to react to user interaction on a list-like structure,

00:22:44

and it helps you flexibly display data that needs to resolve relatively to a particular object in the
model.In the next unit, you will learn more about how to work with OData services.

00:22:56

I look forward to seeing you in the discussion forums.Thank you.

Week 2 Unit 6
00:00:10

Hello and welcome to Week 2, Unit 6 of our openSAP course "Developing Web Apps with
SAPUI5".In today's unit, we'll have a look at OData, which we've already been using
extensively throughout the week, building up the application so far.

00:00:26

Let's understand now how data is requested, and how the OData service and the OData model
and binding in UI5 interplay.

00:00:36

First, OData is an open protocol that allows us to consume data following REST
principles.With additional query options, the results can be refined and payload optimized.

00:00:48

But what divides OData from other REST services is the specified service metadata that
decouples the back end from the front end by providing a clear description of its resources, like
collections, entities, and properties.

00:01:02

This allows us to consume and use additional information about the service structure
centrally.We can examine this metadata file by loading it in the browser and entering the
service URL followed by the /$metadata keyword.

00:01:18

I've prepared that here, and we see that the metadata comes as XML.First we can see a
number of bigger blocks known as "entities", which can be compared to tables in a database.

00:01:31

As we have been using the product entity so far in our app, let's look this up in the file.We see
that this entity is of the custom type "Product".

00:01:44

As in normal database tables, we have a unique identifier the key that gets referenced to
one of the properties.For products, it is the productID.

00:02:01

Most of the other properties should be familiar by now as we've used them in our data
binding.We see that they have a name that can be used for reference, a type, and some more
description configuration.

00:02:16

We also see a simple annotation here, for example, for the product name.Here we see an
"sap:label" annotation, which was added to OData by SAP

00:02:27

and can be used to display a label to input directly out of the metadata.Let's now go further
down to the navigation properties.

00:02:39

Navigation properties show how this entity can be connected to some other entities in our
service.This can be compared to relationships between data tables in a database.

00:02:52

To access our entities via URL, they still need to be mapped in an entity container section.Let's
quickly look this up.

00:03:04

Let's find the starting point, and here we see how our "EntitySet" "ProductSet" is defined, and
is mapped to the "Product" namespace in the XML that is our entity we just looked into.

00:03:21

Let's now go back to the slides and learn a little bit about how we can access properties, entity
sets, and other things in the service directly via the URL in the browser.

00:03:33

We have already seen that the $metadata call can be triggered by adding /$metadata to the
This is called by the OData Model straight after the initialization.

00:03:50

To access an entity set, meaning the list of all entities datawise, we can add the name of the
entity to the URL.If we want to access one single entity, we can pass the entity key into the
URL in brackets,

00:04:03

and from that, we can even retrieve one single property on this by adding the property name to
the URL after a slash.Let's go back to the browser and see how this works.

00:04:18

To understand this, it is way easier to use the browser and a tool provided by the back-end
colleagues that facilitates direct service access by adding the parameter "sap-ds-debug=true".

00:04:35

Let's do this quickly.Regarding the URL, we can use a simple tool to browse our service
clicking on several links.

00:04:47

This works with all NetWeaver OData services.We can now click through our service of
course, we cannot do this for the metadata, but we can do it for one of our entities.

00:05:00

Let's use the "ProductSet" of course, we need the "t" and then it works.We see that we have
links here to our sales order items, or to the "ToSupplier" to related collections.

00:05:15

We can now easily understand how the navigation properties are getting grip and connecting
different entities.Let's now get back to our app and look into the requests that are actually sent
out.

00:05:30

So we open the Chrome Developer Tools, switch to the Network tab, and reload the
application.We can now see let's search for it here because it's pretty small the $metadata
call that gets sent out pretty early,

00:05:48

and a $batch call that the requests sent out by the model.The batch call contains several backend calls to reduce the number of calls overall.

00:05:59

With the OData Model version 2, batching requests is enabled by default.But for us to
understand what data is requested, we can set this option to "false" in our component.

00:06:13

So let's go back to the coding, open the component, and add this simple statement here.We
say "this.getModel()" to get the model that is stored on our component it's the default model
without a name.

00:06:31

And then we say "setUseBatch(false)" here.If we now save this, and rerun the application, and
open the network, let's reload it again,

00:06:51

we will now see that there are several calls here with the dollar sign.We have a "$count",
which is retrieving the count for all entities in this entity set,

00:07:05

and we see that it matches the 115 that we've requested.We see the "skip" and the "top"
options that are used for paging,

00:07:17

and we see that from 0 to 5 all entities were requested, which matches the five we have
here.Let's now go back to the slides and have a closer look at the query options.

00:07:29

This is a pretty handy table that you can use to find all query options and SQL equivalents for
easy understanding.And you can also see here how this is used within UI5 controls out of the
box.

00:07:44

Let's now use the "expand" parameter and try this in our app.Let's go back to the Web IDE for
the coding and open the App.view.xml.

00:08:01

We will now use the "expand" parameter in the data binding for our items binding.We see that
we already have a sorter and some grouping added here,

00:08:12

and we can add another setting here called "parameters", which is basically a new object.In
this "parameters" section, we add the "expand" option and set this to "ToSupplier",

00:08:36

which is, if we go back to our service, the "ToSupplier" navigation property that is set on our
product set.Let's look to see whether we've done everything correctly it looks good, so let's
restart the application.

00:08:58

If we now open the Network tab let's reload it again to ensure that it's there we see that,
within this call, we have the "expand" option that is added here.

00:09:08

Let's open the URL, go to the very end, and we see that an "expand=ToSupplier" has been
added to the request.We can now use this additional information that has been loaded by the
service

00:09:22

in our application to display some additional information.In the "ToSupplier" collection, we have
an "address" block where we can reference a city.

00:09:34

Now we will add a second status to our list item, and display the city from where this product
was delivered.We have a "firstStatus", so let's add a "secondStatus" aggregation block here.

00:09:54

It again takes an object status, and here we will display a title and a text.

00:10:11

For the title, we will use a hard-coded string.Of course, you will use internationalization data in
real applications.

00:10:22

We say "Delivered from". There, that looks good.And we add a text here where we need some
data binding syntax.

00:10:45

And here, we reference our "ToSupplier" navigation property, in here the "Address", and
cascaded down to the "City".Let's do some formatting here so it looks nice, let's do a sanity
check: "ToSupplier", "Address", "City" looks good, let's run it.

00:11:12

We can now see that additional information has been added as we wanted.We see that this
tablet will be delivered via mail from Cologne, so we think it will arrive soon.

00:11:25

That concludes today's lesson on working with the OData service, and actually, the entire
week of the course.This week we have learned how data binding in UI5 works,

00:11:35

and next week we will start building a new application based on a template, and refine the
knowledge already gained.Now good luck with your weekly assignments, and I hope to see
you soon.

00:11:45

Thank you.

www.sap.com

2016 SAP SE or an SAP affiliate company. All rights reserved.


No part of this publication may be reproduced or transmitted in any form
or for any purpose without the express permission of SAP SE or an SAP
affiliate company.
SAP and other SAP products and services mentioned herein as well as their
respective logos are trademarks or registered trademarks of SAP SE (or an
SAP affiliate company) in Germany and other countries. Please see
http://www.sap.com/corporate-en/legal/copyright/index.epx#trademark for
additional trademark information and notices. Some software products
marketed by SAP SE and its distributors contain proprietary software
components of other software vendors.
National product specifications may vary.
These materials are provided by SAP SE or an SAP affiliate company for
informational purposes only, without representation or warranty of any kind,
and SAP SE or its affiliated companies shall not be liable for errors or
omissions with respect to the materials. The only warranties for SAP SE or
SAP affiliate company products and services are those that are set forth in
the express warranty statements accompanying such products and services,
if any. Nothing herein should be construed as constituting an additional
warranty.
In particular, SAP SE or its affiliated companies have no obligation to pursue
any course of business outlined in this document or any related presentation,
or to develop or release any functionality mentioned therein. This document,
or any related presentation, and SAP SEs or its affiliated companies
strategy and possible future developments, products, and/or platform
directions and functionality are all subject to change and may be changed by
SAP SE or its affiliated companies at any time for any reason without notice.
The information in this document is not a commitment, promise, or legal
obligation to deliver any material, code, or functionality. All forward-looking
statements are subject to various risks and uncertainties that could cause
actual results to differ materially from expectations. Readers are cautioned
not to place undue reliance on these forward-looking statements, which
speak only as of their dates, and they should not be relied upon in making
purchasing decisions.

You might also like