Otis Ottington
Otis Ottington

Reputation: 435

Typescript - infinite loop in loop

I have following script:

this.employees.forEach( lpEmpl => {
  lpEmpl.vacation.forEach( lpVac => {
    const start = (new Date(lpVac.start)).toLocaleDateString('en-US', options);
    const end = (new Date(lpVac.end)).toLocaleDateString('en-US', options);
        retVal.push( {
          title: `${lpEmpl.lastname}`,
          start: `${start}`,
          end: `${end}`,
          color: '#efefef'
        });
      });
  });

My classes look like this:

export class Vacation {
   id: number;
  start: Date;
  end: Date;
}

export class Employee {
  id: number;
  lastname: string;
  firstname: string;
  email: string;
  availability: Availability;
  vacation: Vacation[];
}

When I start the application and the script is executed it "ends" in a infinite loop. To find the error I debugged the application but cannot find anything wrong. Interestingly when I slow down the app by debugging, the loop ends up normal and the data is displayed correctly.

Is it because I use nested loops?

Thanks!

EDIT:

The component:

@Component({
  selector: 'calendar',
  templateUrl: './calendar.html',
  styleUrls: ['./calendar.scss'],
  providers: [BaThemeConfigProvider, EmployeeService]
})

export class Calendar {

     calendarConfiguration: any;
     _calendar: Object;

    @Input()
    employees: Employee[];

   constructor(private _baConfig: BaThemeConfigProvider,
          private _basicTablesService: EmployeeService) {

     _basicTablesService.getEmployees().subscribe(results => this.employees = results);

    this.calendarConfiguration = this.getData();
    this.calendarConfiguration.select = (start, end) => this._onSelect(start, end);
  }

  public onCalendarReady(calendar):void {
    this._calendar = calendar;
  }

  private getData(): any {
    const dashboardColors = this._baConfig.get().colors.dashboard;

    return {
       header: {
         left: 'prev,next today',
         center: 'title',
         right: 'month,agendaWeek,agendaDay'
       },
       defaultDate: '2018-10-26',
       selectable: true,
       selectHelper: true,
       editable: true,
       eventLimit: true,
       events: this.printDays()
     };
   }

   private _onSelect(start, end): void {

     if (this._calendar !== null) {
       let title = prompt('Event Title:');
       let eventData;
       if (title) {
         eventData = {
           title: title,
          start: start,
           end: end
         };
         jQuery(this._calendar).fullCalendar('renderEvent', eventData, true);
       }
       jQuery(this._calendar).fullCalendar('unselect');
     }
   }
  private printDays(): any {
    const retVal = [];

    const options: Intl.DateTimeFormatOptions = {
     day: '2-digit', month: '2-digit', year: 'numeric'
    };

    this.employees.forEach( lpEmpl => {
      lpEmpl.vacation.forEach( lpVac => {
        const start = (new Date(lpVac.start)).toLocaleDateString('en-US', options);
        const end = (new Date(lpVac.end)).toLocaleDateString('en-US', options);
        retVal.push( {
          title: `${lpEmpl.lastname}`,
          start: `${start}`,
          end: `${end}`,
          color: '#efefef'
        });
      });
  });
  return retVal;
  }

the view:

  <ba-full-calendar [baFullCalendarConfiguration]="calendarConfiguration" 
       baFullCalendarClass="blurCalendar" 
       (onCalendarReady)="onCalendarReady($event)">
  </ba-full-calendar>

The embedded component:

@Component({
   selector: 'ba-full-calendar',
   templateUrl: './baFullCalendar.html'
  })
 export class BaFullCalendar {

  @Input() baFullCalendarConfiguration:Object;
  @Input() baFullCalendarClass:string;
  @Output() onCalendarReady = new EventEmitter<any>();

  @ViewChild('baFullCalendar') public _selector:ElementRef;

  ngAfterViewInit() {
    let calendar = jQuery(this._selector.nativeElement).fullCalendar(this.baFullCalendarConfiguration);
    this.onCalendarReady.emit(calendar);
  }
}

I use a template from akveo, that offers a calender component. So i cannot post the complete code in here.

Upvotes: 0

Views: 1948

Answers (1)

Poul Kruijt
Poul Kruijt

Reputation: 71901

Problem is the misunderstanding of the async nature of JS, the subscribe callback is called after getData. Refactor to this:

constructor(
  private _baConfig: BaThemeConfigProvider,
  private _basicTablesService: EmployeeService
) {}

ngAfterViewInit(): void {
  this._basicTablesService.getEmployees().subscribe(results => {
    this.employees = results;
    this.calendarConfiguration = this.getData();
    this.calendarConfiguration.select = (start, end) => this._onSelect(start, end);

    let calendar = jQuery(this._selector.nativeElement).fullCalendar(
      this.baFullCalendarConfiguration
    );
    this.onCalendarReady.emit(calendar);
  });
}

Let's ignore the fact of using jQuery inside an angular app though ;) Anyways, it was indeed a race condition, one that could only be won by using a breakpoint. This made sure the request for data was finished, and therefore already set the this.employees value. Without a breakpoint, the getData() was called before the http request finished.

Upvotes: 1

Related Questions