Nathan Hands' Blog

UI5 applications consultant, entrepreneur, fantasy book lover and gamer

better odata handing in ui5 using promises part 3

6 months ago · 4 MIN READ
#tutorial  #UI5 

Introduction

In part 1 we learned about promises and promise encapsulation and created little functions to return our promises in UI5. In part 2 we took that to the next level and made a class to sit on-top of our oData model and turn all of our CRUD requests into promises.

In this post I'm going to go over the use of promise.all which will allow us to wait for all of our promises to complete before executing our final function.

The scenario

In this scenario I'm going to be making use of a gateway service which I wrote about in binding odata to our google chart. This service was made for me to deliver internal training to our new starters and simply put has CRUD operations for a simple 'todo list'

In this scenario I'm going to create a button that triggers a delete request for all of the items in the current list and once that has been processed we will trigger a different function. Tiny bit contrived but it will allow us to go over the use of promise.all to the point where you can hopefully understand it and extract out the functionality to various different scenarios.

The button and our table

Our table is made up using a named local model called "myItems" and we've added in a delete button into the header toolbar to make a table that looks like the following:

Screenshot 2019-02-27 at 10.41.32.png

The XML for that table looks like the following:

<Table width="auto" items="{myItems>/results}">
                        <headerToolbar>
                            <Toolbar>
                                <Title text="Todo" level="H2"/>
                                <ToolbarSpacer />
                                <Button icon="sap-icon://delete" tooltip="Delete All Tasks" press="onDeleteAllTasks" />
                            </Toolbar>
                        </headerToolbar>
                        <columns>
                            <Column>
                                <Text text="Title"/>
                            </Column>
                            <Column>
                                <Text text="Description"/>
                            </Column>
                            <Column>
                                <Text text="Due Date"/>
                            </Column>
                            <Column demandPopin="true" popinDisplay="Block" minScreenWidth="Tablet" width="8rem" />
                            <Column demandPopin="true" popinDisplay="Block" minScreenWidth="Tablet" width="5rem" />
                            <Column demandPopin="true" popinDisplay="Block" minScreenWidth="Tablet" width="8rem" />
                        </columns>
                        <items>
                            <ColumnListItem>
                                <cells>
                                    <Text text="{myItems>Title}" />
                                    <Text text="{myItems>Description}" />
                                    <Text text="{path : 'myItems>Duedate', type : 'sap.ui.model.type.Date', formatOptions : {style: 'medium'}}" />
                                    <Button icon="sap-icon://detail-view" type="Emphasized" text="View Details" press="navToTaskDetails" />
                                    <Button icon="sap-icon://edit" type="Emphasized" text="edit" press="onPressEdit" />
                                    <Button icon="sap-icon://delete" type="Reject" text="remove" press="onPressRemove" />
                                </cells>
                            </ColumnListItem>
                        </items>
                    </Table>

The button calls the function called "onDeleteAllTasks" which I want to get all of the items in that table and delete them by sending a delete request to the SAP backend.

So starting out we define our function and get to work collecting all of our data:

onDeleteAllTasks: function(){
    var tableDataModel = this.getView().getModel("myItems");
    var tableData = tableDataModel.getData();
}

So our first variable grabs our local json model called "myItems" and tableData will return to us our data which in this case is an array called "results". To delete our todo item we need to get the Id for each of our items and send off our delete request to the backend. Additional to that we want to make use of Promise.all which takes an array of promises, so we also need to define an array of promises and push each delete promise to our promise array to use later in our promise.all which looks like the below:

var arrayOfPromises = [];
    tableData.results.forEach(function(item){
    var removeRequest = this.getOdataModel().delete("/ToDoListSet(" + item.Id + ")");
        arrayOfPromises.push(removeRequest);
}.bind(this));

^^You might notice that I'm using my new oData model as talked about in part 2 of this blog series, as we don't natively have a .delete method. also the variable "removeRequest" is a promise, again outlined in my previous post.

The promise.all()

Promise.all takes an array or promises, we've already been building up our array of promises inside of our forEach loop and now we must pass in that array as the parameter. Next promise.all() inside of our .then() function returns to us an array of our resolves.

So the first item in our returned data will be the first promise we inserted into the promise array, and so on and so forth. In this case we don't actually care about returned data but rather we care that all of our delete requests have been processed which is the case when we enter our .then() function which all looks like this:

Promise.all(arrayOfPromises).then(function(promiseResolvesArray){
    this.setMyTableData(); //refresh our table data sending a read request.
}.bind(this));

So if I wanted to access the results of a request from the above I would say access promiseResolvesArray[0] which would equal the results of the first promise we inserted into our arrayOfPromises.

The final function now looks like this:

onDeleteAllTasks: function(){
    var tableDataModel = this.getView().getModel("myItems");
    var tableData = tableDataModel.getData();
    var arrayOfPromises = [];

    tableData.results.forEach(function(item){
        var removeRequest = this.getOdataModel().delete("/ToDoListSet(" + item.Id + ")");
        arrayOfPromises.push(removeRequest);
    }.bind(this));

    Promise.all(arrayOfPromises).then(function(promiseResolvesArray){
        this.setMyTableData();
    }.bind(this));
},

How does this look in practice in our network tab?

network-requests-promise-all.png

as you can see we've thrown off all of our delete requests to the backend and then at the end of them all being done and resolved we send off a refresh of our table data.

If we were to call all of them together and then send off our read request it would have probably returned to us after some of the requests were done and not all and generally wouldn't have been fit for purpose.

Conclusion

That's it! We very simply used a promise.all to ensure that all of our calls were done and resolved before triggering out other call. This is especially useful in a number of scenario's with our UI5 applications. This also concludes a rather basic introduction to promises, how we can use them to properly manage our data management and make for a nice and clean application.

···

Nathan Hand

SAP Applications consultant working as a UI5 developer, Lover of automation, technology, fantasy books & games.
comments powered by Disqus


Proudly powered by Canvas · Sign In