0

I have the following code:

for( let filename of ["action", "course", "program", "staff", "student"]){
    http.get('data/'+filename+'.json').map(res => res.json()).subscribe(
               staff => localStorage.setItem(filename, JSON.stringify(staff)
            ));
}

transpiled into :

for (var _i = 0, _a = ["action", "course", "program", "staff", "student"]; _i < _a.length; _i++) {
    var filename = _a[_i];
    http.get('data/' + filename + '.json')
        .map(function (res) { return res.json(); })
            .subscribe(function (staff) { return localStorage.setItem(filename, JSON.stringify(staff)); });
            }

Which should loop though an array of stings that has the names of files that has json objects that I wish to load in the local storage.

Problem goes as follows:
I only get the value of the last element stored in the local storage. the rest is gone.

reason is:
When the time comes to call the localStorage.setItem in the .subscribe() The value stored in filename is equal to the last value in the array.

Ideas I got

  1. Make The HTTP request synchronous {sure its a bad idea} but the application actually depend on this data thus I don't mind blocking the ui till its done. | anyhow ng2 documentations shows no clue of how to do that...
  2. use Promises which i have no clue how with angular 2
  3. find a way to make sure that filename value is some how stored for each loop
  4. forget about doing it in a loop and write it down hard-coded

my code

app.ts

import {Component,  bootstrap, NgFor,CORE_DIRECTIVES,OnInit,provide} from 'angular2/angular2';
import {RouteConfig,  ROUTER_DIRECTIVES, ROUTER_PROVIDERS, RouteParams, RouteData,
    LocationStrategy, HashLocationStrategy,Location} from 'angular2/router';
import {Http, HTTP_PROVIDERS} from 'angular2/http';

@Component({    
    selector: 'app-holder',
    template: `<router-outlet></router-outlet>`,
    viewProviders: [HTTP_PROVIDERS],
    directives: [ROUTER_DIRECTIVES]]
})
export class MainComponent {
    constructor(http: Http) {
        if(dataStorage.shouldLoad()){
            for( filename of ["action", "actiontype", "advisertype", "course", "program", "staff", "student"]){
                http.get('data/'+filename+'.json').map(res => res.json()).subscribe(staff => localStorage.setItem(filename, JSON.stringify(staff)));
            }
        }
    }
}

bootstrap(MainComponent,[ROUTER_PROVIDERS, provide(LocationStrategy, {useClass: HashLocationStrategy})]);

index.html

<html>
<head>
    <title>Star App</title>
    <!-- Bootstrap -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <script src="../node_modules/systemjs/dist/system.src.js"></script>
    <script src="../node_modules/angular2/bundles/angular2.dev.js"></script>
    <script src="../node_modules/angular2/bundles/router.dev.js"></script>
    <script src="../node_modules/angular2/bundles/http.dev.js"></script>
    <script>
        System.config({
            packages: {'app': {defaultExtension: 'js'}}
        });
        System.import('app/app');
    </script>
</head>
<body>    
    <app-holder>Please wait while Loading...</app-holder>
</body>
</html>

backGround checkes:

Community
  • 1
  • 1
NazS2
  • 29
  • 7

3 Answers3

1

You just fell to the infamous closure in a loop issue.

You'd think ES6 would save you from this with its block scoping? Yes, it does, but only if you actually declare your variable:

for (let filename of ["action", "course", "program", "staff", "student"]) {
//   ^^^ 
    http.get('data/'+filename+'.json')
      .map(res => res.json())
      .subscribe(staff => localStorage.setItem(filename, JSON.stringify(staff)));
}
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I gave that a shot it didn't work. The issue is still present. – NazS2 Dec 02 '15 at 20:53
  • Ecma 5 I believe ! --- for (var _i = 0, _a = ["action", "actiontype", "advisertype", "course", "program", "staff", "student"]; _i < _a.length; _i++) { var filename = _a[_i]; http.get('data/' + filename + '.json').map(function (res) { return res.json(); }).subscribe(function (staff) { return localStorage.setItem(filename, JSON.stringify(staff)); }); } – NazS2 Dec 02 '15 at 21:13
  • Readable enough to suggest a bug in your transpiler… What are you using? – Bergi Dec 02 '15 at 21:17
  • TypeScript , Under WebStorem 11.0.1 – NazS2 Dec 02 '15 at 21:19
  • Wow, it looks like [TypeScript intentionally deviates from the ES6 standard](https://basarat.gitbooks.io/typescript/content/docs/let.html#let-in-closures) here. I'd suggest pressing on them to support this properly, and until then use ES6 as a compile target and use babel in a second step. – Bergi Dec 02 '15 at 21:24
  • or Move to using forEach instead of a (for of). which is the solution I'm going with. But Thank you alot for pointing out that My assumption that let would/should resolve it was correct. – NazS2 Dec 02 '15 at 21:26
0

I don't think this is an Angular problem.

This may help:

Promise.all(
  filenames.map(
    f => http.get('data/'+filename+'.json')))
.then(results => results
  .forEach((r, i) => localStorage.setItem(fileNames[i], r)));
Ben Aston
  • 53,718
  • 65
  • 205
  • 331
  • Yes you are correct its not 100% Angular Problem, However The 1st Idea to resolve the issue is angular related,where if I was using JQuery It would have been easy to make the Ajax call sync. – NazS2 Dec 02 '15 at 20:51
0

based on @dandavis Comment Simplest way I found to resolve the issue was by using forEach on the list array !

Modified code :

["action", "course", "program", "staff", "student"].forEach(filename => 
    http.get('data/'+filename+'.json').map(res => res.json()).subscribe(
         staff => localStorage.setItem(filename, JSON.stringify(staff)));
     )
NazS2
  • 29
  • 7