I have created two components :
search-bar.component.ts
: displayed in all the viewssearch.component.ts
: should display the results (response from a REST API)The working is like this : wherever in the application, I want to perform a global search (products, users, events, etc...). I write something in the search bar, click on SEARCH, and I am redirected to the results page. The results are fetched from a REST API.
PS : I have been searching for hours on the Internet and I did not found.. strange ! I have been reading a lot about @Input
and @Output
indeed.
I am close to achieve what I want, with this kind of code :
import { Component, OnInit } from '@angular/core';
import {Router} from '@angular/router';
@Component({
selector: 'app-search-bar',
templateUrl: './search-bar.component.html',
styleUrls: ['./search-bar.component.css']
})
export class SearchBarComponent implements OnInit {
constructor(
private router: Router
) { }
ngOnInit() {
}
onSubmit(search: string, from: string, to: string) {
this.router.navigate(['recherche'], {
queryParams: {
search: search,
from: from,
to: to
}
});
}
}
The form is built like this : <form ngNoForm class="form-inline">
and the magic (click)="onSubmit(search.value, from.value, to.value);"
.
But I do not think that this is the best way to do this kind of stuff. (this is my first problem)
Moreover, when I am on the results view, if I do a search again on the search-bar, it reloads completely the application like if it was a basic href
. (this is my second problem)
I do not find much use cases or example for this needs I have, but it seems to be quite basic.
EDIT 1 : code of both components
Code of the search-bar.component.html
:
<div class="event-background-blue margin-20-top">
<div class="container">
<!-- Search -->
<div class="row">
<div class="col-12">
<form ngNoForm class="form-inline">
<div class="row">
<div class="col">
<input #search id="search" name="search" class="form-control form-control-lg" type="text" />
</div>
<div class="col">
<div class="input-group date datepicker" data-provide="datepicker" data-date-format="dd/mm/yyyy" data-date-week-start="1" data-date-language="fr">
<input placeholder="Du" type="text" class="form-control form-control-lg" #from name="from" autocomplete="off" >
<div class="input-group-append">
<span class="input-group-text" id="basic-addon2"><i class="fa fa-calendar" aria-hidden="true"></i></span>
</div>
</div>
</div>
<div class="col">
<div class="input-group date datepicker" data-provide="datepicker" data-date-format="dd/mm/yyyy" data-date-week-start="1" data-date-language="fr">
<input placeholder="Au" type="text" class="form-control form-control-lg" #to name="to" autocomplete="off" >
<div class="input-group-append">
<span class="input-group-text" id="basic-addon2"><i class="fa fa-calendar" aria-hidden="true"></i></span>
</div>
</div>
</div>
<div class="col">
<button (click)="onSubmit(search.value, from.value, to.value);" class="btn btn-black" type="submit"><i class="fa fa-search"></i></button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
Code of search.component.html
:
<app-search-bar></app-search-bar>
<div class="container">
<!-- Header -->
<div class="row">
<div class="col-12">
<h1 class="title-search text-primary">Search</h1>
</div>
</div>
<!-- Count of Events -->
<div class="row">
<div class="col-12 margin-20-bottom">
<h3>Events</h3>
</div>
<app-event class="col-12 col-md-6 col-lg-4 mb-10" *ngFor="let event of events" [event]="event"></app-event>
<div class="col-12" *ngIf="!events">
<p>No event, go to <a routerLink="/evenements">events !</a></p>
</div>
</div>
</div>
EDIT 2 : adding code of search.component.ts :
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { EventService } from '../../../services/event.service';
import { Event, EventsResp, EventsSearch } from '../../../models/event';
import { LocationService } from '../../../services/location.service';
import { Location, LocationsResp, LocationsSearch } from '../../../models/location';
import * as moment from 'moment';
@Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.css']
})
export class SearchComponent implements OnInit {
constructor(
private route: ActivatedRoute,
private eventService: EventService,
private locationService: LocationService,
) { }
perPage: number = 20;
page: number = 1;
error: Error;
locations: Location[];
events: Event[];
ngOnInit() {
// Retreive the parameters
const search = this.route.snapshot.queryParamMap.get('search');
const from = this.route.snapshot.queryParamMap.get('from');
const to = this.route.snapshot.queryParamMap.get('to');
this.listEvents(search, from, to);
this.listLocations(search, from, to);
}
// listEvents returns all the events
listEvents(search, from, to): void {
// Set the parameters
let parameters: EventsSearch = {
from: moment(from).toISOString(),
to: moment(to).toISOString(),
search: search,
page: this.page,
per_page: this.perPage,
sort: "",
_location_id: ""
};
// List the events
this.eventService.listEvents(parameters)
.subscribe((resp: EventsResp) => {
this.events = resp['events'];
});
}
// listLocations returns all the locations
listLocations(search, from, to): void {
// Set the parameters
let parameters: LocationsSearch = {
page: this.page,
is_city_guide: undefined,
per_page: this.perPage,
sort: "",
search: search
};
// List the locations
this.locationService.listLocations(parameters)
.subscribe((resp: LocationsResp) => {
this.locations = resp['locations'];
});
}
}
EDIT 3 : As an example, the search bar in Google Drive is a perfect example, it is displayed everywhere, and the results are displayed in the results page.
EDIT 4 :
I took a strong look at this : Passing @Input and subscribing to @Output while navigating to a Route in Angular 2 Component
and this : Angular 5 Pass data on click event from parent component to child component on button clicked at parent component
You have two ways to perform this:
The quick and event based
Create an event emitter in your search-bar component, and trigger it in the onSubmit function, passing the search value.
// Search Bar Component
import { EventEmitter, Output } from '@angular/core';
@Component({
selector: 'app-search-bar',
templateUrl: ['./search-bar.component.html'],
styleUrls: ['./search-bar.component.scss']
})
export class SearchBarComponent implements OnInit {
@Output() searchEvent = new EventEmitter();
// constructor
onSubmit(searchValue: string, ...) {
// your function
this.searchEvent.emit(searchValue);
}
}
<!-- Search Component -->
<app-search-bar (searchEvent)="fetchResults($event)">
</app-search-bar>
The service based way
Create a search service provided in your parent module, and import it both components. In the service, create a search function that will perform the Http request and update a subject. The search bar component will call this function on submit. Then create an observable and listen to it from the search component in order to fetch results.
// Your service
searchResults = new BehaviorSubject<Array<Result>>();
// constructor
onResults() {
return this.searchResults.asObservable();
}
search(value: string) {
this.http.post(url, value).subscribe(results => this.searchResults.next(results);
}
// Search-Bar component
onSubmit(value: string) {
// code
this.searchService.search(value);
}
// Search component
ngOnInit() {
this.searchService.onResults().subscribe(results => this.results = results));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With