84

I am looking to develop locally with just a hardcoded JSON file. My JSON file is as follows (valid when put into JSON validator):

{
    "contentItem": [
            {
            "contentID" : "1", 
            "contentVideo" : "file.mov",
            "contentThumbnail" : "url.jpg",
            "contentRating" : "5",
            "contentTitle" : "Guitar Lessons",
            "username" : "Username", 
            "realname" : "Real name",
            "contentTags" : [
                { "tag" : "Guitar"},
                { "tag" : "Intermediate"},
                { "tag" : "Chords"}
            ],      
            "contentAbout" : "Learn how to play guitar!",
            "contentTime" : [
                { "" : "", "" : "", "" : "", "" : ""},
                { "" : "", "" : "", "" : "", "" : ""}
            ],          
            "series" :[
                { "seriesVideo" : "file.mov", "seriesThumbnail" : "url.jpg", "seriesTime" : "time", "seriesNumber" : "1", "seriesTitle" : "How to Play Guitar" },
                { "videoFile" : "file.mov", "seriesThumbnail" : "url.jpg", "seriesTime" : "time", "seriesNumber" : "2", "seriesTitle" : "How to Play Guitar" }
            ]
        },{
            "contentID" : "2", 
            "contentVideo" : "file.mov",
            "contentThumbnail" : "url.jpg",
            "contentRating" : "5",
            "contentTitle" : "Guitar Lessons",
            "username" : "Username", 
            "realname" : "Real name",
            "contentTags" : [
                { "tag" : "Guitar"},
                { "tag" : "Intermediate"},
                { "tag" : "Chords"}
            ],      
            "contentAbout" : "Learn how to play guitar!",
            "contentTime" : [
                { "" : "", "" : "", "" : "", "" : ""},
                { "" : "", "" : "", "" : "", "" : ""}
            ],          
            "series" :[
                { "seriesVideo" : "file.mov", "seriesThumbnail" : "url.jpg", "seriesTime" : "time", "seriesNumber" : "1", "seriesTitle" : "How to Play Guitar" },
                { "videoFile" : "file.mov", "seriesThumbnail" : "url.jpg", "seriesTime" : "time", "seriesNumber" : "2", "seriesTitle" : "How to Play Guitar" }
            ]
        }
    ]
}

I've gotten my controller, factory, and html working when the JSON was hardcoded inside the factory. However, now that I've replaced the JSON with the $http.get code, it doesn't work. I've seen so many different examples of both $http and $resource but not sure where to go. I'm looking for the simplest solution. I'm just trying to pull data for ng-repeat and similar directives.

Factory:

theApp.factory('mainInfoFactory', function($http) { 
    var mainInfo = $http.get('content.json').success(function(response) {
        return response.data;
    });
    var factory = {}; // define factory object
    factory.getMainInfo = function() { // define method on factory object
        return mainInfo; // returning data that was pulled in $http call
    };
    return factory; // returning factory to make it ready to be pulled by the controller
});

Any and all help is appreciated. Thanks!

Mo.
  • 26,306
  • 36
  • 159
  • 225
jstacks
  • 2,437
  • 8
  • 32
  • 48
  • 1
    It doesn’t work? What does it do? Does it throw an error? Is there any output in the JavaScript console? – Josh Lee Jun 05 '13 at 02:21
  • The console just says "Failed to load resource" and then has the console.json file path. So it's not loading it for some reason. My factory and JSON are exactly as you see above. When I hardcode the JSON into the factory, it works. – jstacks Jun 05 '13 at 02:37
  • 1
    What are you using as your backend? NodeJs or a simple python based server or something else? – callmekatootie Jun 05 '13 at 02:44
  • I'm just trying to develop excluding the backend (Rails). So the JSON is just a .json file with the data above hardcoded. Presumably similar to what the backend would render. – jstacks Jun 05 '13 at 02:47
  • You may not need ".data" on the response.. change to -- "return response;", unless your returned JSON is bundled inside a 'data' object. – Bhaskara Kempaiah Jun 05 '13 at 04:18

5 Answers5

218

Okay, here's a list of things to look into:

1) If you're not running a webserver of any kind and just testing with file://index.html, then you're probably running into same-origin policy issues. See:

https://code.google.com/archive/p/browsersec/wikis/Part2.wiki#Same-origin_policy

Many browsers don't allow locally hosted files to access other locally hosted files. Firefox does allow it, but only if the file you're loading is contained in the same folder as the html file (or a subfolder).

2) The success function returned from $http.get() already splits up the result object for you:

