This blog post describes how you can apply deep filtering in your SAPUI5 application, using CAP and odata V4. This can be used to filter in expanded entities, which has come available in V4. Please note that using an oData V2 model to do this will not work, since this type of filtering is not supported in the odata v2 model.
1. Introduction
Most SAPUI5 applications consume an oData service, which is often built using the CAP framework. Using this framework, you are able to create linked entities, called associations. In specific scenario’s you might want to be able to filter in associated entities, while only retrieving the main list. For example, if you have the (very simplistic) below model:
Imagine these are entities in your CAP service, and Car has association links to Windows and Wheels, and you want to filter on cars that have specific windows installed in them. In this scenario, we would like to filter out those cars, using a deep filter (on the expanded entity properties).
2. Prerequisites
You will need a couple of things set up before being able to do this. Lets start with setting up our service the correct way, using an association;
U_to_Wheels : Association to many Wheels
on U_to_Wheels.ID = Wheels.ID;
More information on associations can be found in the documentation here.
Once we have a valid cap services, with our associations in place, we can start using them in our SAPUI5 application. Lets start by setting up our application to use an odata V4 model (in our manifest.json);
3. The filtering itself
Next we need to make sure that when we select our filter on our table, we define our filter just a little different then we are used to;
new Filter({
path: "U_to_Wheels", // The name of your expanded entityset
operator: FilterOperator.Any, // Can be any or all, defines which records have to apply to your filter
variable : "U_to_Wheels", // Also the name of your expanded entityset
condition : new Filter("U_to_Wheels/ID", FilterOperator.EQ, "1") // The condition you want to filter on
})
Once we apply this filter, we will see an oData call which should look like this:
GET Cars?expand=U_to_Wheels&$filter=U_to_Wheels/any(U_to_Wheels:U_to_Wheels/ID eq '1')
The filtering should be applied, and you can query expanded entities.. Hurray!!
Please note that we applied FilterOperator.Any in this case, which should return all MAIN records that have ATLEAST 1 wheel with ID 1 in the expanded set. We can also switch the FilterOperator.All which returns the MAIN records that ONLY have wheels with ID 1 in the expanded entity set.
4. Possible issues
When switching over to the required oData V4 model from an existing oData V2 model (which was the case in my journey), you might run into some issues. Here I will highlight some that I have ran into, and how to solve them;
When switching to the V4 model, some bindings require an explicit TargetType (which states what ‘type’ the binding should be, i.e. string, number, etc.). There is an ‘all’ variant available, which makes it accept all types like so;
When using the V4 model, some functions are not available anymore, for example the attachRequestFailed function. Previously, we have the following code implemented (which broke):
this.getModel().attachRequestFailed((oEvent) => {
ServiceCaller.handleDefaultRequestFailure(oEvent.getParameter("response"));
});
To fix this, we implemented the same functionality as follows:
this.getModel().attachDataReceived((oEvent) => {
if(oEvent.getParameter("error")){
ServiceCaller.handleDefaultRequestFailure(oEvent.getParameter("error"));
}
});
Another error that I encountered is that the .read() function is no longer available on the V4 model. This is logical, since you would like to do all service calls using bindings instead of fetching things in code. You can use things like $.get to work around it if strictly needed, but this is bad practice.
5. Concluding
Now you should know how to do some deep filtering on expanded entities, using the oData V4 model. In case of any questions, please leave a comment. Happy coding!