Why shareReply() method is not working as expected - Angular 15 - javascript

Trying to make use of same API in 3 components, In order to avoid duplicate HTTP calls I can use shareReply() to cache the response and use it where ever I want from RxJs. So I did like below
api-service.ts
getUsers(): Observable<any> {
let headers = new HttpHeaders();
headers = headers.set('app-id', '63b691428f53f6370fc9eed6');
return this.http.get(this.url, { headers }).pipe(
map((resp) => {
return resp;
}),
shareReplay()
);
}
test1-component
data$!: Observable<any>;
constructor(private api: ApiService) {}
ngOnInit(): void {
this.loadTest1Data();
}
loadTest1Data() {
this.data$.subscribe({
next: (response) => {
console.log('Loading data for Component - 1', response);
},
error: (error) => {
console.log('Error While Loading data for Component - 1', error);
},
complete: () => {
console.log('Success');
},
});
}
test2-component (test3-component also use same code)
data$!: Observable<any>;
constructor(private api: ApiService) {}
ngOnInit(): void {
this.loadTest2Data();
}
loadTest2Data() {
this.data$.subscribe({
next: (response) => {
console.log('Loading data for Component - 2', response);
},
error: (error) => {
console.log('Error While Loading data for Component - 2', error);
},
complete: () => {
console.log('Success');
},
});
}
Error i got reproducing HERE - Stackblitz
Cannot read properties of undefined (reading 'subscribe')
Could someone tell me what went wrong and how to resolve it? or is there any other alternative approach is there? (apologies, I'm not allowed to use any third party state management tools)
Thanks in Advance for your valuable time

Your test components aren't defining data$ so this.data$.subscribe is doing the equivalent of undefined.subscribe, hence the error.
For the test components you can do
#Input() data$: Observable<any> = of();
and pass in via
<app-test1 [data$]="data$"></app-test1>
Note, it's more common to do
<app-test1 [data]="data$ | async"></app-test1>
so that the components receive the data directly and the AsyncPipe takes care of subscribing and unsubscribing.

Related

How do I access the Response Headers using ServiceStack

I'm using react with Redux toolkit but I'm unbale to access the Response headers
return await client
.get(new Users())
.then((data) => {
// how to I access the Response Header here?
console.log("data", data);
return data;
})
.catch((error) => {
});
I'm assuming you're referring to ServiceStack's TypeScript Service Client, the public API of which you can find on index.d.ts where you can use the instance requestFilter and responseFilter:
export declare class JsonServiceClient {
//...
requestFilter: (req: IRequestInit) => void;
responseFilter: (res: Response) => void;
}
To inspect the underlying W3C fetch API's Request / Response, e.g:
let client = new JsonServiceClient();
client.responseFilter = function(r) {
console.log(r.headers)
}
client.get(new Users())
.then(...)

API not getting called in service file Angular 7

I am working with a news App. When I try to call API, it is not getting called in service file function. When I try to console it is getting consoled but when I see network tab there is no API called.
Component file:
newPostView(postId: { split: (arg0: string) => any[]; }) {
postId = postId.split("-")[1];
this.data = {
postId: postId,
postType: localStorage.language
}
this._newsService.newsCount(this.data);
}
Below is my function in the service file.
newsCount(data) {
console.log("post data", data);
return this.http.put(config.baseApiUrl + 'post-views', data);
}
If you are returning from the service file then you should subscribe in component file. So your updated code should look like this.
Component File:
newPostView(postId: { split: (arg0: string) => any[]; }) {
postId = postId.split("-")[1];
this.data = {
postId: postId,
postType: localStorage.language
}
this._newsService.newsCount(this.data).subscribe((res:any) =>{
});
}

Unit Testing with Angular 7

I am trying to write unit test for this angular script:
export class DataService {
private csrfToken: string = '';
private isContentShow: BehaviorSubject<boolean> = new BehaviorSubject(true);
constructor(private http: HttpClient, private cookieService: CookieService) {
this.token = this.cookieService.get('token');
}
public createData(data: Data) {
try {
this.http.post( url,
data,
{
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': this.token
})
})
.subscribe(
data => {
this.isContentShow.next(true);
},
err => {
this.showError();
},
() => console.log('Request Complete')
);
return true;
} catch {
this.showError();
}
}
public getIsContentShow(): Observable<boolean> {
return this.isContentShow.asObservable();
}
}
The test that I had so far and its running as expected.
it('#getIsContentShow should return value from observable',
(done: DoneFn) => {
service.getIsContentShow().subscribe(value => {
expect(value).toBe(true);
done();
});
});
However I am trying to write the test for createData() function
I am able to mock the HttpClient using HttpClientTestingModule however I don't know how to handdle the CookieService and token ?
Thanks
You can use spies to spy on the cookieService get method. This way, you can write your unit test to test the combinations of returns you say the cookieService can provide.
This link says that you can spy on the prototype of the method in order to handle it how you like in the constructor.
it(
"should call #getGeneralStats in the constructor",
inject(
[CookieService, HttpClient],
(cookieService: CookieService, http: HttpClient) => {
let mySpy = spyOn(cookieService, 'get').and.returnValue(<your value>);
dataService = new DataService(http, cookieService);
expect(mySpy).toHaveBeenCalled();
}
)
);
For you, this may depend on how you're writing your tests. The example shows the service being instantiated like new ServiceName, but it's also possible to use dependency injection to get the service. If you're using DI for the service you are testing, I'd have to research more how to do this (others please feel free to add your answer if you know how to do that)!