$http({method: 'GET', url: '/someUrl'}).success(function(data, status, headers, config) {

So it's redundant to call success with function(response) and return response.data.

3) The success function does not return the result of the function you pass it, so this does not do what you think it does:

var mainInfo = $http.get('content.json').success(function(response) {
        return response.data;
    });

This is closer to what you intended:

var mainInfo = null;
$http.get('content.json').success(function(data) {
    mainInfo = data;
});

4) But what you really want to do is return a reference to an object with a property that will be populated when the data loads, so something like this:

theApp.factory('mainInfo', function($http) { 

    var obj = {content:null};

    $http.get('content.json').success(function(data) {
        // you can do some processing here
        obj.content = data;
    });    

    return obj;    
});

mainInfo.content will start off null, and when the data loads, it will point at it.

Alternatively you can return the actual promise the $http.get returns and use that:

theApp.factory('mainInfo', function($http) { 
    return $http.get('content.json');
});

And then you can use the value asynchronously in calculations in a controller:

$scope.foo = "Hello World";
mainInfo.success(function(data) { 
    $scope.foo = "Hello "+data.contentItem[0].username;
});
Cœur
  • 37,241
  • 25
  • 195
  • 267
Karen Zilles
  • 7,633
  • 3
  • 34
  • 33
  • 27
    Hey that's a response AND an angular $http course for the same price - Nice answer ! – Mat Oct 08 '13 at 21:10
  • 4
    In your explanation under 4), won't the 'return obj' get called before the $http.get() is resolved? Just asking because I think that's what happening to me. – Pathsofdesign Jan 16 '14 at 06:57
  • 3
    Yes it will. But the closure called when $http.get() is resolved keeps a reference to 'obj'. It will fill in the content property which you can then use. – Karen Zilles Jan 16 '14 at 21:39
  • What is problematic with using the second form of #3 over using #4? – Spencer Sep 18 '14 at 18:33
  • If you "return mainInfo" in #3, you'll just be returning a null, because the ajax request will not have resolved. In #4 you return (a reference to) an object with a property of null. Both your controller code and the ajax request have this reference. When the ajax resolves, it updates the object to point at the results. You need the extra level of indirection to make sure communication takes place. – Karen Zilles Sep 18 '14 at 22:54
  • My problem was the first on your list. "Locally hosted files couldn't access other locally hosted files". I tried it in FF and it worked perfectly.. What's the best method to learn Angular while working locally and what test data can I use if most browsers don't allow this? – KidBilly Feb 13 '15 at 14:45
  • I typically setup apache on my machine. You can probably setup a lighter weight web server using node.js. – Karen Zilles Feb 13 '15 at 18:18
  • @KarlZilles Thanks for this excellent explanation - I was just running into this problem. When you say in #4 that the factory returns a reference to `obj`, this means that the controller and the $http request in the factory are both looking at the same exact object, correct? This is why it is able to be updated after the factory function has returned. My (incorrect) assumption was that when you called the factory from the controller, it would pass only a *copy* of the object, and not a link to the same object. What would be a good term to search if I want to learn more about this behavior? – Deimyts Jun 22 '15 at 13:12
  • @Deimyts Yes, exactly. Maybe "javascript object reference". The terms are so generic that my google search didn't return good results. :-/ – Karen Zilles Jun 22 '15 at 21:17
  • Woops downvoted accidently, should have been an upvote. Anyway, helped me very much! – Matthias Müller Sep 18 '15 at 07:16
  • 1
    The chained callback .success() has been deprecated. Use .then(success, error) instead. – Timothy Perez Oct 21 '15 at 17:41
21

I wanted to note that the fourth part of Accepted Answer is wrong .

theApp.factory('mainInfo', function($http) { 

var obj = {content:null};

$http.get('content.json').success(function(data) {
    // you can do some processing here
    obj.content = data;
});    

return obj;    
});

The above code as @Karl Zilles wrote will fail because obj will always be returned before it receives data (thus the value will always be null) and this is because we are making an Asynchronous call.

The details of similar questions are discussed in this post


In Angular, use $promise to deal with the fetched data when you want to make an asynchronous call.

The simplest version is

