I am using a Heroku based twitter API and getting data into my web app using Angular Mat table and other associated components.
I want to be able to have the user change the string query and update the results based on input field (with deBounce applied).
Example of API:
https://am-twitter-scrape.herokuapp.com/hashtags/Python?pages_limit=3&wait=0
I want the hashtags value to be updated by what user enters into input field and then return updated results. I'm following a tutorial by https://www.freakyjolly.com/angular-7-6-add-debounce-time-using-rxjs-6-x-x-to-optimize-search-input-for-api-results-from-server/ but I am struggling to purpose it to my needs.
My code is not throwing errors, nor is it working now. Can I ask for any help please?
Data service (twitterdata.service.ts)
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Users } from '../models/users.model';
@Injectable()
export class TwitterdataService {
// setup custom placeholder vars that will be binded to search input field
// They will modify the declared JSON APIs below
private myCustomHashtag:string = 'Python';
private myCustomUser:string = 'Twitter';
// Load JSON APIs via HttpClient and set them up with obervables (models)
private hashtagsUrl:string = `https://am-twitter-scrape.herokuapp.com/hashtags/${this.myCustomHashtag}?pages_limit=3&wait=0`;
private usersUrl:string = `http://am-twitter-scrape.herokuapp.com/users/${this.myCustomUser}?pages_limit=3&wait=0`;
constructor( private http: HttpClient ) { }
// Retrieve JSON API (hashtags), using template model
getTweetsByHashtag(): Observable<Users[]> {
return this.http.get<Users[]>(this.hashtagsUrl);
}
// Retrieve JSON API (Users), using template model
getTweetsByUsers(): Observable<Users[]> {
return this.http.get<Users[]>(this.usersUrl);
}
}
Layout of twitter table data in HTML template (hashtag-tweets-component.html)
<mat-card>
<!--Search input field to filter table data-->
<div class="search-container" style="direction: rtl;">
<mat-form-field>
<mat-icon matPrefix aria-hidden="false" aria-label="Search">search</mat-icon>
<input matInput #hashtagsSearchInput placeholder="Search by hashtag" [(ngModel)]="myCustomHashtag">
</mat-form-field>
</div>
<!--display loading spinner whilst data loads-->
<div class="spinner-container" *ngIf="dataSource.loading$ | async">
<mat-spinner></mat-spinner>
</div>
<!--Table populated by twitter hashtags API feed-->
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<ng-container matColumnDef="text">
<th mat-header-cell *matHeaderCellDef> Tweet </th>
<td mat-cell *matCellDef="let hashtags"> {{hashtags.text | ellipsis: 50}} </td>
</ng-container>
<ng-container matColumnDef="likes">
<th mat-header-cell *matHeaderCellDef> Likes </th>
<ng-container *matCellDef="let hashtags">
<td mat-cell *ngIf="(hashtags.likes>0); else noLikes"> {{hashtags.likes}} </td>
</ng-container>
<ng-template #noLikes>-</ng-template>
</ng-container>
<ng-container matColumnDef="replies">
<th mat-header-cell *matHeaderCellDef> Replies </th>
<ng-container *matCellDef="let hashtags">
<td mat-cell *ngIf="(hashtags.replies>0); else noReplies"> {{hashtags.replies}} </td>
</ng-container>
<ng-template #noReplies>-</ng-template>
</ng-container>
<ng-container matColumnDef="retweets">
<th mat-header-cell *matHeaderCellDef> Retweets </th>
<ng-container *matCellDef="let hashtags">
<td mat-cell *ngIf="(hashtags.retweets>0); else noRetweets"> {{hashtags.retweets}} </td>
</ng-container>
<ng-template #noRetweets>-</ng-template>
</ng-container>
<ng-container matColumnDef="hashtags">
<th mat-header-cell *matHeaderCellDef> Hashtags </th>
<td mat-cell *matCellDef="let hashtags"> {{hashtags.hashtags | slice:0:2}} </td>
</ng-container>
<ng-container matColumnDef="date">
<th mat-header-cell *matHeaderCellDef> Date </th>
<td mat-cell *matCellDef="let hashtags"> {{convertDate(hashtags.date)}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</mat-card>
<!--Table pagination-->
<mat-paginator
[length]="length"
[pageSize]="pageSize"
[pageSizeOptions]="pageSizeOptions"
[showFirstLastButtons]="yes">
</mat-paginator>
Twitter table data Typescript (hashtag-tweets-component.ts)
import { Component, ViewChild, ElementRef, OnInit } from '@angular/core';
import { TwitterdataService } from '../services/twitterdata.service';
import { Users } from '../models/users.model';
import { Observable, of, fromEvent } from 'rxjs';
import { debounceTime, map, distinctUntilChanged, filter } from 'rxjs/internal/operators';
import { MatTableDataSource, MatPaginator } from '@angular/material';
@Component({
selector: 'app-hashtag-tweets',
templateUrl: './hashtag-tweets.component.html',
styleUrls: ['./hashtag-tweets.component.sass']
})
export class HashtagTweetsComponent implements OnInit {
dataSource = new MatTableDataSource<Users>();
displayedColumns = ['text', 'likes', 'replies', 'retweets', 'hashtags', 'date'];
// Setup pagination attr
length = 50;
pageSize = 10;
pageSizeOptions = [5, 10, 20];
@ViewChild( MatPaginator ) paginator: MatPaginator;
// Setup element reference for the search field
@ViewChild('hashtagsSearchInput') hashtagsSearchInput: ElementRef;
apiResponse:any;
isSearching:boolean;
// Trim and reformat date string (unfortunately not already a date object to start with)
convertDate(rawDate: string): string {
const dateOnly = rawDate.split('-')[1].trim();
const [day, month, year] = dateOnly.split(' ');
return `${month} ${day}, ${year}`;
}
constructor( private twitterdataService: TwitterdataService ) {
}
ngOnInit() {
// Query the api using the data service and pull it into dataSource for Mat table
this.twitterdataService.getTweetsByHashtag().subscribe(
data => this.dataSource.data = data
);
// Listen to the user input on search field and update results
fromEvent(this.hashtagsSearchInput.nativeElement, 'keyup').pipe(
// get value
map((event: any) => {
return event.target.value;
})
// if character length greater then 2
,filter(res => res.length > 2)
// Time in milliseconds between key events (wait until run search)
,debounceTime(1000)
// If previous query is diffent from current
,distinctUntilChanged()
// subscription for response
).subscribe((text: string) => {
this.isSearching = true;
this.twitterdataService.getTweetsByHashtag().subscribe(
data => this.dataSource.data = data
);
this.isSearching = false;
// this.apiResponse = res;
},(err)=>{
this.isSearching = false;
console.log('error',err);
});
}
ngAfterViewInit(): void {
// Add the MatTable paginator after view init
this.dataSource.paginator = this.paginator;
}
}
fromEventemits any values? Can you put console.log inside the operators and check?