Angular2 Passing parameters to web service http GET - node.js

I have a profileComponent which is making a GET call to service endpoint as follows , AparmentService is injected in bootstarp, hence no providers
#Component({
selector: 'profile',
template: `<h1>Profile Page</h1>
{{userEmail.email}}
{{profileObject | json}}
`,
directives: [ROUTER_DIRECTIVES]
})
export class ProfileComponent implements OnInit {
userEmail = JSON.parse(localStorage.getItem('profile'));
public profileObject: Object[];
constructor(private apartmentService: ApartmentService) {
this.apartmentService = apartmentService;
}
ngOnInit(): any {
console.log(this.userEmail.email); <--This value displays fine in the console
this.apartmentService.getProfile(this.userEmail.email).subscribe(res => this.profileObject = res); <-- getting [] response for this
console.log(JSON.stringify(this.profileObject)); <-- undefined
}
}
The service looks like this
#Injectable()
export class ApartmentService {
http: Http;
constructor(http: Http) {
this.http = http;
}
getProfile(userEmail :string){
return this.http.get('/api/apartments/getprofile/:userEmail').map((res: Response) => res.json());
}
}
when I try to hit the endpoint directly in the browser with the parameter, I am getting the respone. But not within Angular.
Any Ideas ?

http.get() is async
ngOnInit(): any {
console.log(this.userEmail.email); <--This value displays fine in the console
this.apartmentService.getProfile(this.userEmail.email).subscribe(res => this.profileObject = res); <-- getting [] response for this
// at this position the call to the server hasn't been made yet.
console.log(JSON.stringify(this.profileObject)); <-- undefined
}
When the response from the server arives res => this.profileObject = res is executed. console.log() is made before the call to the server was even initalized
Use instead
ngOnInit(): any {
console.log(this.userEmail.email); <--This value displays fine in the console
this.apartmentService.getProfile(this.userEmail.email)
.subscribe(res => {
this.profileObject = res;
console.log(JSON.stringify(this.profileObject));
});
}
I think :userEmail in the URL isn't doing what you expect. Try instead:
getProfile(userEmail :string){
return this.http.get(`/api/apartments/getprofile/${userEmail}`).map((res: Response) => res.json());
}

Related

How to handle error while returning an observable in angular?

I am creating an AuthGuard for my app..now when i try to load the component without getting logged in it should redirect me to the login page..But i am getting an error like following
and nothing happens.
I am throwing this error from my backend {"status":401,"message":"Auth Token Not found!"}}
as there is no auth token
The following is the code of my AuthGuard
export class AuthGuardService implements CanActivate {
constructor(private authService: AuthService, private router: Router) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | Observable<boolean> {
return this.authService.checkLogin().pipe(
map((data: HttpResponse) => {
if (data.status == 200) {
console.log("OUTPUT:", data)
return true
}
else return false
}),
)
}
}
The following is my function in AuthService:
public checkLogin():Observable<HttpResponse> {
return this.http.get<HttpResponse>('http://localhost:5000/auth/check-login', { withCredentials: true })
}
Now how can i handle the errors like these and set a fallback value to false so if any error occurs then that route could not be accessed
If I understood you correctly you want to achieve the following behavior:
If checkLogin() gets a response that indicates "success", the auth-guard shall return true
If checkLogin() gets an error-response, redirect the user to a fallback-page and return false
If you use the code below, please note that catchError() is only triggered if the backend responds with an exception, while map() is always triggered if a success-response comes in from the backend.
Therefore, in the positive case, you can simply return true without checking the contents of the response. However, if you receive an exception from the backend, you can redirect the user to the fallback page using this.router.navigate() and then return of(false) to prevent the request from passing the auth guard.
export class AuthGuardService implements CanActivate {
constructor(private authService: AuthService, private router: Router) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | Observable<boolean> {
return this.authService.checkLogin().pipe(
map(() => true),
catchError(() => {
this.router.navigate(['route-to-fallback-page']);
return of(false);
})
);
}
}
Alternative solution for Angular 7.1+
Starting from Angular 7.1 you can just return a UrlTree-Object containing the fallback-route:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> {
return this.authService.checkLogin().pipe(
map(() => true),
catchError(() => this.router.parseUrl('/route-to-fallback-page'))
);
}
You can use shared error response
this.http.get<HttpResponse>('http://localhost:5000/auth/check-login', { withCredentials: true }).pipe(
catchError((error=>{
this.getErrorMessage(error);
return throwError(()=>error);
}))
)
The getErrorMessage Function will return the errors;
private getErrorMessage(error:HttpErrorResponse){
switch(error.status){
case 400:{
return this.toast.error(`Bad Request :${JSON.stringify(error.error?.Message)}`,error.status.toString())
}
case 401:{
return this.toast.error(`Unauthorized :${JSON.stringify(error.error?.Message)}`,error.status.toString())
}
case 403:{
return this.toast.error(`Access Denied :${JSON.stringify(error.error?.Message)}`,error.status.toString())
}
case 500:{
return this.toast.error(`Internal Server Error :${JSON.stringify(error.error?.Message)}`,error.status.toString())
}
case 404:{
return this.toast.error(`Page Not Found :${JSON.stringify(error.error?.Message)}`,error.status.toString())
}
default:{
return this.toast.error('Check your internet connection!');
}
}
}