theApp.factory('mainInfo', function($http) { 
    return {
        get:  function(){
            $http.get('content.json'); // this will return a promise to controller
        }
});


// and in controller

mainInfo.get().then(function(response) { 
    $scope.foo = response.data.contentItem;
});

The reason I don't use success and error is I just found out from the doc, these two methods are deprecated.

The $http legacy promise methods success and error have been deprecated. Use the standard then method instead.

Community
  • 1
  • 1
Qiang
  • 1,468
  • 15
  • 18
  • 2
    Use ``return $http.get('content.json');`` in the factory, otherwise return is null. – Francesco Aug 22 '15 at 10:20
  • 2
    Hey, just a heads up. The reason it does work (contrary to your answer here) is that you're returning a reference to an object. The success function also has a reference to that same object. When the ajax function does eventually return it updates the "content" property in the original object that was returned. Try it. :-) – Karen Zilles Sep 11 '15 at 21:12
  • 1
    P.s. `.success` is now deprecated. Use `.then` instead. https://docs.angularjs.org/api/ng/service/$http – redfox05 Dec 08 '15 at 15:02
4

this answer helped me out a lot and pointed me in the right direction but what worked for me, and hopefully others, is:

menuApp.controller("dynamicMenuController", function($scope, $http) {
$scope.appetizers= [];
$http.get('config/menu.json').success(function(data) { 
    console.log("success!");
    $scope.appetizers = data.appetizers;
        console.log(data.appetizers);
    });    
});
bjb568
  • 11,089
  • 11
  • 50
  • 71
jp093121
  • 1,752
  • 19
  • 13
  • 6
    shouldn't you be doing something like this inside a service? – Katana24 Aug 03 '14 at 19:33
  • Never do this in a controller! bad! You should have this written as a service. Although the way you called the json value isn't wrong, you should have a Service returning the promise not doing this in the controller. From a reusability standpoint as well this is horrible. For instance, you're performing and $http.get() every single time you load the controller vs having a cached version of the call in a service. – Downpour046 Jul 09 '16 at 13:54
1

I have approximately these problem. I need debug AngularJs application from Visual Studio 2013.

By default IIS Express restricted access to local files (like json).

But, first: JSON have JavaScript syntax.

Second: javascript files is allowed.

So:

  1. rename JSON to JS (data.json->data.js).

  2. correct load command ($http.get('App/data.js').success(function (data) {...

  3. load script data.js to page (<script src="App/data.js"></script>)

Next use loaded data an usual manner. It is just workaround, of course.

Adam Boduch
  • 11,023
  • 3
  • 30
  • 38
Alex Sam
  • 7
  • 2
1

++ This worked for me. It's vanilla javascirpt and good for use cases such as de-cluttering when testing with ngMocks library:

<!-- specRunner.html - keep this at the top of your <script> asset loading so that it is available readily -->
<!--  Frienly tip - have all JSON files in a json-data folder for keeping things organized-->
<script src="json-data/findByIdResults.js" charset="utf-8"></script>
<script src="json-data/movieResults.js" charset="utf-8"></script>

This is your javascript file that contains the JSON data

// json-data/JSONFindByIdResults.js
var JSONFindByIdResults = {
     "Title": "Star Wars",
     "Year": "1983",
     "Rated": "N/A",
     "Released": "01 May 1983",
     "Runtime": "N/A",
     "Genre": "Action, Adventure, Sci-Fi",
     "Director": "N/A",
     "Writer": "N/A",
     "Actors": "Harrison Ford, Alec Guinness, Mark Hamill, James Earl Jones",
     "Plot": "N/A",
     "Language": "English",
     "Country": "USA",
     "Awards": "N/A",
     "Poster": "N/A",
     "Metascore": "N/A",
     "imdbRating": "7.9",
     "imdbVotes": "342",
     "imdbID": "tt0251413",
     "Type": "game",
     "Response": "True"
};

Finally, work with the JSON data anywhere in your code

// working with JSON data in code
var findByIdResults = window.JSONFindByIdResults;

Note:- This is great for testing and even karma.conf.js accepts these files for running tests as seen below. Also, I recommend this only for de-cluttering data and testing/development environment.

// extract from karma.conf.js
files: [
     'json-data/JSONSearchResultHardcodedData.js',
     'json-data/JSONFindByIdResults.js'
     ...
]

Hope this helps.

++ Built on top of this answer https://stackoverflow.com/a/24378510/4742733

UPDATE

An easier way that worked for me is just include a function at the bottom of the code returning whatever JSON.

// within test code
let movies = getMovieSearchJSON();
.....
...
...
....
// way down below in the code
function getMovieSearchJSON() {
      return {
         "Title": "Bri Squared",
         "Year": "2011",
         "Rated": "N/A",
         "Released": "N/A",
         "Runtime": "N/A",
         "Genre": "Comedy",
         "Director": "Joy Gohring",
         "Writer": "Briana Lane",
         "Actors": "Brianne Davis, Briana Lane, Jorge Garcia, Gabriel Tigerman",
         "Plot": "N/A",
         "Language": "English",
         "Country": "USA",
         "Awards": "N/A",
         "Poster": "http://ia.media-imdb.com/images/M/MV5BMjEzNDUxMDI4OV5BMl5BanBnXkFtZTcwMjE2MzczNQ@@._V1_SX300.jpg",
         "Metascore": "N/A",
         "imdbRating": "8.2",
         "imdbVotes": "5",
         "imdbID": "tt1937109",
         "Type": "movie",
         "Response": "True"
   }
}
Community
  • 1
  • 1
Aakash
  • 21,375
  • 7
  • 100
  • 81