2
votes

I have a component in Angular called NewEventComponent that only contains a form to submit a new event. On it's submit method it has this code:

onSubmit() {
  this.isSendingRequest = true;

  this.geolocationService.geocodeAddress(this.location.value).subscribe(
    (results: google.maps.GeocoderResult[]) => {
      if (results && results.length) {
        const eventData: EventData = {
          name: this.name.value,
          location: [results[0].geometry.location.lat(), results[0].geometry.location.lng()],
          startDate: this.datetimeService.convertDateAndTimeToISOString(this.startDate.value, this.startTime.value),
          endingDate: this.datetimeService.convertDateAndTimeToISOString(this.endingDate.value, this.endingTime.value),
          description: this.description.value,
          sport: this.sport.value,
          intensity: this.intensity.value,
          fee: this.fee.value,
          maxPlayers: this.maxPlayers.value
        };

        this.eventService.createEvent(eventData).subscribe(
          (res: any) => {
            this.alertService.createAlert({ message: EVENT_CREATED_MESSAGE, type: AlertType.Success });
            this.router.navigate(['events', res.data.event.id]);
          }
        );
      } else {
        this.handleInvalidLocationError();
      }
    }
  );
}

It only sends the event to the server and redirects to the event page, which is the EventDetails component. This component only asks for the requested event to the server and passes it as an Input to a child component. This is the code:

TS:

@Component({
  selector: 'app-event-details',
  templateUrl: './event-details.component.html',
  styleUrls: ['./event-details.component.scss']
})
export class EventDetailsComponent implements OnInit {

  private event: EventResponse;

  constructor(
    private activatedRoute: ActivatedRoute,
    private eventService: EventService
  ) { }

  ngOnInit() {
    this.getEvent();
  }

  getEvent(): void {
    this.activatedRoute.params.subscribe(
      (params: Params) => {
        this.eventService.getEvent(params.id).subscribe(
          (res: any) => {
            this.event = res.data.event;
          }
        );
      }
    );
  }
}

HTML:

<div class="row">
  <div class="col">
    <div class="info-wrapper">
      <app-event-details-info [event]="event">
      </app-event-details-info>
    </div>
  </div>
</div>

And the child component EventDetailsInfo only receives the event as an Input like this @Input() event: EventResponse; and displays the information in the html like this: <h5>{{ event?.name }}</h5>.

The problem here is that when navigating from the NewEvent component to the EventDetails component, the change detection is not triggered, so until I click anywhere in the DOM for example, the changes are not showing. But when I navigate to the EventDetails component trough the url directly the page is rendering properly.

What am I missing on change detection? Any idea? Thanks!

PS: EventService only executes HTTP calls and return them as Observables, like:

createEvent(eventData: EventData): Observable<any> {
  return this.http.post(`${environment.apiUrl}/events/`, eventData);
}

getEvent(eventId: string): Observable<any> {
  return this.http.get(`${environment.apiUrl}/events/${eventId}`);
}

PS2: This is the GeolocationService method used in the ´onSubmit()´ method:

geocodeAddress(address: string): Observable<google.maps.GeocoderResult[]> {
  const response: Subject<google.maps.GeocoderResult[]>
    = new Subject<google.maps.GeocoderResult[]>();
  const request: google.maps.GeocoderRequest = { address };

  this.geocoder.geocode(request,
    (results: google.maps.GeocoderResult[]) => {
      response.next(results);
    }
  );

  return response;
}
2
What is EventService? Perhaps the event is emitted outside Angulars zone.Günter Zöchbauer
I have updated the answer with the service method, it only has http callsElias Garcia
Perhaps a problem with the route configuration. Please try to create a minimal reproduction in stackblitz.comGünter Zöchbauer
Ok, It's a complex app but I'll try to create a simple reproduction later and post it here. I'm very lost with this issue...Elias Garcia
Only add what is absolutely necessary to reproduce. Everything else is just noise that makes it difficult to track down the issue.Günter Zöchbauer

2 Answers

1
votes

Update

onSubmit() {
  this.isSendingRequest = true;

  this.geolocationService.geocodeAddress(this.location.value).subscribe(
    (results: google.maps.GeocoderResult[]) => {
      this.zone.run(() => π
        if (results && results.length) {
            const eventData: EventData = {
              name: this.name.value,
              location: [results[0].geometry.location.lat(), results[0].geometry.location.lng()],
              startDate: this.datetimeService.convertDateAndTimeToISOString(this.startDate.value, this.startTime.value),
              endingDate: this.datetimeService.convertDateAndTimeToISOString(this.endingDate.value, this.endingTime.value),
              description: this.description.value,
              sport: this.sport.value,
              intensity: this.intensity.value,
              fee: this.fee.value,
              maxPlayers: this.maxPlayers.value
            };

            this.eventService.createEvent(eventData).subscribe(
              (res: any) => {
                this.alertService.createAlert({ message: EVENT_CREATED_MESSAGE, type: AlertType.Success });
                this.router.navigate(['events', res.data.event.id]);
              }
            );
          } else {
            this.handleInvalidLocationError();
          }
        }
     });
  );
}

Original

Perhaps the event is emitted outside Angular zone.

Try:

constructor(private zone: NgZone) { }

and:

this.zone.run(() => this.router.navigate(['events', res.data.event.id]));

If this solves the issue, you should fix EventService

1
votes

Depending on the order of when the subscription occurs, you could be missing the event entirely. Consider using shareReplay(1) and exposing an event.

Alternatively, move the subscription of the event to the constructor:

  constructor(
    private activatedRoute: ActivatedRoute,
    private eventService: EventService
  ) { 
     this.getEvent();
  }

  ngOnInit() {
  }