10

In SAPUI5/OpenUI5, I have a JSONModel I populate by a file from server:

var oModel = new JSONModel();
oModel.loadData("http://127.0.0.1/data/config.json");
console.log(JSON.stringify(oModel.getData()));

The console logs undefined since the request is asynchronous.
How to make it synchronous so console.log() is called after the data was loaded?

Mike
  • 14,010
  • 29
  • 101
  • 161
Benvorth
  • 7,416
  • 8
  • 49
  • 70

6 Answers6

20

Using synchronous ajax requests is not recommended as it blocks the UI and will probably result in a warning in the console.

You can attach to the Model.requestCompleted event to access the asynchronously loaded data:

oModel.attachRequestCompleted(function() {
        console.log(oModel.getData());
    });
schnoedel
  • 3,918
  • 1
  • 13
  • 17
  • 1
    Please note, if you register this event call after the data has been loaded, it will not execute its code like _promises_ do. – Martin Braun Aug 04 '17 at 13:17
  • 3
    Thats right. And as you can call loadData() multiple times, the event can be fired multiple times too. It would have been nice if loadData() would return a promise. But it seems like they don't like promises in walldorf ;) – schnoedel Aug 04 '17 at 18:07
  • For mine particular case, I intent to preload to global model. From a local model, I cannot attachRequestCompleted to global model before it is loaded. I also cannot access local model from global side. – fury Feb 06 '18 at 12:12
  • 2
    @MartinBraun Since 1.64, [`dataLoaded` can be used](https://stackoverflow.com/a/63892279/5846045) for that. – Boghyon Hoffmann Mar 26 '21 at 13:52
3

The keyword you are looking for is "Deferred"-object --> it enables you to wait for an AJAX request in SAPUI5.

Check this for SAPUI5 context: SAPUI5 Wait for an Deferred-Object // wait for .done() function

Community
  • 1
  • 1
dotchuZ
  • 2,621
  • 11
  • 39
  • 65
3

Since UI5 version 1.64.0, the API loadData returns a Promise instance:

logLoadedData: async function () {
  const jsonModel = new JSONModel();
  await jsonModel.loadData("<host>/data/config.json");
  console.log(jsonModel.getData()); // after the loadData promise is resolved
},

Alternatively, there is also the API dataLoaded which returns a promise as well. It will resolve when all requests sent by loadData are finished. Here is a syntax without async-await:

doSomethingWith: async function (jsonModel) {
  // Not sure if the model has all data loaded? Just use dataLoaded:
  await jsonModel.dataLoaded();
  console.log(jsonModel.getData());
},

The API loadData is also called internally when the constructor function of JSONModel was called with a string (URL) as an argument. In that case, dataLoaded might come in handy as well.

Boghyon Hoffmann
  • 17,103
  • 12
  • 72
  • 170
1

You can use the attachRequestCompleted-listener from the Model [1]

model.attachRequestCompleted(function(){
    console.log(this.getData()); //"this" is the model
});

Another function to use is

$.get(url, function(response){
    console.log(response);
    model.setData(response);
});
// or
$.ajax(url, {
    success: function(){
        console.log(response);
        model.setData(response);
    }
});

This has the advantage that you can configure the request with every setting that jQuery.ajax accepts [2]

herrlock
  • 1,454
  • 1
  • 13
  • 16
0

Another way to achieve this is to use the attachEventOnce method from EventProvider.

oModel.attachEventOnce("requestCompleted", function(oEvent) {
    console.log(JSON.parse(oEvent.getParameter("response").responseText));
}, this);

It's best to use this approach when you only need to react to one request, and not all. Otherwise, if you use oModel.attachRequestCompleted(...), all requests will go through the same handler function.

You can also use method chaining to make this a little easier.

oModel.attachEventOnce(...) returns the object that called the method, so you can load your data and handle the callback all in one statement.

oModel.attachEventOnce("requestCompleted", function(oEvent) {
    console.log(JSON.parse(oEvent.getParameter("response").responseText));
}, this).loadData("http://127.0.0.1/data/config.json");

This will first execute the loadData() request, and then console the response when the request has been completed. It will only use the callback function the first time a request is made. Subsequent requests will not go through the callback function.

If you want ALL requests to go through the SAME callback function, you can do the same thing but using oModel.attachRequestCompleted(...)

oModel.attachRequestCompleted(function(oEvent) {
    console.log(JSON.parse(oEvent.getParameter("response").responseText));
}, this).loadData("http://127.0.0.1/data/config.json");

This will execute the loadData() request, console the response, and also console the response of all subsequent requests.

NOTE: Be careful using this in the callback functions. If you don't pass this as a parameter of the attachRequestCompleted(...) or attachEventOnce(...) methods, then this will lose it's original context as the controller, and inherit the context of the object calling the function. herrlock's answer demonstrates how the context of this changes.

Event Provider API Reference

Kyle
  • 614
  • 1
  • 5
  • 13
-3

Turned out there is a parameter in the .loadData() function to create a sync- call:

oModel.loadData("http://127.0.0.1/data/config.json", "", false);

See API-Reference as well.

Benvorth
  • 7,416
  • 8
  • 49
  • 70
  • 1
    But this might result in a warning in the console because synchronous ajax requests are not recommended as they will block the UI. You can attach to the `Model.requestCompleted` event to access the asynchronously loaded data: `oModel.attachRequestCompleted(function() {console.log(oModel.getData());});` – schnoedel Jan 20 '16 at 19:35
  • The documentation now explicitly warns to avoid setting the `bAsync` to `false`: https://github.com/SAP/openui5/commit/6a1b04f47564807d43db653fcc9c61c56e0f0a75. Please avoid creating another sync XHR which UI5 is already full of. For apps using UI5 version ≥ 1.64, a promise can be used: https://stackoverflow.com/a/63892279/5846045 – Boghyon Hoffmann Sep 14 '20 at 21:57