Assign route dynamically Node/Express

I need dynamically assign a new route but it for some reason refuses to work.
When I send a request in the Postman it just keeps waiting for a response
The whole picture of what I am doing is the following:
I've got a controller with a decorator on one of its methods
#Controller()
export class Test {
#RESTful({
endpoint: '/product/test',
method: 'post',
})
async testMe() {
return {
type: 'hi'
}
}
}
export function RESTful({ endpoint, method, version }: { endpoint: string, version?: string, method: HTTPMethodTypes }) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor): void {
const originalMethod = descriptor.value
Reflect.defineMetadata(propertyKey, {
endpoint,
method,
propertyKey,
version
}, target)
return originalMethod
}
}
export function Controller() {
return function (constructor: any) {
const methods = Object.getOwnPropertyNames(constructor.prototype)
Container.set(constructor)
for (let action of methods) {
const route: RESTfulRoute = Reflect.getMetadata(action, constructor.prototype)
if (route) {
const version: string = route.version ? `/${route.version}` : '/v1'
Container.get(Express).injectRoute((instance: Application) => {
instance[route.method](`/api${version}${route.endpoint}`, async () => {
return await Reflect.getOwnPropertyDescriptor(constructor, route.propertyKey)
// return await constructor.prototype[route.propertyKey](req, res)
})
})
}
}
}
}
Is it possible to dynamically set the route in the way?
I mainly use GraphQL but sometimes I need RESTful API too. So, I want to solve this by that decorator
In order for the response to finish, there must be a res.end() or res.json(...) or similar. But I cannot see that anywhere in your code.

I am not getting response from nodeJS server in angular 2 [duplicate]

This question already has answers here:
How do I return the response from an Observable/http/async call in angular?
(10 answers)
Closed 5 years ago.
I am newbie to MEAN stack development. So, please help me to figure out the problem.
app.js
const express = require('express');
const app = express();
const path = require('path');
app.use(express.static(path.join(__dirname, './darwin-src/public')));
const port = 3000;
app.get('/images', (req, res) => {
console.log('In server');
var data;
var Scraper = require ('images-scraper')
, google = new Scraper.Google();
google.list({
keyword: 'banana',
num: 10,
detail: true,
nightmare: {
show: false
}
})
.then(function (data) {
console.log('first 10 results from google', data);
res.end("" + data);
})
.catch(function(err) {
console.log('err', err);
});
});
app.listen(port, () => {
console.log(`Starting the server at port ${port}`);
});
image-service.service.ts
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { Image } from './model/image';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import 'rxjs/add/observable/of';
#Injectable()
export class ImageServiceService {
constructor(private http: Http) { }
private serverApi = 'http://localhost:3000';
public getImages(image: string): Observable<Image[]> {
console.log('Inside Service');
let URI = `${this.serverApi}/images`;
return this.http.get(URI)
.map(function(res) {
return res.json();
});
}
}
image-view.component.ts
import { Component, OnInit } from '#angular/core';
import { ImageServiceService } from '../image-service.service';
import { Image } from '../model/image';
#Component({
selector: 'app-image-view',
templateUrl: './image-view.component.html',
styleUrls: ['./image-view.component.css']
})
export class ImageViewComponent implements OnInit {
private data: Image[] = [];
constructor(private imageService: ImageServiceService) { }
ngOnInit() {
}
onSubmit(image: string) {
console.log(image);
this.imageService.getImages(image).subscribe(response => this.data = response);
console.log(this.data.length);
}
}
The length of array is zero and I can't figure out why. The response comes on nodejs console after a while but the frontend displays the result before the response comes. Please help!
Hit the server url separately in browser and see if you get the expected response. If this is okay, then the problem is with the client.
On seeing your client code, one issue seems obvious. You are not using the observable from ImageServiceService properly. All your manipulations should be within the subscribe method.
onSubmit(image: string) {
this.imageService.getImages(image).subscribe(response => {
this.data = response;
console.log(this.data.length);
// Do other manipulations that you wish to do
});
}
If you using the observable to display something in the view, then
consider . using async pipe
The code in the subscribe handler is not executed synchronously. So, your console.log statement is executed before you get a response from your server. I don't see your image-view.component.html markup. But, I believe you need to use the async pipe in your bound option.
private data$: Observable<Image[]>;
onSubmit(image: string) {
console.log(image);
this.data$ = this.imageService.getImages(image);
}
And you HTML:
<div *ngFor="let image of data$ | async">
{{image.value}}
</div>

