1

I am trying to modify the following Angular JS example so it works as a standalone file in a folder on a device not connected to the Internet. http://curran.github.io/screencasts/introToAngular/exampleViewer/#/44

I am ok with local-linking the two libraries at the end of the process, and I believe I have referenced the templates correctly. I have made a local array for the JSON data (countriesArray) but I don't know how to load this to the countries factory correctly, and without http. I have used the example's JSON URL temporarily, but there must be another error with the code as it only displays a blank screen.

Sorry for my ignorance.

<html ng-app="countryApp">
      <head>
        <meta charset="utf-8">
        <title>Angular.js Example</title>
        <script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.10/angular.min.js"></script>
        <script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.10/angular-route.min.js"></script>
        <script>
          var countryApp = angular.module('countryApp', ['ngRoute']);
    	  var countriesArray = [
      {
        "name": "China",
        "population": 1359821000,
        "flagURL": "//upload.wikimedia.org/wikipedia/commons/f/fa/Flag_of_the_People%27s_Republic_of_China.svg",
        "capital": "Beijing",
        "gdp": 12261
      },
      {
        "name": "India",
        "population": 1205625000,
        "flagURL": "//upload.wikimedia.org/wikipedia/en/4/41/Flag_of_India.svg",
        "capital": "New Delhi",
        "gdp": 4716
      },
      {
        "name": "United States of America",
        "population": 312247000,
        "flagURL": "//upload.wikimedia.org/wikipedia/en/a/a4/Flag_of_the_United_States.svg",
        "capital": "Washington, D.C.",
        "gdp": 16244
      }
    ];
          countryApp.config(function($routeProvider) {
            $routeProvider.
              when('/', {
                template: '<ul><li ng-repeat="country in countries"><a href="#/{{country.name | encodeURI}}">{{country.name}}</a></li></ul>',
                controller: 'CountryListCtrl'
              }).
              when('/:countryName', {
                template: '<h1>{{country.name}}</h1><ul><li>Flag: <img ng-src="{{country.flagURL}}" width="100"></li><li>Population: {{country.population | number }}</li><li>Capital: {{country.capital}}</li><li>GDP: {{country.gdp | currency }}</li></ul>',
                controller: 'CountryDetailCtrl'
              }).
              otherwise({
                redirectTo: '/'
              });
          });
    
          countryApp.factory('countries', function(){
            function getData(callback){
              $http({
                method: 'GET',
                url: 'http://curran.github.io/screencasts/introToAngular/examples/snapshots/snapshot42/countries.json',
                cache: true
              }).success(callback);
            }
    
            return {
              list: getData,
              find: function(name, callback){
                getData(function(data) {
                  var country = data.filter(function(entry){
                    return entry.name === name;
                  })[0];
                  callback(country);
                });
              }
            };
          });
    
          countryApp.controller('CountryListCtrl', function ($scope, countries){
            countries.list(function(countries) {
              $scope.countries = countries;
            });
          });
    
          countryApp.controller('CountryDetailCtrl', function ($scope, $routeParams, countries){
            countries.find($routeParams.countryName, function(country) {
              $scope.country = country;
            });
          });
    
          countryApp.filter('encodeURI', function(){
            return window.encodeURI;
          });
        </script>
      </head>
      <body>
        <div ng-view>{{countries}}</div>
      </body>
    </html>

1
  • You've forgotten to inject the $http service into the countries factory - most likely reason why it's not working. countryApp.factory('countries', function ($http) { Commented Mar 17, 2015 at 20:57

1 Answer 1

2

First, your array of countries is best placed in the factory than as a global variable.

Then, in the function getData of the factory, instead of making the http call just pass the array to your callback.

The factory looks as follows :

countryApp.factory('countries', function () {

    var countriesArray = [ ... ];

    function getData(callback) {
        return callback(countriesArray);
    }

    return {
        list: getData
    };
});

See fiddle


Avoid using a callback :

Your previous getData function used to pass the result to the callback, and the call from the controller was as follows :

countries.list(function (countries) {
    $scope.countries = countries;
});

But you can try to avoid using a callback for that and use a thenable instead :

1) Inject $q as a dependency of your factory

2) Rewrite getData as :

function getData() {
    return $q.when(countriesArray);
}

Note return $q.when(countriesArray) might seem a bit tricky if you are not used to. But it's not very difficult. Here we are literally saying "return when countriesArray is resolved". As countries array is not something asynchronous, we can return immediately, and $q.when wraps the object into a $q promise, therefore the caller can call getData as follows : getData.then(...)

3) Now, the call from the controller looks like :

countries.list().then(function (countries) {
    $scope.countries = countries;
});

See second fiddle


Fetch the data from a local json

If you want to load the data from a standalone file instead of putting them right in the factory (that would be a valuable choice), you will have to get back $http. You would call a local url, therefore there is absolutely no problem to run that on a device not connected to the internet :

 function getData() {
    return $http.get('countries.json');
}

$http returns a promise, therefore the call to getData stay unchanged : getData.then(...)

Sign up to request clarification or add additional context in comments.

6 Comments

Thank you Michael for your response. So quick, detailed and educational. I am now trying to find why the list template works but the individual country pages return curly brackets rather than the data, but it's already much further than I could have got in hours alone. Thank you so much.
That's because I didn't implemented the find method in the countries factory, as that was a bit over the question.
Thank you for the information. I have tried to add the method back in, and to do so taking into account what you said about avoiding callbacks. I understand methods a bit more now, but I can't get it to work, and think I need to make changes to getData to allow this.
filter returns an array, therefore, with this method you have to return the first. See fiddle.
You might be interested by some javascript libraries like lodash. That is useful especially when you have to work with objects or arrays in js, but not only. For example to find the country named China in the array with lodash would be : _.findWhere(countriesArray, {name: 'China'})
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.