POST - Angular 5

I'm fairly new to angular and got stuck at getting data from SpringREST which is at backend.
So scenario is:I'll be getting a JSON string from backend as POST(JSON data will be redirected to my hosted link of site as POST) and I need to catch that JSON string and display it on UI.
I'm not sure about the postMethod in dataservice.ts if it should be there.
I googled on stackoverflow and came up with below code which doesn't seem to work in my scenario:
Component.ts
import { MyDataService } from './services/my-data.service';
constructor(private posting: MyDataService
) {}
ngOnInit() {
this.posting.postMethod().subscribe(
(response => {
console.log(response)
}));
}
}
Data-service.ts
#Injectable()
export class MyDataService {
constructor(private http: Http)
{ }
postMethod(model: any ) {
return this.http.post("http ://", model)
.map(res => res.json());
}
}
As the error says, You need to pass the parameter to the service when invoking
this.posting.postMethod(model).subscribe(
(response => {
console.log(response)
}));
As i can see in your component.ts you are not passing the model as a parameter.
you need to pass the model as a parameter.
this.posting.postMethod(anyData).subscribe(
(response => {
console.log(response)
}));
If this is not the issue then please update us with the error you are getting.
This is the right way to define a function in the subscribe method:
ngOnInit() {
this.posting.postMethod(model).subscribe(
(response) => {
console.log(response)
});
}

Angular 2 - Viewing a Single Record of Data

