How to migrate from MOCK API to real API (Angular) - reactjs

I am new to Angular, and trying to connect this sample app to my API gateway, which is a ContactApp.
Currently it works on mock API and I want to use an actual api server. I have done all the steps from this website but it seems like a wrong approach, It didn't work. Can someone explain how I should configure it?
here is the request call for getContacts
getContacts(): Observable<Contact[]>
{
return this._httpClient.get<Contact[]>('api/apps/contacts/all').pipe(
tap((contacts) => {
this._contacts.next(contacts);
})
);
}
and here is the mock api reply:
this._MockApiService
.onGet('api/apps/contacts/all')
.reply(() => {
// Clone the contacts
const contacts = cloneDeep(this._contacts);
// Sort the contacts by the name field by default
contacts.sort((a, b) => a.name.localeCompare(b.name));
// Return the response
return [200, contacts];
});
All I need is an explanation of steps I should follow to migrate MOCK API to cloud..or any similar project?

You can simply do this:
On your .ts file:
//your imports
import { HttpClient, HttpBackend } from "#angular/common/http";
//
export class YourComponent implements OnInit{
contacts:any;
...
//your constructor
constructor(private http: HttpClient)
//initialize the function here
ngOnInit(): void {
this.getContacts()
}
//your function to get the contacts API data
getContacts(){
return this.http.get("your API endpoint")
.subscribe((contacts: any) => {
this.contacts= response;
});
}
then on your .html file, bind the data
<p *ngFor="let contacts of contacts">{{contacts.name}}</p>

Related

How to properly load gmail api to angular 2 app

I am kind of new to angular 2 so ill try to explain the requirement in details.
The app I build has a login page (/login) and has the settings page (/settings).
when the user access the login page the gapi var is initialized properly then the user logs in to the app.
Once the user is in he has the settings page. the issue starts when the user refresh the page, when that happens the gapi var is no longer recognized and become undefined. My though is that the gapi library is not getting loaded and therefore it fails.
I placed the following code in the app index.html file
<script type="text/javascript">
// Client ID and API key from the Developer Console
var CLIENT_ID = '***.apps.googleusercontent.com';
// Array of API discovery doc URLs for APIs used by the quickstart
var DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/gmail/v1/rest"];
// Authorization scopes required by the API; multiple scopes can be
// included, separated by spaces.
var SCOPES = 'https://www.googleapis.com/auth/gmail.readonly';
/**
* On load, called to load the auth2 library and API client library.
*/
function handleClientLoad() {
console.log("handleClientLoad")
gapi.load('client:auth2', initClient);
}
/**
* Initializes the API client library and sets up sign-in state
* listeners.
*/
function initClient() {
gapi.client.init({
discoveryDocs: DISCOVERY_DOCS,
clientId: CLIENT_ID,
scope: SCOPES
}).then(function () {
// Listen for sign-in state changes.
console.log("client init");
gapi.auth2.getAuthInstance().isSignedIn.listen();
// Handle the initial sign-in state.
//updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
});
}
</script>
<script async defer src="https://apis.google.com/js/api.js"
onload=this.onload=function(){};handleClientLoad();
onreadystatechange="if (this.readyState === 'complete') this.onload()";>
</script>
To conclude how can I properly load the gapi module to handle the above refresh scenario?
I tried to work with the solution from Best way to wait for 3rd-party JS library to finish initializing within Angular 2 service? however it didnt work, gapi is still undefined.
What I've done is create a custom GoogleService that is responsible for initializing the GAPI client in Angular apps. Instead of interacting with the GAPI client directly, my app interacts with the GoogleService.
For example (using Angular 9.x)
import { Injectable } from '#angular/core';
import { environment } from '../environments/environment';
#Injectable({
providedIn: 'root',
})
export class GoogleService {
private gapiAuth?: Promise<gapi.auth2.GoogleAuth>;
constructor() {
// Chrome lets us load the SDK on demand, but firefox will block the popup
// when loaded on demand. If we preload in the constructor,
// then firefox won't block the popup.
this.googleSDK();
}
async signinGoogle() {
const authClient = (await this.googleSDK()) as gapi.auth2.GoogleAuth;
const googleUser = await authClient.signIn();
const profile = googleUser.getBasicProfile();
return {
type: 'GOOGLE',
token: googleUser.getAuthResponse().id_token as string,
uid: profile.getId() as string,
firstName: profile.getGivenName() as string,
lastName: profile.getFamilyName() as string,
photoUrl: profile.getImageUrl() as string,
emailAddress: profile.getEmail() as string,
};
}
async grantOfflineAccess() {
const authClient: gapi.auth2.GoogleAuth = (await this.googleSDK()) as any;
try {
const { code } = await authClient.grantOfflineAccess();
return code;
} catch (e) {
// access was denied
return null;
}
}
// annoyingly there is some sort of bug with typescript or the `gapi.auth2`
// typings that seems to prohibit awaiting a promise of type `Promise<gapi.auth2.GoogleAuth>`
// https://stackoverflow.com/questions/54299128/type-is-referenced-directly-or-indirectly-in-the-fulfillment-callback-of-its-own
private googleSDK(): Promise<unknown> {
if (this.gapiAuth) return this.gapiAuth;
const script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.defer = true;
script.src = 'https://apis.google.com/js/api.js?onload=gapiClientLoaded';
this.gapiAuth = new Promise<void>((res, rej) => {
(window as any)['gapiClientLoaded'] = res;
script.onerror = rej;
})
.then(() => new Promise(res => gapi.load('client:auth2', res)))
.then(() =>
gapi.client.init({
apiKey: environment.google.apiKey,
clientId: environment.google.clientId,
discoveryDocs: environment.google.discoveryDocs,
scope: environment.google.scopes.join(' '),
}),
)
.catch(err => {
console.error('there was an error initializing the client', err);
return Promise.reject(err);
})
.then(() => gapi.auth2.getAuthInstance());
document.body.appendChild(script);
return this.gapiAuth;
}
}

