subscribing in Angular - javascript

I am completely new to Angular and I've created a project using SpringBoot 2.0.5.RELEASE, Angular 5 and spring data to build an end to end single page java web application. I use spring boot 1.5 to expose REST APIs and angular5 with routing to build the client that will consume the APIs exposed by the server.
I've defined this component:
import { Component } from '#angular/core';
import { Router } from '#angular/router';
import { User } from '../models/user.model';
import { UserService } from './user.service';
#Component({
templateUrl: './add-user.component.html'
})
export class AddUserComponent {
user: User = new User();
constructor(private router: Router, private userService: UserService) {
}
createUser(): void {
alert ('lala');
this.userService.createUser(this.user)
.subscribe( data => {
alert('User created successfully.');
});
}
}
in the page I can see the alert lala, but not 'User created successfully.' but I have no idea why
The link address when I create a user is this is this one http://localhost:4200/api/users
This is my proxy.config.json file:
{
"/api/*": {
"target": "http://localhost:8080/user-portal",
"secure": false
}
}
and from curl is fine :
curl -X POST -H "Content-Type: application/json" "http://localhost:8080/user-portal/api/users"
and user.service.ts:
import {Injectable} from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { User } from '../models/user.model';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
#Injectable()
export class UserService {
constructor(private http: HttpClient) {}
private userUrl = '/api/users';
public getUsers() {
return this.http.get<User[]>(this.userUrl);
}
public deleteUser(user) {
return this.http.delete(this.userUrl + '/'+ user.id);
}
public createUser(user) {
return this.http.post<User>(this.userUrl, user);
}
}

Firstly, best not to use alert. Use console.log. Secondly, you are only handling success, you are not handling failure. Do this:
createUser(): void {
console.log('lala');
this.userService.createUser(this.user)
.subscribe(data => {
console.log('User created successfully', data);
},
err => {
console.log('There was an error', err);
},
() => {
console.log('I have completed now and nothing will ever be emitted from this Observable again');
});
}
The error handler will be executed if the HTTP response is not a success response, viz if the status code of the response is not in the 2xx range.
Check your browser network tab also to see if the HTTP request is failing.
You prob also want to debug this:
public createUser(user) {
console.log('userUrl', this.userUrl)
console.log('user', user)
return this.http.post<User>(this.userUrl, user);
}
To make sure all is as expected.

In Chrome hit F12 to open the dev tools and go to the network tab. Make sure that a request is being made to the end point and that it is not throwing and error.

Related

How can I get the user's email from Keycloak in this Angular app?

I am working on an app in Angular 14 that requires authentication/authorization, reason for witch I use Keycloak Angular
.
I need to get the currently logged in user's data from the application.
For this purpose, I have a service:
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { User } from '../../../models/user';
#Injectable({
providedIn: 'root'
})
export class UserFormService {
httpOptions: object = {
headers: new HttpHeaders({
'Content-Type' : 'application/json'
})
}
apiURL: string = 'http://localhost:8080';
constructor(private http: HttpClient) { }
public currentUserEmail: any;
public currentUserData: any;
public async getUserEmail(){
let currentUser = await this.keycloakService.loadUserProfile();
this.currentUserEmail = currentUser.email;
}
public getUserByEmail(email: string): Observable<User>{
return this.http.get<User>(`${this.apiURL}/getUserByEmail/${email}`, this.httpOptions);
}
}
I use it in a component:
public getUserByEmail() {
this.supplierFormService.getUserByEmail(this.currentUserEmail).subscribe(response => {
this.currentUser = response;
console.log('currentUser: ', response);
});
}
In keycloak.init.ts I have:
import { KeycloakService } from 'keycloak-angular';
export function initializeKeycloak(keycloak: KeycloakService) {
return () =>
keycloak.init({
config: {
url: 'http://localhost:8085',
realm: 'MyRealm',
clientId: 'my-app'
},
initOptions: {
onLoad: 'check-sso',
silentCheckSsoRedirectUri:
window.location.origin + '/assets/silent-check-sso.html'
}
});
}
ngOnInit(): void {
// Get user's email
this.getUserEmail();
// Get user's data by email
this.getUserByEmail();
}
The problem
Instad of returning the user's data, the service throws a 500 (Internal Server Error) and the email is undefined, as can be seen below:
http://localhost:8080/getUserByEmail?email=undefined
How do I fix this problem?
You should sync those two calls, the getUserByEmail may be excecuted faster then currentUserEmail is set:
async ngOnInit(): void {
// Get user's email
await this.getUserEmail();
// Get user's data by email
this.getUserByEmail();
}
decode jwt token returned from keycloak. It contains current user data and Id
Then get user by this id

