AngularJS: fabryczny $ http.get plik JSON

84

Chcę rozwijać się lokalnie, używając tylko zakodowanego na stałe pliku JSON. Mój plik JSON jest następujący (ważny po umieszczeniu w walidatorze JSON):

{
    "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" }
            ]
        }
    ]
}

Mój kontroler, fabryka i html działały, gdy JSON został zakodowany na stałe w fabryce. Jednak teraz, gdy zastąpiłem JSON kodem $ http.get, nie działa. Widziałem wiele różnych przykładów zasobów $ http i $, ale nie jestem pewien, gdzie się udać. Szukam najprostszego rozwiązania. Po prostu próbuję pobrać dane dla instrukcji ng-repeat i podobnych.

Fabryka:

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
});

Wszelka pomoc jest mile widziana. Dzięki!

jackety
źródło
1
To nie działa? Co to robi? Czy zgłasza błąd? Czy w konsoli JavaScript jest jakieś wyjście?
Josh Lee,
Konsola po prostu wyświetla komunikat „Nie udało się załadować zasobu”, a następnie zawiera ścieżkę do pliku console.json. Więc z jakiegoś powodu go nie ładuje. Moja fabryka i JSON są dokładnie takie, jak widać powyżej. Kiedy zakoduję JSON w fabryce, to działa.
jackstacks
1
Czego używasz jako zaplecza? NodeJs lub prosty serwer oparty na Pythonie czy coś innego?
callmekatootie
Po prostu staram się rozwijać z wyłączeniem zaplecza (Railsów). Więc JSON to tylko plik .json z zakodowanymi powyżej danymi. Przypuszczalnie podobny do tego, co renderowałby backend.
jackstacks
Możesz nie potrzebować „.data” w odpowiedzi .. zmień na - „return response;”, chyba że zwrócony kod JSON jest umieszczony w obiekcie „data”.
Bhaskara Kempaiah

Odpowiedzi:

218

OK, oto lista spraw, którym należy się przyjrzeć:

1) Jeśli nie używasz żadnego serwera WWW i tylko testujesz z plikiem: //index.html, prawdopodobnie masz problemy z zasadami tego samego pochodzenia. Widzieć:

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

Wiele przeglądarek nie zezwala plikom hostowanym lokalnie na dostęp do innych plików obsługiwanych lokalnie. Firefox na to zezwala, ale tylko wtedy, gdy ładowany plik znajduje się w tym samym folderze co plik HTML (lub podfolder).

2) Funkcja sukcesu zwrócona z $ http.get () już dzieli obiekt wynikowy za Ciebie:

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

Dlatego wywołanie sukcesu z funkcją (odpowiedzią) i zwrócenie odpowiedzi.data jest zbędne.

3) Funkcja sukcesu nie zwraca wyniku funkcji, którą ją przekazujesz, więc nie robi tego, co myślisz, że robi:

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

To jest bliżej tego, co zamierzałeś:

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

4) Ale to, co naprawdę chcesz zrobić, to zwrócić odwołanie do obiektu z właściwością, która zostanie zapełniona po załadowaniu danych, więc coś takiego:

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 rozpocznie się od null, a po załadowaniu danych wskaże go.

Alternatywnie możesz zwrócić rzeczywistą obietnicę zwrotu $ http.get i użyć tego:

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

Następnie możesz użyć tej wartości asynchronicznie w obliczeniach w kontrolerze:

$scope.foo = "Hello World";
mainInfo.success(function(data) { 
    $scope.foo = "Hello "+data.contentItem[0].username;
});
Karen Zilles
źródło
27
Hej, to odpowiedź ORAZ kątowy kurs $ http za tę samą cenę - dobra odpowiedź!
Mat
4
W swoim wyjaśnieniu w punkcie 4), czy 'return obj' nie zostanie wywołane przed rozwiązaniem $ http.get ()? Po prostu pytam, bo myślę, że to właśnie się ze mną dzieje.
Pathsofdesign
3
Tak, to będzie. Ale zamknięcie wywoływane po rozwiązaniu $ http.get () zachowuje odniesienie do 'obj'. Wypełni właściwość content, której możesz następnie użyć.
Karen Zilles
Co jest problematyczne w przypadku używania drugiej formy nr 3 w porównaniu z użyciem nr 4?
Spencer,
1
Połączone wywołanie zwrotne .success () zostało wycofane. Zamiast tego użyj .then (sukces, błąd).
Timothy Perez
21