angular 2, http service not requesting [duplicate]

When I make a post request the angular 2 http is not sending this request
this.http.post(this.adminUsersControllerRoute, JSON.stringify(user), this.getRequestOptions())
the http post is not sent to the server but if I make the request like this
this.http.post(this.adminUsersControllerRoute, JSON.stringify(user), this.getRequestOptions()).subscribe(r=>{});
Is this intended and if it is can someone explain me why ? Or it is a bug ?
Since the post method of the Http class returns an observable you need to subscribe it to execute its initialization processing. Observables are lazy.
You should have a look at this video for more details:
https://egghead.io/lessons/rxjs-rxjs-observables-vs-promises
You must subscribe to the returned observable if you want the call to execute.
See also the following angular documentation "Communicating with backend services using HTTP".
Starting the request
For all HttpClient methods, the method doesn't begin its HTTP request until you call subscribe() on the observable the method returns.
This is true for all HttpClient methods.
You should always unsubscribe from an observable when a component is destroyed.
All observables returned from HttpClient methods are cold by design.
Execution of the HTTP request is deferred, letting you extend the observable with additional operations such as tap and catchError before anything actually happens.
Calling subscribe() triggers execution of the observable and causes HttpClient to compose and send the HTTP request to the server.
Think of these observables as blueprints for actual HTTP requests.
In fact, each subscribe() initiates a separate, independent execution of the observable.
Subscribing twice results in two HTTP requests.
const req = http.get<Heroes>('/api/heroes');
// 0 requests made - .subscribe() not called.
req.subscribe();
// 1 request made.
req.subscribe();
// 2 requests made.
On a related note: The AsyncPipe subscribes (and unsubscribes) for you automatically.
Get method doesn't require to use the subscribe method but post method requires the subscribe. Get and post sample codes are below.
import { Component, OnInit } from '#angular/core'
import { Http, RequestOptions, Headers } from '#angular/http'
import 'rxjs/add/operator/map'
import 'rxjs/add/operator/catch'
import { Post } from './model/post'
import { Observable } from "rxjs/Observable";
#Component({
templateUrl: './test.html',
selector: 'test'
})
export class NgFor implements OnInit {
posts: Observable<Post[]>
model: Post = new Post()
/**
*
*/
constructor(private http: Http) {
}
ngOnInit(){
this.list()
}
private list(){
this.posts = this.http.get("http://localhost:3000/posts").map((val, i) => <Post[]>val.json())
}
public addNewRecord(){
let bodyString = JSON.stringify(this.model); // Stringify payload
let headers = new Headers({ 'Content-Type': 'application/json' }); // ... Set content type to JSON
let options = new RequestOptions({ headers: headers }); // Create a request option
this.http.post("http://localhost:3000/posts", this.model, options) // ...using post request
.map(res => res.json()) // ...and calling .json() on the response to return data
.catch((error:any) => Observable.throw(error.json().error || 'Server error')) //...errors if
.subscribe();
}
}

Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL

