Value is undefined when subscribing to service - javascript

I have this problem. When I am trying to subscribe to a data returned from service I get undefined when trying to log it.
This is my route, I have tested it with REST client and it works fine:
router.post('/liveAuction', (req, res, next) => {
const id = req.body.id;
Auction.getAuctionById(id, (err, liveAuction) => {
if (err){
res.json({
success: false,
message: "Something went wrong!"
});
console.log(err);
}
else {
res.json({
success: true,
message: "Auction retrieved!",
liveAuction
});
}
});
});
This is my method of getting data from mongoDB:
module.exports.getAuctionById = function(id, callback){
const query = {_id: id};
Auction.find(query, callback);
console.log(query);
}
This is my service:
getAuctionById(id):any{
let headers = new Headers();
headers.append('Content-Type', 'application/json');
return this.http.post('http://localhost:3000/auctions/liveAuction', {id: id}, {headers: headers})
.map(res => res.json());
}
And this is my component.ts:
import { Component, OnInit, ElementRef, Inject } from '#angular/core';
import { Observable } from 'rxjs/Rx';
import {Router, RoutesRecognized} from '#angular/router';
import { AuctionService } from '../../services/auction.service';
import { DataTransferService } from '../../services/data-transfer.service';
#Component({
selector: 'app-auction-details',
templateUrl: './auction-details.component.html',
styleUrls: ['./auction-details.component.css']
})
export class AuctionDetailsComponent implements OnInit {
liveAuction: any;
auction: any;
id: any;
constructor(
private auctionService: AuctionService,
private dataService: DataTransferService
) {
this.dataService.currentProduct.subscribe(auction => this.auction = auction);
this.id = this.auction._id;
console.log(this.auction._id);// I get the right id here.
}
ngOnInit() {
this.auctionService.getAuctionById(this.id).subscribe(auction => this.liveAuction = auction.liveAuction);
console.log(this.liveAuction);// Here I get undefined.
Also if I try to use get items of it, like this.liveAuction._id, I get "Cannot find property _id of undefined" error.
Could you help me understand what I am doing wrong? I have done similar procedure for my other component which I use as Mat-Dialog component with different service, but functionality is completely the same. I have compared them like three times already and everything looks same but here it doesn't work. Please suggest what I am doing wrong. Thanks!

Related

You provided 'undefined' where a stream was expected. in token interceptor

I am trying to make an interceptor to refresh the token, but it throws me this error and I don't know why
ERROR TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
token-interceptor.service.ts
import { Injectable } from '#angular/core';
import { AuthService } from './auth.service';
import { HttpClient, HttpErrorResponse, HttpHandler, HttpInterceptor, HttpRequest } from '#angular/common/http';
import { environment } from 'src/environments/environment';
import { catchError, map} from 'rxjs/operators';
import { throwError } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class TokenInterceptorService implements HttpInterceptor {
constructor(
private auth: AuthService,
private http: HttpClient
) { }
intercept(req: HttpRequest<any>, next: HttpHandler) {
return next.handle(req).pipe(
catchError((err: any) => {
if (err instanceof HttpErrorResponse) {
if (err.url.includes('signin') || err.url.includes('refreshToken')) {
return next.handle(req)
}
//if error is not about authorization
if (err.status !== 401) {
return next.handle(req)
}
this.renewToken(req).subscribe(request => {
return next.handle(request)
})
} else {
return throwError(err)
}
})
)
}
renewToken(req: HttpRequest<any>) {
return this.http.get(`${environment.API_URL}/refreshToken`, { withCredentials: true }).pipe(
map((res: any) => {
//update access token
this.auth.setToken(res.token)
return req.clone({
setHeaders: {
authorization: `Bearer ${res.token}`
}
})
})
)
}
}
Ignore this: It looks like your post is mostly code; please add some more details. It looks like your post is mostly code; please add some more details.
this piece of code is wrong:
this.renewToken(req).subscribe(request => {
return next.handle(request)
})
istead it should be:
return this.renewToken(req).pipe(switchMap(request => next.handle(request)));
you are just returning nothing in your variant, that is why it doesn't work.
also the whole logic of token interpceptor seems weird to me. I believe you should rethink about how you want it to work. for now as I see you sending request without token and in almost all cases you are sending it again unmodified, and the one that I fixed above will send it again with token. Wouldn't it be right to add token every time, and only send it 2nd time if token is outdated?

Angular - HTTPClientModule delete request not working

I am making a simple delete request from my angular app but nothing is happening and no error is appearing. My service code is as follows :
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class TodoService {
todoUrl = 'https://example.herokuapp.com/api/todoDB/';
constructor(private http: HttpClient) { }
getTodo() {
return this.http.get(this.todoUrl);
}
postTodo(todoObject: any) {
return this.http.post(this.todoUrl , todoObject);
}
deleteTodo(id: any) {
const url = `${this.todoUrl}${id}`;
console.log(url); // *** This is printing correct URL
return this.http.delete(url);
}
}
My getTodo() and postTodo() are working completely fine but the deleteTodo() method is not working and also it does not show any error either. When I put the URL from the console.log(url) in postman, it works but it is not working from my app.I am using the following code in my component to access the deleteTodo() method of my service :
removeTodo(i: any) {
this.todoService.deleteTodo(this.todoArray[i]._id);
}
My delete route of server :
// Delete Todo
router.delete('/:id' , (req , res) => {
Todo.findById(req.params.id)
.then((todo) => todo.remove().then(() => res.json({success : true})))
.catch(err => res.json({success : false}).status(404))
});
You need to subscribe to the Observable
Code Snippet for your problem:
removeTodo(i: any) {
this.todoService.deleteTodo(this.todoArray[i]._id).subscribe(e=>{
// Callback
// Perform Actions which are required after deleting the id from the TODO
});
}
Additional Reference:
https://www.pluralsight.com/guides/posting-deleting-putting-data-angular
https://angular.io/guide/http#making-a-delete-request
Modify your code to support catchError and throwError using pipe for debugging.
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';
deleteTodo(id: any) {
const url = `${this.todoUrl}${id}`;
return this.http.delete(url).pipe(
catchError((err) => {
console.log('error caught in service')
console.error(err);
return throwError(err); //Rethrow it back to component
})
);
}

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.

Angular2 Http Call not firing

Context :
Following several tutorials, I am testing authentication with Angular2 and JWT. I come with a component and a service :
app.component.ts
user.service.ts
App component (and template) contains the subscription to an observable that shows the user logged in status. The Observable item is kept in the user service, and changes (fine) when user logs in and out.
The authentication token is written in "localStorage" as "auth_token". It contains a validity value (time) that should force the user to login again after a time.
What I'd like to do is to CHECK the token validity on app init. First, I tried to do it from the user.service CONSTRUCTOR, then (fail), I tried to do it from ngOnInit in the app.component, then (fail again), I tried to do it on event call (click on a button) from the app component, but fails again!
Some shortened code :
//app.component.html
//...
<a md-button class="app-icon-button" aria-label="checklogin" (click)="checkLogin()">
<md-icon svgIcon="check"></md-icon>
</a>
//...
//app.component.ts
//...
checkLogin(){
console.log('CHECK LOGIN FUNCTION');
let token = localStorage.getItem('auth_token');
if(token){
console.log('TOKEN FOUND');
this.userService.checkToken(token);
}else{
console.log('NO TOKEN FOUND');
}
}
//...
//user.service.ts
//...
checkToken(token){
console.log('CHECK TOKEN FUNCTION');
console.log('TOKEN : '+token);
let headers = new Headers();
headers.append('Content-Type','application/json');
return this.http
.post(
'/url/script.php',
JSON.stringify(token),
{ headers }
)
.map(res => res.json())
.map((res) => {
console.log('SCRIPT RESULT : ');
if(res.valid){
console.log('TOKEN IS VALID');
return true;
}else{
console.log('TOKEN NOT VALID');
return false;
}
});
}
//...
I did skip the observable part, and subscription.
Problem :
The problem actually is that the app NEVER CALLS the script!
When I do click on the "checkLogin" button (when token exists),
console shows 'CHECK LOGIN FUNCTION',
console shows 'TOKEN FOUND',
console shows 'CHECK TOKEN FUNCTION',
console shows 'TOKEN : '****************************** (token),
But it never shows 'SCRIPT RESULT',
and when using firebug to check if the http call is done, there is NO CALL to the script.php. Looks like the this.http part is just ignored...
Thanks for reading/help
Service starts working when subscription used only when consumer subscribe to output result, using .subscribe method.
You need: this.userService.checkToken(token).subscribe()
Your checkToken() method is returning an Observable that you need to subsrcibe to. An observable will never to execute unless it's subscribed to.
checkLogin(){
console.log('CHECK LOGIN FUNCTION');
let token = localStorage.getItem('auth_token');
if(token){
console.log('TOKEN FOUND');
this.userService.checkToken(token).subscribe(result => {
console.log(result);
}),
error => {
console.log(error);
});
} else {
console.log('NO TOKEN FOUND');
}
}
Ajax call's which use Observables will work only if you have an subscriber.
So you need to subscribe to that Observable. It is an Angular 2 feature. When you don't subscribe the Observable, it will never make that call.
And also you don't need to return anything from the subscriber, because you actually can't return anything.
this.userService.checkToken(token).subscribe((res) => {
console.log('SCRIPT RESULT : ');
if(res.valid) {
console.log('TOKEN IS VALID');
} else {
console.log('TOKEN NOT VALID');
}
});
checkToken(token){
console.log('CHECK TOKEN FUNCTION');
console.log('TOKEN : '+token);
let headers = new Headers();
headers.append('Content-Type','application/json');
return this.http
.post(
'/url/script.php',
JSON.stringify(token),
{ headers }
)
.map(res => res.json());
}
Have You tried using Postman and try to call function you need?
Also, why do You need to validate a token if angular2-jwt can do this for You?
You can do just like this:
install angular2-jwt with npm.
Include in app.module.ts:
import { AUTH_PROVIDERS } from 'angular2-jwt';
add to providers:
providers: [
AUTH_PROVIDERS,
],
and for example auth.service.ts looks like this:
import { Injectable, Inject } from '#angular/core';
import { Http, Response, Headers, RequestOptions, RequestMethod } from '#angular/http';
import { Router } from '#angular/router';
import { Observable } from 'rxjs/Observable';
import { Configuration } from '../../app.config';
import { RegisterViewModel } from '../../model/viewModel/registerViewModel';
import { LoginViewModel } from '../../model/viewModel/loginViewModel';
import { tokenNotExpired, AuthHttp } from 'angular2-jwt';
#Injectable()
export class AuthService {
private actionUrl: string;
constructor(private _http: Http, private _config: Configuration, private _router: Router, private _authHttp: AuthHttp){
this.actionUrl = _config.apiUrl;
}
register(user: RegisterViewModel){
let headers = new Headers({ 'Content-Type': 'application/json' });
//Admin in this system can only register users. that is why auth
return this._authHttp.post(this.actionUrl + '/Account/Register', JSON.stringify(user), { headers : headers })
.do(response => {
console.log(response.toString());
});
}
login(user: LoginViewModel) {
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
return this._http.post('http://localhost:56181/api/token', "username=" + user.userName + "&password=" + user.password + "&userId=" + user.userId, { headers : headers })
.do(response => {
if(response){
let authResult = response.json();
this.setUser(authResult);
this._router.navigate(['']);
}
});
}
public isAuthenticated(): boolean {
//angular2-jwt has this function to check if token is valid
return tokenNotExpired();
}
private setUser(authResult: any): void {
localStorage.setItem('id_token', authResult.id_token);
}
public logout(): void {
localStorage.removeItem('id_token');
this._router.navigate(['']);
}
}
also remember that angular2-jwt has default name for token in localstorage as id_token or else you will have to use angular2-jwt help class to specify other token name.
You can check if it is working by simply doing this:
in app.component.ts:
export class AppComponent {
constructor(private _auth: AuthService){
}
}
and in app.component.html:
<li>
<a class="nav-link" [routerLink]="['/login']" *ngIf="!_auth.isAuthenticated()">Login</a>
</li>
<li>
<a class="nav-link" (click)="_auth.logout()" *ngIf="_auth.isAuthenticated()">Log Out</a>
</li>
also You can read a little bit documentation about it in:
https://auth0.com/blog/introducing-angular2-jwt-a-library-for-angular2-authentication/

Angular 2 HTTP Service not returning promise

I'm trying to get an angular 2 service to retrieve data from an HTTP request and return it as a promise. When I use the service in the component, the data I'm passing from the service is returned as undefined.
This is my service
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class RecordService {
constructor(private http: Http) {}
getPosts(): Promise<any> {
return this.http
.get('https://jsonplaceholder.typicode.com/posts')
.toPromise()
.then((response: Response) => response.json().data)
.catch(this.handleError);
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error);
console.log('ERROR');
return Promise.reject(error.message || error);
}
}
and this is my component
import { Component, OnInit } from '#angular/core';
import { RecordService } from './record.service';
import { Router } from '#angular/router';
#Component({
selector: 'record-view',
template: '<h1>This is the record creation page</h1>',
providers: [RecordService]
})
export class RecordComponent implements OnInit{
message: string;
error: any;
constructor(private recordService: RecordService) {
}
ngOnInit() {
this.recordService.getPosts()
.then(data => console.log(data))
.catch(error => console.log(error));
}
}
Any ideas why the data would be undefined?
response.json() already gives you back the data object of your response as JSON, so remove the .data property access.
When you response.json() the result is the exact content from the response of the request you made.
In this case, https://jsonplaceholder.typicode.com/posts returns an array (if open the url in a browser you'll see the array): [{...}, {...}, ...].
From response.json().data remove .data and add || {} if body is null
Finally:
.then((response: Response) => response.json() || {})

Categories

Resources