0

I have search feature in home page(homePageComponent) and when i click on search it will redirect to different page called search list page(searchListComponent) and i have one more component in searchListComponent itself(according to application design) which is called search card component(searchCardComponent) where i show all the search result. i was trying to send search text from homePageComponent to searchCardComponent using eventemitter but somehow the data is not passing. it would be good if somebody help on that.

Sample code below

homePageComponent

HTML (Product A)

     <ng-select
              class="col-md-5 solution-list-dropdown"
              [items]="productType$ | async"
              [addTag]="true"
              [(ngModel)]="selectedSolutionId"
              bindLabel="description"
              bindValue="id"
              placeholder="Select"
              [clearable]=true
              [searchable]=true
              [dropdownPosition]="'down'"
              [ngClass]="{ 'dropdown-is-invalid': submitted && f.category.errors }">
           </ng-select>
<button class="red-button"  (click)="searchRedirection(selectedSolutionId)">
          Search
        </button>

typescript

  @Output() searchValue = new EventEmitter<string>();

   public searchRedirection(selectedSolutionId: string) {
      this.searchValue.emit(selectedSolutionId);
      this.router.navigate(['/category/' + selectedSolutionId]);
    }

SearchListComponent

HTML

<div class="container-fluid product-card-container p-0" id="hor-divider">

    <div class="product-list">
      <app-product-card (searchValue)="onApplyEvent($event)" *ngFor="let product of products | filter:searchText | filter:filterText" [product]="product">
      </app-product-card>
    </div>

</div>

Typescript

  onApplyEvent(event: any): any {
    console.log('event : ' + event);
  }

Here i'm expecting the console log to print the "event : Product A", so that i can leverage it to bind this value into *ngFor. Any help would be appreciated.

2 Answers2

0

Event emitter the way you declared it won't work this way because it can only be read by parent component of homePageComponent with that @Output decorator. You could create a separate events class and import it into both components (one that emits and one that receives) but that's a horrible practice in my opinion. Better thing to do would be to either send search word as a query param in the route or create a shared service that would have a BehaviorSubject object as a field as it can store and retransmit the last emitted value. Here is the params example

this.router.navigate(['/category'], { queryParams: { selectedSolutionId: selectedSolutionId } });

then to get the value in SearchListComponent you would do

import { ActivatedRoute } from '@angular/router';
.....
constructor(private route: ActivatedRoute) { }
ngOnInit() {
this.route.queryParams
  .subscribe(params => {
    this.selectedSolutionId = params.selectedSolutionId;
    //then you could pass this.selectedSolutionId as @Input to app-product-card component
    console.log(this.selectedSolutionId );
  });
}
ihor.eth
  • 2,207
  • 20
  • 27
0

you can use something like a shared service to transfer data between the two components,

In this service we could use a subject or eventEmitter (but subject is better), and in the home page component we can emit that subject

while in the search card component, we will subscribe to this subject to retrieve the data

here is an example

shared.service.ts

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable({providedIn: 'root'}) // the service is available application wide, so the same instance of this class will be available in the whole application

export class SharedService {

  searchSubject = new Subject<String>(); // a new subject of type String, replace it with your data type
}

and in the home component, once a solution is selected, just emit it to the shared service, you can add (change) or (ngModelChange) to your list in the html to check the changes and once a change is happened, emit the subject

so the function in the home component html could be something like that

(ngModelChange) = "onSelectSolution()"

and in the ts file

constructor(private sharedService: SharedService) { // inject the shared service

}

onSelectSolution() {
  // just emit the event
  this.sharedService.searchSubject.next(this.selectedSolutionId); // emit the value to the shared service
}

then in the search card component

import { OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';

// add the selector 

export class SearchCardComponent implements OnInit, OnDestroy {
  subscription: Subscription // define some subscription property to assign the subscription to, and destroy it once we left this component

  constructor(private sharedService: SharedService){}

  ngOnInit() {
    // here to subscribe to the subject in the sharedService
    this.subscription = this.sharedService.searchSubject.subscribe((mySearch: String) => {
      // now we passed the search from the home component to the search card component
    });
  }

  ngOnDestroy() {
    // just unsubscribe the subscription we used
    this.subscription.unsubscribe();
  }
}

check this link to see the difference between (change) and (ngModelChange) difference between (change) and (ngModelChange)

Mohammed Yousry
  • 2,134
  • 1
  • 5
  • 7
  • I don't think this will work with regular subject. The component that would need to get the value from subject won't be rendered at the time subject value is emitted that's why BehaviorSubject or ReplaySubject is a better option in this case – ihor.eth Apr 08 '20 at 16:35
  • ah yes, you are right, I thought the searchCard component will be loaded separately after clicking some button, I misunderstood the question, thanks for the clarification :) – Mohammed Yousry Apr 08 '20 at 17:55