简体   繁体   中英

TypeError cannot read property “length” of undefined - angular 4

Every time I load the webpage, I'd have to click the logo in-order my data to fully populate the local array in my component. The data fetched is located in a local JSON file. Having to refresh the page every-single-time is fairly unprofessional/annoying.

Using Angular CLI 1.3.2

Here's where my problem lies:

 @Injectable()
export class LinksService implements OnInit{
  siteFile : IFile[];


  constructor(private http: Http) {

    this.getJSON().subscribe(data => this.siteFile = data, error => 
     console.log(error));
  }

 public getJSON(): Observable<any> {
    return this.http.get('./assets/docs/links.json')
                     .map((res:any) => res.json());
 }
 getAllIpageLinks() : IPageLink[]{
    var selectedIPageLinks: IPageLink[] = new Array();
    var selectedFileLinks : IFile[] = new Array();

    selectedFileLinks = this.siteFile; 


   for (var i=0; i<selectedFileLinks.length; i++)
   {
       selectedIPageLinks =
       selectedIPageLinks.concat(selectedFileLinks[i].files);
   }
    return selectedIPageLinks.sort(this.sortLinks);
}

Component:

constructor(private elRef: ElementRef, private linksService: LinksService) {
   this._file = this.linksService.getAllIpageLinks();
  }

Edit The title has to be clicked in order for array of IFile[] to completely render. I've tried setting IFile to an empty array (IFile[] = []) The error goes away, however, it will render empty data.

The problem seems to be in the For loop, it can't recognize .length.

Problem :

The codes are correct but the approach is wrong. Subscribing to an Observable getJSON() is async task. Before any data is being returned by getJSON() , you already calls getAllIpageLinks() and therefore you get null value on very first run. I believe since you have injected the service as singleton in component, the data gets populated in subsequent call( on refresh by clicking logo).

Solution:

  1. Apply the changes (that you are making in getAllIpageLinks ) by using map operator on observable.
  2. return the instance of that observable in the component.
  3. subscribe to that observable in the component(not in .service)

Welcome to StackOverflow . Please copy paste your codes in the question instead of giving screenshot of it. I would be able than to give you along the exact codes

Reference Codes : I haven't tested the syntax but should be enough to guide you. 1. Refactor getAllIpageLinks() as below

public getAllIpageLinks(): Observable<any> {
return this.http.get('./assets/docs/links.json')
                 .map((res:any) => res.json());
                 .map(res => {

                    var selectedIPageLinks: IPageLink[] = new Array();
                    var selectedFileLinks : IFile[] = new Array();

                    selectedFileLinks = res; 
                    for (var i=0; i<selectedFileLinks.length; i++)
                        {
                            selectedIPageLinks =
                            selectedIPageLinks.concat(selectedFileLinks[i].files);
                        }
                        return selectedIPageLinks.sort(this.sortLinks);
                 });
 }
  1. call above getAllIpageLinks() in your component
  2. and subscribe to it there

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM