0

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.

8
  • 1
    Where and what is retVal? Why are you using forEach and not map? Commented Oct 30, 2018 at 11:55
  • const retVal = []; Commented Oct 30, 2018 at 12:03
  • Its an array I store the data in. I am Java programmer so I automatically used forEach. But for no other reason. Is map better? Commented Oct 30, 2018 at 12:04
  • Are you sure it's an infinite loop and just "not rendering"? Commented Oct 30, 2018 at 12:11
  • 1
    This is a timing/race condition issue and needs more code. The issue is not an infinite loop, but the lack of rendering of your items. Can you share the entire method, how you load the data, and what your template looks like Commented Oct 30, 2018 at 12:19

1 Answer 1

1

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.

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

Comments

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.