Search code examples
javascriptangulartypescriptmicroservices

Microservices, Async Calls, Transferring Data between Sibling Components: Putting it all together


I'm making a worker clock-in clock out application on Angular and I need advice.

The application is organised as components (sidenav.component.ts; home.component.ts; calendar.component.ts; workday.component.ts) and services (home.service.ts; calendar.service.ts). It has a calendar with all the days of the month. The calendar is an array where every unit is a day that has entrance (AM and PM) and exit (am PM) along with notes that the worker can add. It's all protected by spring security.

I've struggled a bunch with this because many times I need to exchange data between the components and services and even between the components themselves (sometimes using services).

here's an example:

say after I've logged in I need to access the calendar but only after it's been populated with the data from the user: loading the calendar with data takes about 10 seconds and in that time I wanted the sidenav menu to be deactivated! In my mind this can be done by changing a boolean in a service ("homeservice.lock") from false to true so that the lock is shared between components. But when I try it the menu remains locked even after lock is true.

This is the home service


lock: Boolean = false

This is the calendar populating bit that's in the calendar component

 this.calendarService.getDays(parameter).subscribe((res => {
    this.calendarCopy = JSON.parse(JSON.stringify(res));

    this.calendar= res;
      this.calendarService.saveCal(this.calendarCopy);
      if (this.calendarCopy){
        this.homeService.lock = true;
          console.log("the calendar is populated! The lock is true: " + this.homeService.lock)
      }
     

FInally this is the nav component. As you can see it can only be activated if the lock in the service is true.


export class MainNavComponent implements OnInit {
ngOnInit() {
   //link to the calendar
    this.setButton();
}

...
setButton(){
if (this.homeservice.lock){
 document.getElementByID("navbutton").removeAttribute("disabled")
}
...


But the sidebar menu doesn't unlock. I suspect it's because is in onInit and at the boot of the application the boolean is still false. After the calendar is populated and the boolean is true the menu remains locked. I need something that constantly checks the status of the boolean in an async manner! Or I need a different strategy.

I encounter this problem over and over. I need to share data between components and I feel like it's near impossible! I can't use @Input or @Output because these aren't nested components but sibling who are totally independent from each other and only share the services. Sometimes I might need to send a Date from the calendar component to the single date component where the user can put in the clock in and clock out data, add notes about the day etc. and so every "day" has a model that contains "day.date, day.entranceAM, day.exitAM, day.entrancePM day.exitPM and day.notes".

The components have a templateUrl rather than just a template so I can't really put a little template filled with the stuff I need.

The question is how to put this all together? I realise that I'm talking about async queries because I'm treying to make two independent components communicate without reloading the page. I know microservices are needed for this but I can't quite put it all together!

Assistance would be helpful!


Solution

  • This would be very trivial if you were to use observables in your service.

    See your service could expose 2 observables, the days$ and the isLocked$ observable.

    export class CalendarService {
      
      public days$ = this.getDays().pipe(share());
      public isLocked$ = merge(
        of(true),
        this.days$.pipe(map(x => false))
      );
    
      private getDays() {
        return timer(2000)
          .pipe(
            map(x => [1,2,3,4,5,6,7])
          );
      }
    }
    

    The isLocked$ observable will emit an initial value of true and when the getDays() (your API call) completes, isLocked$ will then emit false.

    Then the code on your components would be relatively simple.

    on the component.ts files you'll have to:

    constructor(private calendarService: CalendarService) {}
    
    public isLocked$ = this.calendarService.isLocked$;
    

    and on your component.html files, just do

    <button [disabled]="isLocked$ | async">I'm locked until the calendar is loaded</button>
    

    EDIT:

    Here's a stackblitz link to demonstrate it: https://stackblitz.com/edit/angular-ivy-tdhcrf