Storing observable data into global variable returns 'undefined' in Angular 4

I am currently working with a node server that I've set up and created an endpoint /user-info which res.send({id: <my-id>, name: <my-display-name>})
On Angular I have created a global.service.ts file that will call this endpoint using http.get and subscribe that data and store into two variables I have declared.
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable()
export class Globals {
constructor(private http: HttpClient) { }
public id: string;
public name: string;
userInfo() {
this.http.get('/user-info').subscribe(
(data: any) => {
this.id = data.id;
}
);
console.log(this.id);
}
}
Once I console.log(this.id) it returns undefined. I have already the server-side to see if that was causing the problem but it returns a string.
In my server.js file (node server) I am working with express. This is the endpoint:
app.get('/user-info', function(req, res) {
client.get('id', (err, data) => {
client.get('name', (err, reply) => {
res.send({id: data, name: reply})
})
})
})
I am using redis to store values 'id' and 'name' and the client.get is just a redis command used to call those values from cache. I have tested just checking localhost:8000/user-info and everything looks fine.
Am I missing/misunderstanding something? Thanks!
if console.log still outside of call, it will execute before you got a response. Try this:
userInfo() {
this.http.get('/user-info').subscribe(
(data: any) => {
this.id = data.id
console.log(this.id);
}
)
}

Why can I not call upon the property of this object? (Angular, MongoDB)

I am making an angular application which uses a MongoDB database and NodeJS server.
The idea is that I make an application which for now only has a list of posts and beside that the detailed-post. The components are nicely standing next to eachother and working but I have one problem. When I try to retrieve a single post I can see via console.dir(post) that all is good and the object has been transmitted to the angular app. The problem is that when I try to use post.content I get an undefined message.
I have searched for hours but can not seem to find the cause of this. I would greatly appreciate any help you can give me. Beneath here is all the information, if you need to see something else, please tell me.
Thanks in advance.
This is the post-detail.component.html where I want to display the data.
<div class="row">
<div class="col-xs-12">
<p>Content:</p>
<h1>{{ post.content }}</h1>
</div>
</div>
The detail.ts file (I left out the imports)
#Component({
selector: 'app-post-detail',
templateUrl: './post-detail.component.html',
styleUrls: ['./post-detail.component.css']
})
export class PostDetailComponent implements OnInit {
post: Post = new Post();
id: string;
constructor(private postService: PostService,
private route: ActivatedRoute,
private router: Router) {
}
ngOnInit() {
this.route.params
.subscribe(
(params: Params) => {
this.id = params['id'];
this.postService.getPost(this.id).then(res => {
console.dir(res);
console.dir(res.content);
this.post = res;
});
}
);
}
}
The post.service.ts which I am using to retrieve the actual data:
#Injectable()
export class PostService {
postChanged = new Subject<Post[]>();
private headers = new Headers({'Content-Type': 'application/json'});
private serverUrl = environment.serverUrl + '/blogPosts/'; // URL to web api
private posts: Post[];
constructor(private http: Http) {
}
//this one DOES work
getPosts() {
console.log('Fetching BlogPosts from database.')
return this.http.get(this.serverUrl, {headers: this.headers})
.toPromise()
.then(response => {
this.posts = response.json() as Post[];
return response.json() as Post[];
})
.catch(error => {
return error;
});
}
getPost(index: string) {
console.log('Fetching individual BlogPost from database.');
console.log('index' + index);
if (index == null) {
console.log('null');
return null;
}
return this.http.get(this.serverUrl + index, {headers: this.headers})
.toPromise()
.then(response => {
console.dir(response.json().content);
console.dir(response.json());
return response.json() as Post;
})
.catch(error => {
return this.handleError(error);
});
}
}
The Post model:
export class Post {
private id: string;
private _content: string;
constructor(values: Object = {}) {
Object.assign(this, values);
}
public get _id(): string {
return this.id;
}
public set _id(n: string) {
this.id = n;
}
public get content(): string {
return this._content;
}
public set content(n: string) {
this._content = n;
}
}
And I added in the Postman GET /blogPost/id and the console log as images.
Thanks!
Console log
Postman GET route
I might be wrong but can you please change the _content to content everywhere in the service ?
Edit: are you sure the this.id is correct when you call the service method ? cause if it is null or undefined then return null will be executed.
Another note is that in Postman i see the response is an array of objects (one object) at this example. Can you try this.post = res[0]; in the component ?
return response.json() as Post;
In post.service.ts should be:
return response.json()[0] as Post;
I did not see that the object was wrapped in an array, by accessing it I was able to get it out and use it.

Resources