I am new to Angular so I am having trouble figuring out how to form my questions for what I am trying to accomplish, but here it goes.
I have a component that is fetching a single user record from a service. I then want to display those user details on my UI. In other parts of my code, they have always been multiple records so I have used *ngFor and looped over the array of data. However, since this is just a single result, I am not too sure how to accomplish this.
Component:
import { Component, OnInit } from '#angular/core';
import { Router, ActivatedRoute, Params } from '#angular/router';
import { UserRecord } from '../shared/user-record.interface';
import { UserService } from '../shared/user.service';
#Component({
selector: 'app-view-record',
templateUrl: './view-record.component.html',
styleUrls: ['./view-record.component.css']
})
export class ViewRecordComponent implements OnInit {
private record: UserRecord[];
private errorMessage: any = '';
private loaded = false;
private RecordID: number; // User ID of who we are looking at
constructor(private _crudService: UserService,
private activatedRoute: ActivatedRoute) { }
ngOnInit() {
// Get the userID from the activated route
this.activatedRoute.params.subscribe((params: Params) => {
this.RecordID = params['id'];
});
// Call our service and pass the userID
this._crudService.getRecord(this.RecordID)
.then(res => {
this.record = this._crudService.record;
return this._crudService.getRecord(this.RecordID);
})
.then(res => {
console.log(this.record)
this.loaded = true;
})
.catch(err => { console.error(err); });
}
}
Service:
getRecord(userID: number) {
const headers: Headers = new Headers({
"Authorization": this._frameworkService.getSessionInfo().token
});
return new Promise((resolve, rejects) => {
this._http.post(this.baseUrl + '/fetchRecord', { "userID": userID }, { "headers": headers })
.map(res => res.json())
.subscribe((data) => {
if (data) {
this.record = data;
}
resolve(true);
});
});
}
Interface:
export interface UserRecord {
RecordID: number;
QID: string;
FavoriteColor?: string;
FavoriteNumber?: number;
FavoriteActor?: string;
MetaInsertUTC: string;
MetaUpdateUTC: string;
FirstName: string;
LastName: string;
NTID: string;
}
Service Result:
[
{
"RecordID":"55",
"QID":"Q00019204",
"FavoriteColor":"Blue",
"FavoriteNumber":"6",
"FavoriteActor":"Bob",
"MetaInsertUTC":"2017-06-29 18:47:01.750",
"MetaUpdateUTC":null,
"FirstName":"Jim",
"LastName":"Bobs",
"NTID":"bobby"
}
]
In my Component HTML, I have tried {{record.FirstName}} but receive the error of ViewRecordComponent.html:16 ERROR TypeError: Cannot read property 'FirstName' of undefined.
Since this isn't a set of data results, I don't see how *ngFor would be applicable in the use case.
I assumed that since my component is storing the data in the record object, I should be able to access that from the UI? The console.log shows all of the correct data points.
How would I reference the users FirstName in my component HTML? Hopefully I'm on the right path at least.
Your response seems to be an array with an object, so record.FirstName doesn't exist, but record[0].FirstName does.
And when it comes to the view, remember to use either the safe navigation operator or *ngIf so that you do not run into undefined issues like mentioned by DeborahK. Observable type error: cannot read property of undefined
Furthermore just some suggestion on how to handle http in Angular... I would do something like the following...
getRecord(userID: number) {
const headers: Headers = new Headers({
"Authorization": this._frameworkService.getSessionInfo().token
});
return this._http.post(this.baseUrl + '/fetchRecord', { "userID": userID }, { "headers": headers })
.toPromise()
.then(res => res.json()[0]) // get the object only
.catch(err => { console.error(err); });
}
and component:
this._crudService.getRecord(this.RecordID)
.then(res => {
this.record = res;
});
But that's totally up to you :)
Getting data from Http is asynchronous. This means that when the page is first displayed, the data is not yet there.
There are several ways to resolve this:
One option is to use the "?" (safe navigation) operator: {{record?.FirstName}} This better handles nulls. See this link for more information: https://angular.io/guide/template-syntax#the-safe-navigation-operator----and-null-property-paths
Another option is to use *ngIf around your HTML code. *ngIf='record'
So when your page is first displayed, it will not generate an error that record is not yet set. As soon as the data is retrieved, the binding will notice the change and update the UI appropriately.
Here is what one of my service methods look like:
getProducts(): Observable<IProduct[]> {
return this._http.get(this._productUrl)
.map((response: Response) => <IProduct[]> response.json())
.catch(this.handleError);
}
And here is the call to that service:
ngOnInit(): void {
this._productService.getProducts()
.subscribe(products => this.products = products,
error => this.errorMessage = <any>error);
}
Notice that the subscribe is in the component that calls the service, not in the service itself.

Categories

Resources