Razvan Zamfir
Razvan Zamfir

Reputation: 4666

How can I pass a variable from a service to a library, in this Angular app?

I have been developing an e-commerce app with Angular 14.

I am currently working on a product search feature.

export class ProductListComponent extends ComponentWithLoading implements OnInit {

    public searchCriteria: string;
    public searchText: string;
    
    constructor(
        private route: ActivatedRoute,
        private router: Router,
        private productService: ProductService
      ) {
        super();
    }
      
      
    public searchProducts(
        page?: number,
        pageSize?: number,
        searchCriteria?: string,
        searchText?: string
      ): void {
        this.showLoading();
        this.productService.setSearchParams(this.searchCriteria, this.searchText);
        this.productService
          .searchProducts(page, pageSize, searchCriteria, searchText)
          .pipe(
            take(1),
            tap((response: ApiPagination<ProductBase[]>) => {
              this.products = response.content ?? [];
              this.pageSettings = response.page;
            }),
            delay(250),
            finalize(() => this.hideLoading())
          )
          .subscribe();
      } 
}

<div class="product-search">
  <mat-form-field class="search-box">
    <input matInput placeholder="Search..." [(ngModel)]="searchText">
  </mat-form-field>

  <mat-form-field>
    <mat-select placeholder="Search by" [(ngModel)]="searchCriteria">
      <mat-option value="name">Name</mat-option>
      <mat-option value="role">Category</mat-option>
      <mat-option value="country">Country of origin</mat-option>
    </mat-select>
  </mat-form-field>

  <button mat-raised-button color="primary" (click)="searchProducts(page, pageSize, searchCriteria, searchText)">Search</button>
</div>

In the ProductService I have:

export class ProductService implements BaseService {

  public searchParams: object[];

  constructor(
    private http: HttpClient
  ) {}


  public setSearchParams(searchCriteria: string, searchText: string) {
    this.searchParams = [{
      searchCriteria: searchCriteria,
      searchText: searchText
    }];
    console.log('Search params from the ProductService', this.searchParams);
  }

  public searchProducts(
      pageNumber?: number,
      size?: number,
      searchCriteria?: string,
      searchText?: string,
      ): Observable<ApiPagination<ProductBase[]>> {
    return this.productRepo.searchProducts(pageNumber, size, searchCriteria, searchText);
  }
  
}

The setSearchParams method above successfully returns the search parameters from the search-box.

I also use a pagination library to paginate the products list(s), whether they result from a search or not. Here is the library:

export class Pagination {
  public pageNumber: number;
  public size: number;
  public searchParams: object[];

  constructor(
    pageNumber?: number,
    size?: number,
    searchParams?: object[]
  ) {
    this.pageNumber = pageNumber ?? 0;
    this.size = size ?? 10;
    this.searchParams = [
      {
        searchCriteria: 'name',
        searchText: 'Laptop'
      }
    ];
  }

    public getPaginationParams(): HttpParams {
     let params = new HttpParams();

     if (this.searchParams.length) {
      this.searchParams.forEach(sp => {
        Object.entries(sp).forEach(entry => {
           params = params.append(entry[0], entry[1]);
        });
      });
    }

    params = params.append('page', this.pageNumber);
    params = params.append('size', this.size);

    return params;
  }
}

I use the library in another service:

export class ProductRepository {

    public searchParams: object[];

    constructor(private httpClient: HttpClient, private apiService: ApiService) { }

    public searchProducts(
        pageNumber?: number,
        size?: number,
        searchCriteria?: string,
        searchText?: string
      ): Observable<ApiPagination<ProductBase[]>> {
        const url = 'ProductsSearch';
        const params = new Pagination(
          pageNumber,
          size
        ).getPaginationParams();
        console.log(params);

        return this.httpClient
          .get<ApiPagination<ProductBase[]>>(this.apiService.constructUrl(url), {
              params
            }
          )
          .pipe(catchError(() => of()));
    }

}

The above class takes the params from the library and I understand the necessity. But I need to also pass the searchParams object to the library.

The goal

The goal (necessity) is to make the Pagination library take (the values of) it's search params (searchParams variable) from the ProductService service, instead of the hardcoded "name" and "Laptop".

In other words, I take the search params from ProductListComponent, via the UI and the model, and need to pass it in the pagination library.

The problem

Importing the service in the library not only seems bad software design, but it results in compilation errors.

Question

How can I bring the search params from the ProductService service to the library?

Upvotes: 1

Views: 502

Answers (2)

Nehal
Nehal

Reputation: 13307

Looking at your question and comments, seems like you really want to pass the data to your library. In that case, I'd just use sessionStorage to avoid any circular dependency or compiler error.

  • You can also use localStorage if you want to keep the data beyond the session for better user experience.
public setSearchParams(searchCriteria: string, searchText: string) {
    this.searchParams = [{
      searchCriteria: searchCriteria,
      searchText: searchText
    }];
    console.log('Search params from the ProductService', this.searchParams);
    sessionStorage.setItem('searchParams', JSON.stringify(this.searchParams))
  }

In pagination library:

this.searchParams = JSON.parse(sessionStorage.getItem('searchParams')  as string);

Upvotes: 1

Thiago Nascimento
Thiago Nascimento

Reputation: 312

why you not store your data on an subject like a behaviourSubject, and when u need u subscribe, get the data and unsubscribe

use ur service to create your behaviorSubject and store the data whenever you want, after u only have to subscribe on your service behaviorSubject

on your service do this:

    yourSubject$ = new BehaviorSubject(<unknow>);

on your component that have the data:

    paramsSubject$ = yourservice.yourSubject$
paramsSubject$.next("b");

And whenever you want the data do this:

    paramsSubject$ = yourservice.yourSubject$
paramsSubject$.subscribe(value => {
  console.log("Subscription got", value); 
}); 

i think you can do the same thing as i answered here

Angular: how to share data from custom-component with app-component (app-root)

Upvotes: 1

Related Questions