I am trying to test my Ionic app with Jasmine. This is my test suit.
beforeEach(() => {
auth = new Authentication(<any>new HttpMock(), <any>new StorageMock())
user = new MockUser();
header = new Headers({'uid': '1'});
resp = new Response( new ResponseOptions({body: {name: user.name }, headers: header}))
});
it('facebok login ',(done)=>{
spyOn(auth.http,'post').and.returnValue(HttpMock.returnValue(resp));
spyOn(Facebook,'login').and.returnValue(Promise.resolve({authResponse: {accessToken: 1}}))
auth.facebookLogin().then((res)=>{
expect(auth.http.post.calls.argsFor(0)).toEqual([CONFIG.url.facebookUrl,{}])
expect(res.authResponse.accessToken).toEqual(1);
done();
},(err)=>{
done();
});
});
My HttpMock class to mock http calls looks like this.
export class HttpMock {
static returnValue(data){
return Observable.create((observer) => {
observer.next(data);
})
}
}
The relevant part in the service I am testing is,
facebookLogin(): Promise<any>{
let permissions = ["public_profile","email"];
return Facebook.login(permissions)
.then( (response) => {
let token = { access_token: response.authResponse.accessToken };
return this.login( token ,'facebookUrl').toPromise();
}).catch( this.handleError);
login(data , urlKey): Observable<any>{
return this.http.post(CONFIG.url[urlKey], data)
.map( (res: Response) => this.saveUserInfo(res) ).catch( this.handleError)
}
saveUserInfo(res: Response): Response{
let userInfo = this.getUserInfo(res);
this.user = userInfo;
this.storage.set('user', userInfo);
return res;
}
The facebookLogin method goes like this. Access Facebook class login method which returns a promise. With information from the promise, I make http post request and save the returned data and then convert observable to promise with toPromise. In the test I spy on Facebook.login to return a resolving promise and spyOn http.post to return a successful observable. This is working fine in my app.But I am unable to run the test as it give the following error.
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
The code runs fine till the last point in http.post.map but then is not being run in the test. I think the problem is with the toPromise in the service.
Any kind of hep would be appreciated.
From my limited knowledge on Observable , I believe the problem with the approach was due to the fact that toPromise didnt get the value from observer.next(data). I assume subscription is necessary for that. The simple approach with Observable.of worked for me. You can import it from import 'rxjs/add/observable/of'

Angular 2 and JWT authentication (Auth0)

I have an angular 2 app with JWT authentication (Auth0) that after user login stores the profile and token id in the localStorage, the profile contains a "role" attribute to check if the user can access an especific page.
Everything works like a charm but if the user change the role property in the localStorage to "admin" for example and reload the app he can access pages that he is not authorized. How can I handle that?
auth.service.ts:
declare var Auth0Lock: any;
#Injectable()
export Class AuthService {
lock = new Auth0Lock(ID, domain);
user: Object;
constructor() {
this.user = JSON.parse(localStorage.getItem('profile'));
}
public login() {
// Auth0 method
this.lock.show({}, (err: string, profile: Object, token: string) => {
if (err) { console.log(err); return; }
localStorage.setItem('profile', JSON.stringify(profile));
localStorage.setItem('id_token', token);
});
}
public logout() {
localStorage.removeItem('profile');
localStorage.removeItem('id_token');
}
public loggedIn() {
return tokenNotExpired();
}
public isAdmin() {
if (this.user['role'] == 'admin') {
return true;
} else {
return false;
}
}
}
app.component.ts:
// imports, etc
export Class AppComponent {
constructor(private auth: AuthService) {}
}
app.component.html:
<nav>
<ul>
<li><a [routerLink]="['Customers']" *ngIf="auth.loggedIn()">Customers</a></li>
<li><a [routerLink]="['Admin']" *ngIf="auth.loggedIn() && auth.isAdmin()">Admin</a></li>
</ul>
</nav>
Any ideas to handle that would be appreciated
In my opinion, it's impossible to create fully secured pages/forms, when you have JavaScript front-end framework. I mean, there is always possibility to open any form, when all application is downloaded to and constructed at client side.
For me, it's important to secure back-end api, and leave "hacking" opportunities to junior developers.
If you have such requirements, try to use server side generated framework: php, asp.net mvc, jsp & etc.
UPDATE
As it came up, Angular2 has directive #CanActivate, which gives more accurate way to cancel navigaition. BUT server side authorization is CRITICAL in javascript front-end based applications.
This will help how to deal with JWT in express back-end: express-jwt

Sending Array of Objects to Web API from AngualrJS

During this process, I grab an array of "vehicles" from the Web API. I modify and do whatever to each vehicle. Then I want to send the list back, without going through and looping...
I've tried a lot of the ways that i've looked up.
I've got this in the WEB API for a breakpoint to see if I can even get the array there, but I havent been able to yet.
public IHttpActionResult UpdateVehicles(Vehicle[] vehiclesArry)
{
return Ok();
}
I'm confused if I need to do a $post, or if I could just "get" it to the correct method like I've been doing. The problem is I can't get the array to the WEB API method.
I've got my $resource setup like this.
return $resource(appSettings.serverPath + "/api/Violators/:id",null,
{
'update': { method: 'PUT' },
'delete': { method: 'DELETE' },
'post': { method: 'POST' }
});
I've tried using $post, but it says the object doesn't support it. I'm not sure what other ways I can try. I've tried using "dynamic" in the web API, that doesn't seem to work either.
You're missing the params object for $resource, so it doesn't know the id.
return $resource(appSettings.serverPath + "/api/Violators/:id", { id: '#id' });
You don't need to explicitly setup methods for get, post, delete. That's already done for you. If your API uses PUT for update, set that up like this:
return $resource(appSettings.serverPath + "/api/Violators/:id", { id: '#id' }, {
update: { method: 'PUT' }
});
Also, the property on your resource must be vehiclesArry exactly or web API won't know how to map it. I also want to echo #sowen. You will need to setup a view model that your endpoint receives.
My assumption is that you are having some script errors in your page or you are not using the $http methods properly.
One problem people usually run into is using the correct url to the web api endpoints in your angular controller. If you don't get it right, you might be getting 404 errors. Look for those in your browser console(network tab)
The below code should work fine without any issues
$http.get("../api/Values/")
.then(function (res) {
var vehicles = res.data;
console.log('data received', JSON.stringify(vehicles));
//Let's update the Name of each vehicle.
$.each(vehicles, function (indx, item) {
item.Name = item.Name + " Updated";
});
console.log('data modified', JSON.stringify(vehicles));
//Let's make a call to web api with modified data
$http.post("../api/Values/UpdateVehicles", vehicles)
.then(function (res2) {
console.log('response', JSON.stringify(res2.data));
});
});
Assuming you have angular js properly loaded in your page and the above code is part of your angular controller for the current page and you have the Web api controller with 2 action methods like below example.
public class ValuesController : ApiController
{
[HttpPost]
[Route("api/Values/UpdateVehicles")]
public IHttpActionResult UpdateVehicles(Vehicle[] vehiclesArry)
{
// just returning whatever came in for TESTING PURPOSE
return Ok(vehiclesArry);
}
public IEnumerable<Vehicle> Get()
{
return new List<Vehicle>
{
new Vehicle {Id = 1, Name = "Car"},
new Vehicle {Id = 2, Name = "Van"}
};
}
}
Also,FYI : I am using Attribute routing in my api controller for the UpdateVehicle endpoint.
Create a model object like
public class UpdateReq
{
public IEnumerable<Vehicle> Vehicles { get; set; }
}
From your angular, just pass a json with an array
{
[v1, v2, v3]
}

Resources