cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Showing results for 
Search instead for 
Did you mean: 

Community Tip - You can change your system assigned username to something more personal in your community settings. X

Data URL instead of a XMLHTTPRequest, how?

jmikesell
16-Pearl

Data URL instead of a XMLHTTPRequest, how?

Help mentions this as a way to access local resources loaded in downloaded/offline experiences. Does anyone know what this method looks like in practice? I've spent a few hours Googleing and trying stuff without success. I have been using XMLHTTPRequest to load a local file of JSON data, but as noted it doens't work offline.

 

So if i have a local file at "app/resources/Uploaded/myfile.json" how would load this to a variable using the "Data URL" method? I can say that below does not work.

myData= JSON.parse("data:,/app/resources/Uploaded/myfile.json")
// or
myData = JSON.parse("data:/app/resources/Uploaded/myfile.json")

 

ACCEPTED SOLUTION

Accepted Solutions

After trying every single method I could find on the internet I finally found one that does what I want. While @ClayHelberg proposed solution to use loadResourceScript does accomplish this it requires the object name to be in the JSON file which is not proper and makes it a JS file, as in the file needs to begin with myObjectName = { .... }, where a properly formatted JSON file should begin with just a curly brace "{". Having the name in the file creates dependencies between the JSON file and main JS file I don't want to manage. This was the benefit of my previous XMLHttp method you got to define the name of the object as you read it in.

 

So here is what worked for me, I have confirmed this works in both online and downloaded offline experiences.

 

$http.get('app/resources/Uploaded/test.json').success(function (data) {
    httpJSON = data;
    console.log('$http load = ', httpJSON); //test the load
});

 

View solution in original post

10 REPLIES 10

Do you have control of the JSON files, and are they static? If so, you could try moving the variable assignment into the file and then load it as a regular JS file with $scope.app.nf.loadResourceScript().

@ClayHelberg thanks in advance for the help.

I think i'm having scope problems with this method. I can see the object loaded via loadResourceScript in the global scope in debug but i can't access it from my experience JS.

 

So if this what my JSON file "myJSONFile.json" looks like:

myJSON = {
   "0": {
      "name": {
         "first": "Bill",
         "last": "Barner"
      }
   },
   "1": {
      "name": {
         "first": "Joe",
         "last": "Smoe"
      }
   }
};

And i do this in the Home.JS:

$scope.app.fn.loadResourceScript("Uploaded/myJSONFile.json");
//that loads in to global scope

console.log(myJSON); //but something like this doesn't work

You might need to stash it in the $scope to make it visible. So instead of

myJSON = {
  ...
}

try this:

$scope.myJSON = {
   ...
}

Hey, I found your interesting post and tried it on my own. I took @jmikesell JSON file and safed it in the upload folder. 

I modified the code for the home.js a little bit:

 

$scope.readjson = function() {
$scope.app.fn.loadResourceScript("Uploaded/myJSONFile.json");
setTimeout(function () {
console.log(myJSON);
clearInterval(timerId);
}
, 500);
}

 

I found out, that, when calling the function once and without a time delay, you get an error:

"Uncaught ReferenceError: myJSON is not defined"

As soon, as you call it a second time or with a delay, everything works fine. 

Yes, loadResourceScript() probably should have been defined to return a Promise, so you could write something like this:

$scope.readjson = function() {
  $scope.app.fn.loadResourceScript("Uploaded/myJSONFile.json")
    .then(function() { console.log(myJSON); });
}

The problem with the timeout approach is that it's not general. For example, if myJSONfile.json is very large, or if the data connection is slow, 500ms might not be long enough to ensure that the entire file is read and parsed.

 

After looking at it again, loadResourceScript() is probably not the best approach for this, due to these issues. It might be better to write your own version that handles the asynchronous loading better. Something like this:

$scope.asyncLoadScript = function (fname) {
  return new Promise((resolve, reject) => {
    if(fname) {
      var head = document.head || document.getElementsByTagName('head')[0],
          script = document.createElement('script');
      script.async = true;
      script.onload = resolve;
      script.onerror = reject;
      script.type = 'text/javascript';
      script.src=fname;
      head.appendChild(script);
    }
  });
}

Now you can use the promise to make sure your code doesn't try to use the JSON object before it's ready:

$scope.testJSON = function() {
  $scope.asyncLoadScript("app/resources/Uploaded/JSONTest.js")
    .then(function() { console.log(myJSON) }, 
          function() { console.log("Oops, something went wrong"); });
}

 

 

Thanks, that works great!

After trying every single method I could find on the internet I finally found one that does what I want. While @ClayHelberg proposed solution to use loadResourceScript does accomplish this it requires the object name to be in the JSON file which is not proper and makes it a JS file, as in the file needs to begin with myObjectName = { .... }, where a properly formatted JSON file should begin with just a curly brace "{". Having the name in the file creates dependencies between the JSON file and main JS file I don't want to manage. This was the benefit of my previous XMLHttp method you got to define the name of the object as you read it in.

 

So here is what worked for me, I have confirmed this works in both online and downloaded offline experiences.

 

$http.get('app/resources/Uploaded/test.json').success(function (data) {
    httpJSON = data;
    console.log('$http load = ', httpJSON); //test the load
});

 

Yes, that's a much more elegant solution than mine. Nicely done!

Is it also possible to write into the json-file?

I tried this:

$scope.jayson = function() {
var jsonfile = $http.get('app/resources/Uploaded/test.json');
var Username = $scope.app.mdl['CurrentSessionInfo'].svc['GetCurrentUser'].data[0].result;
console.log("Trying to save. Username: "+Username);

$http.post('app/resources/Uploaded/test.json',{'id': Username})
.success(function(response) {
$scope.usersData = response.users;
});
}

but it throws an error: 

ionic.bundle.min.js:135 POST http://localhost:3000/resource/TestTablet/dist/app/resources/Uploaded/test.json 405 (Method Not Allowed)

No, I don't think you'll be able to store a new JSON file from a script. If your goal is to persist some data across sessions, you will probably have better luck using the Thingworx connection to store data there. You could also try using localStorage, but I have no idea if that would work on devices running Vuforia View.

Announcements

Top Tags