[Fixed] Angular 11 Router data observable not updated when data has not changed

Issue

I have this Angular resolver:

@Injectable()
export class DataResolver implements Resolve<any> {

    constructor(private apiService: ApiService) {}
    
    public resolve(route: ActivatedRouteSnapshot): any {
      const data = {
        param1: route.queryParamMap.get('param1'),
        param2: route.queryParamMap.get('param2'),
      };
      return this.apiService.search(data);
    }
}

The apiService.search method looks like this:

public search(data): Observable<any> {
  let params = new HttpParams(); 
  for (const key in data) {
    if (data.hasOwnProperty(key)) {
      const value = data[key];
      if (value) {
        params = params.set(key, value);
      }
    }
  }

  return this.http.get<any>(`${API_URL}/endpoint`, { params });
}

Then I have a component that subscribes to changes on the ActivatedRoute‘s data:

route.data.subscribe((data) => {
  console.log(data);
});

And the route looks like this:

{
  component: HomeComponent,
  path: '/',
  resolve: {
    data: DataResolver,
  },
  runGuardsAndResolvers: 'paramsOrQueryParamsChange',
}

The problem is, the subscription handler only gets called if the data actually changes, regardless of if the resolve method of the resolver has been called. In particular, if two (or more) subsequent resolve calls resolve an empty array then the line console.log(data) only gets called the first time.

Is this the expected behaviour? can I force the observable to emit a new value?

My goal is to show a message every time the resolve resolves an empty array, regardless of if it was empty before.

Solution

You have unnecessarily wrapped the observable in a promise and resolved it. Try rewriting your resolver like this.

@Injectable()
export class DataResolver implements Resolve<any> {
    constructor(private apiService: ApiService) {}

    public resolve(route: ActivatedRouteSnapshot): any {
        const data = {
            param1: route.queryParamMap.get("param1"),
            param2: route.queryParamMap.get("param2"),
        };

        return this.apiService.search(data);
    }
}

I would also look at cleaning up your API service by using HttpPrams

// import { HttpClient,HttpParams } from '@angular/common/http';
public search(data): Observable<any> {
  let params = new HttpParams({ fromObject: data });
  return this.http.get<any>(`${API_URL}/endpoint`, { params });
}

you may need to add the resolver to your route table

RouterModule.forRoot([{
  path: 'yourRoute',
  component: YourComponent,
  resolve: {
    yourDataWillBeInThisProperty: DataResolver
  }
}])

if you want the component to reload you may need to change the onSameUrlNavigation property in your router module

@NgModule({
  imports: [ RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload' }) ],
  exports: [ RouterModule ]
})
export class AppRoutingModule {}

I would not recommend using the onSameUrlNavigation for your issue but would just subscribe to the route.queryParams observable and trigger the search there.

In your component

ngOnInit(){
  this.sub = this.route.queryParams.subscribe(() => { this.data = this.route.snapshot.data.data; }); 
  // don't forget to unSubscribe
}

Leave a Reply

(*) Required, Your email will not be published