Chciałem zauważyć, że czwarta część zaakceptowanej odpowiedzi jest błędna .

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;    
});

Powyższy kod, który napisał @Karl Zilles zakończy się niepowodzeniem, ponieważ objzawsze zostanie zwrócony przed otrzymaniem danych (w związku z tym wartość zawsze będzie null), a to dlatego, że wykonujemy wywołanie asynchroniczne.

Szczegóły podobnych pytań omówiono w tym poście


W Angular użyj $promise do obsługi pobranych danych, gdy chcesz wykonać wywołanie asynchroniczne.

Najprostsza wersja to

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;
});

Powodem, dla którego nie używam successi errorwłaśnie dowiedziałem się z dokumentu , te dwie metody są przestarzałe.

$httpSukces metody dziedzictwo obietnicy i błąd są nieaktualne. thenZamiast tego użyj metody standardowej .

Qiang
źródło
2
Użyj return $http.get('content.json');w fabryce, w przeciwnym razie zwrot jest zerowy.
Francesco
2
Hej, tylko jedno ostrzeżenie. Powodem, dla którego to działa (w przeciwieństwie do twojej odpowiedzi tutaj) jest to, że zwracasz odniesienie do obiektu. Funkcja sukcesu ma również odniesienie do tego samego obiektu. Kiedy funkcja Ajax w końcu zwraca, aktualizuje właściwość „content” w oryginalnym zwróconym obiekcie. Spróbuj. :-)
Karen Zilles
1
Ps .successjest teraz przestarzały. Użyj .thenzamiast tego. docs.angularjs.org/api/ng/service/$http
redfox05
4

ta odpowiedź bardzo mi pomogła i wskazała mi właściwy kierunek, ale to, co zadziałało dla mnie i, mam nadzieję, innych, to:

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);
    });    
});
jp093121
źródło
6
nie powinieneś robić czegoś takiego w ramach usługi?
Katana24
Nigdy nie rób tego w kontrolerze! zły! Powinieneś mieć to napisane jako usługa. Chociaż sposób wywołania wartości json nie jest zły, powinieneś mieć usługę zwracającą obietnicę, że nie zrobi tego w kontrolerze. Z punktu widzenia wielokrotnego użytku jest to okropne. Na przykład, wykonujesz i $ http.get () za każdym razem, gdy ładujesz kontroler, a nie masz buforowanej wersji wywołania w usłudze.
Downpour046
1

Mam w przybliżeniu ten problem. Potrzebuję debugowania aplikacji AngularJs z Visual Studio 2013.

Domyślnie IIS Express ograniczony dostęp do plików lokalnych (takich jak json).

Ale po pierwsze: JSON ma składnię JavaScript.

Po drugie: dozwolone są pliki javascript.

Więc:

  1. zmień nazwę JSON na JS ( data.json->data.js).

  2. prawidłowe polecenie ładowania ($http.get('App/data.js').success(function (data) {...

  3. ładowanie skryptu data.js do strony ( <script src="App/data.js"></script>)

Następnie użyj załadowanych danych w zwykły sposób. To oczywiście tylko obejście.

Alex Sam
źródło
1

++ To zadziałało dla mnie. Jest to vanilla javascirptdobre w przypadkach użycia, takich jak porządkowanie podczas testowania z ngMocksbiblioteką:

<!-- 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>

To jest twój javascriptplik, który zawiera JSONdane

// 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"
};

Na koniec pracuj z danymi JSON w dowolnym miejscu kodu

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

Uwaga: - To jest świetne do testowania, a nawet karma.conf.jsakceptuje te pliki do uruchamiania testów, jak pokazano poniżej. Polecam to tylko do porządkowania danych i testing/developmentśrodowiska.

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

Mam nadzieję że to pomoże.

++ Zbudowany na podstawie tej odpowiedzi https://stackoverflow.com/a/24378510/4742733

AKTUALIZACJA

Łatwiejszym sposobem, który zadziałał dla mnie, jest po prostu umieszczenie functionna dole kodu zwracającego cokolwiek 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"
   }
}
Aakash
źródło