Error in Logout through MSAL.JS in Javascript / Angular 6 SPA application using B2C

I have one Javascript SPA application using MSAL.JS for authentication against Azure AD B2C and another Angular 6 SPA application using MSAL for Angular against Azure AD B2C.
In both the applications the logout is throwing below error.
Correlation ID: 6de6e068-7b07-4d24-bac4-c1af3131815b
Timestamp: 2018-09-25 16:16:20Z
AADB2C90272: The id_token_hint parameter has not been specified in the request. Please provide token and try again.
For Logout, MSAL has very simple logout api which does not take any parameter, so how can I provide id_token_hint? Am I missing something? Is there any config parameter I need to provide while injecting MsalModule in Angular Application. Or anything similar in Javascript app for Msal.UserAgentApplication.
I m basically using the currently latest "msal": "^0.2.3" , this is my authentication service, there is no configuration needed in the app.module, and the logout works perfectly:
import { Injectable } from '#angular/core';
import { environment } from '../../environments/environment';
import * as Msal from 'msal';
import { User } from "msal/lib-commonjs/User";
import { ApiService } from './api.service';
import { BackendRoutes } from './backend.routes';
#Injectable()
export class AuthenticationService {
private _clientApplication: Msal.UserAgentApplication;
private _authority: string;
constructor(private apiService: ApiService, private backendRoutes: BackendRoutes) {
this._authority = `https://login.microsoftonline.com/tfp/${environment.tenant}/${environment.signUpSignInPolicy}`;
this._clientApplication =
new Msal.UserAgentApplication(
environment.clientID,
this._authority,
this.msalHandler,
{
cacheLocation: 'localStorage',
redirectUri: window.location.origin
});
}
msalHandler(errorDesc: any, token: any, error: any, tokenType: any) {
let userAgent: Msal.UserAgentApplication = <any>(this);
if (errorDesc.indexOf("AADB2C90118") > -1) {
//Forgotten password
userAgent.authority = `https://login.microsoftonline.com/tfp/${environment.tenant}/${environment.passResetPolicy}`;
userAgent.loginRedirect(environment.b2cScopes);
} else if (errorDesc.indexOf("AADB2C90077") > -1) {
//Expired Token, function call from interceptor with proper context
this.logout();
}
}
addUser(): void {
if (this.isOnline()) {
this.apiService.post(this.backendRoutes.addUser).subscribe();
}
}
login(): void {
this._clientApplication.loginRedirect(environment.b2cScopes);
}
logout(): void {
this._clientApplication.logout();
}
getAuthenticationToken(): Promise<string> {
return this._clientApplication.acquireTokenSilent(environment.b2cScopes)
.then(token => token)
.catch(error => {
return Promise.reject(error);
});
}
And the interceptor linked to it:
export class AuthenticationHttpInterceptor implements HttpInterceptor {
constructor(private authenticationService: AuthenticationService) {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return from(this.authenticationService.getAuthenticationToken()
.then(token => {
return req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
})
.catch(err => {
this.authenticationService.msalHandler(err,null,null,null);
return req;
}))
.switchMap(req => {
return next.handle(req);
});
}
}

Angular: Make Post Request From ErrorHandler?

I am trying to send any errors, exceptions that Angular is catching to my server. I made my own class called GlobalErrorHandler that is extending ErrorHandler. Please check below
import { ErrorHandler, Injectable, Injector } from "#angular/core";
import {HttpHeaders, HttpClient} from "#angular/common/http";
import { TestServiceService } from "../_services/test-service.service";
#Injectable()
export class GlobalErrorHandler implements ErrorHandler {
constructor(
private injector: Injector,
private http: HttpClient,
private service: TestServiceService,
) {}
url = 'http://127.0.0.1:4000/post';
handleError(error) {
const httpOptions = {
headers: new HttpHeaders({'Content-Type': 'application/json'})
};
try {
let msg = JSON.parse(error)
console.log('>>>>>>>>> message is ', msg)
this.http.post(this.url, msg, httpOptions);
}
catch (e) {
console.log('>>>>>>>>> err in catch is ', e)
}
}
}
I am able to console.error(error) whenever an error occurs, but I cannot make a post request to my server.
What am I missing in my code to make post request from ErrorHandler?
After changing the code to the following (replacing JSON.parse with JSON.stringify and catching the post errors successfully):
handleError(error) {
const httpOptions = {
headers: new HttpHeaders({'Content-Type': 'application/json'})
};
let subscription = this.http.post(this.url, JSON.stringify(error), httpOptions).subscribe(
(data => {console.log('>>>>>>> data is ', data));subscription.unsubscribe();},
error => {console.log('>>>>>>>> err', error);subscription.unsubscribe();}
)
}
The error was discovered to be on the serverside, but the code above should be useful to anyone trying to trasmit clientside errors(in Angular2+) to the server provided that the server has been implemented correctly.

Angular/TypeScript : How to resolve this compilation issue?

Hell
I ma new in angular 5. I am create a login and auth service. But i cannot compile my code. Here is my code
// user.service.ts
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class UserService {
private loggedIn = false;
constructor(private http: Http) {
// this.loggedIn = !!localStorage.getItem('auth_token');
}
//authenticate user with dummy logic because i use a json-server
authenticate(login:string, password:string) {
console.log('Authenticate ....');
const credentials = {login:login, password:password};
let headers = new Headers();
headers.append('Content-Type', 'application/json');
var result = this.http
.get(
'/users?login?'+JSON.stringify(login),
{ headers }
);
if(result.password==password){
return true;
}
return false;
}
}
When i compile ( ng server ) i get the following error
ERROR in src/app/auth/user.services.ts(28,17): error TS2339:
Property 'password' does not exist on type 'Observable<Response>'.
Line 28 is : if(result.password==password){
I don't know what i am missing ?I try to understand the Observable concept. If you add an idea, it will help me.
Thanks
result here is an observable, you need to subscribe to it to get response.
Something like below:
var result = this.http.get(
'/users?login?'+JSON.stringify(login),
{ headers }
);
//result is an observer here, you have to subscribe to it
result.subscribe((response) => {
if(response.password==password){
return true;
}
return false;
});
You can check this awesome article: https://gist.github.com/staltz/868e7e9bc2a7b8c1f754
Use Observables properly
Use HttpClient, not old Http
You can also define a User class to make typing more strict.
// user.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class UserService {
private loggedIn = false;
constructor(private http: HttpClient) {
// this.loggedIn = !!localStorage.getItem('auth_token');
}
//authenticate user with dummy logic because i use a json-server
authenticate(login:string, password:string) :Observable<boolean> {
return this.http
.get('url/whatever') //returns a User object having password
.map(user => user.password === password); // maps result to the desired true or false value
}
}
// to consume the service from a component, for example
this.userService.authenticate('myusername', 'mypassword')
.subscribe(authenticated => {
console.log('login status', authenticated)
})
You are trying to access the observable returned from the http call.
To get the information in the observable you have to subscribe to it.
For detailed information about hot to get remote data please read this:
https://angular.io/guide/http
NOTE: You should not use the deprecated angular/http. Use angular/common/http instead.

Angular 2 HTTP GET to Node backend for list of file names in directory

I'm trying to use an Angular 2 HTTP GET request to simply connect with a Node/Express backend that responds with a list of the file names in a certain folder using the fs.readdir method.
I set up the Angular 2 request as a service:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import './rxjs-operators';
#Injectable()
export class PhotoService {
constructor (private http: Http) {}
private photosUrl = '/api/photos'; // URL to web API
getPhotos() : Observable<string[]> {
return this.http.get(this.photosUrl)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body.data || { };
}
private handleError (error: any) {
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg); // log to console instead
return Observable.throw(errMsg);
}
}
and then called this service from a component:
ngOnInit() {
this.photoService.getPhotos()
.subscribe(
photos => this.fileList = photos,
error => this.errorMessage = <any>error);
}
This is the Node backend (with Express set up as per conventions):
//Photo Service
app.get('/api/photos', function(req, res) {
fs.readdir('./uploads', function(error, files) {
if (error) {
throw error;
}
else {
res.end(files);
}
});
});
As seen, the HTTP request calls a GET method to http://localhost:3000/api/photos and the Node backend is supposed to receive that request and send back an array of strings that have the names of files in the 'uploads' folder.
However it does not seem to be working. I think I'm getting confused with the format in which the Node API sends the response and how that works with the Observable type that Angular uses in the service.
Your Angular 2 code looks good to me. But in your Node backend you should not send data with res.end() (see the documentation). Correct would be res.send(files); or in your case res.json(files); which will also set the right Content-Type header.

Categories