How do you return data to your Angular app using proxy.conf.json? - json

I am using proxy.conf.json as I develop my Angular application.
However I would like to, for a few endpoints, simply return a JSON object when called. Currently my proxy.conf file redirects to a locally running backend which returns these JSONs. However I'd rather not run the backend server and simply return the JSON from proxy.conf.json.
Is this possible somehow?

It is possible by using proxy.conf.js instead of proxy.conf.json. Then you can specify a bypass function where you can return a response directly. This is mentioned in the angular-cli documentation for the proxy but it does not give many details. Here is a sample proxy.conf.js file to do it.
const PROXY_CONFIG = {
'/api': {
'target': 'http://localhost:5000',
'bypass': function (req, res, proxyOptions) {
switch (req.url) {
case '/api/json1':
const objectToReturn1 = {
value1: 1,
value2: 'value2',
value3: 'value3'
};
res.end(JSON.stringify(objectToReturn1));
return true;
case '/api/json2':
const objectToReturn2 = {
value1: 2,
value2: 'value3',
value3: 'value4'
};
res.end(JSON.stringify(objectToReturn2));
return true;
}
}
}
}
module.exports = PROXY_CONFIG;
You need to recheck the url in the bypass function because it is called for all /api requests and then you just directly return a response for the ones you want the others will still be redirected to the target address. You return true to tell the proxy the request finished.
Make sure to then specify the correct file when running ng serve --proxy-config proxy.conf.js.

For returning JSON from proxy.config.conf, there doesn't seem to be an easy way to do it.
One way would be to have a baseApiUrl in the environment.ts file as well urlSuffix set to .json. Then all of the API calls would have to be something like this: enviroment.baseApiUrl + uri + environment.urlSuffix. Then in environment.prod.ts, the urlSuffix would be an empty string. This is a hacky solution but would work.
Alternative using HTTP_INTERCEPTORS
A cleaner solution that leverages the framework, would be to use an HttpInterceptor with the HttpClient along with setting the baseApiUrl in the environment.ts file. This allows for different API endpoints per environment.
environment.ts
export const environment = {
apiBaseUrl: 'http://localhost:4200/assets/api',
production: false
}
development.interceptor.ts
import {Injectable} from '#angular/core';
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '#angular/common/http';
import {Observable} from 'rxjs/Observable';
import {environment} from '../environments/environment';
#Injectable()
export class DevelopmentInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let clonedRequest = null;
if (!environment.production) {
clonedRequest = request.clone({
url: `${request.url}.json`
});
return next.handle(clonedRequest);
}
return next.handle(clonedRequest);
}
}
This class will intercept any http request made by the HttpClient. Using the properties in the environment.ts file, you can check if the current build is a production build. If it is, clone the request and append .json to it. Anything that is in the assets folder is accessible from the browser. Below is a sample file that maps to the url http:localhost:4200/assets/api/test.json.
/src/assets/api/test.json
{
"name": "test",
"description": "Test Data"
}
Place this file in the assets directory and have the directory structure follow the endpoints of the actual API.
test.service.ts
import {Injectable} from '#angular/core';
import {HttpClient} from '#angular/common/http';
import {Observable} from 'rxjs/Observable';
import {environment} from '../environments/environment';
#Injectable()
export class TestService {
private baseUrl = `${environment.apiBaseUrl}/test`;
constructor(private http: HttpClient) {
}
getTestData(): Observable<any> {
return this.http.get(this.baseUrl);
}
}
Import the environment.ts file here and set the base url to the apiBaseurl property. As long as you import the environment.ts file and not the environment.prod.ts file, this will work in all environments as the appropriate environment file will be read from when the app is built. So this url only has to be change in one place per environment.
Angular CLI Build Targets & Environment Files
app.component.ts
import {Component, OnInit} from '#angular/core';
import {TestService} from './test.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'app';
constructor(private testService: TestService) {
}
ngOnInit() {
this.testService.getTestData().subscribe(
(testData) => console.log(testData)
);
}
}
Here the TestService is injected into the AppComponent and the getTestData() method is called to fetch data from the API. The DevelopmentInterceptor checks the environment and appends .json to the request. Then the data is logged to the console.
app.module.ts
import {BrowserModule} from '#angular/platform-browser';
import {NgModule} from '#angular/core';
import {HTTP_INTERCEPTORS, HttpClientModule} from '#angular/common/http';
import {AppComponent} from './app.component';
import {DevelopmentInterceptor} from './development.interceptor';
import {TestService} from './test.service';
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [
TestService,
{
provide: HTTP_INTERCEPTORS,
useClass: DevelopmentInterceptor,
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule {
}
Register the TestService and the DevelopmentInterceptor as providers.
Using this setup, proxy.config.json is not necessary.
For more information on HttpInterceptors, there is the Angular Documentation Intercepting Http Requests & Responses.
There is also a tutorial by Jason Watmore that does some more advanced things with this approach. Angular 5 - Mock Backend Example for Backendless Development

Related

Problem with reading from a json file when refreshing th page Angular

I have an angular application and the client wants the path of the Backend in a json file, so he can change it easily whithout needing of another deployment.
Well i did it, but when i refresh the page or close the app and reopen it, the app don't detect the path of the backend, it is like a problem of retard or synchronisation.
This is the error in the console :
http://***/undefinedapi/Leave/GetlistLeave
This is how i did it :
The json file :
{
"ApiRoot": "http://***/"
}
How i read from the constant from the json file :
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs';
import { apiRoot } from '../model/model.apiRoot';
import { map } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class apiRootService {
static apiRoot: string;
constructor(private http: Http) { }
public initialiseApiRoot()
{
this.http.get('./assets/apiRoot/apiRoot.json').pipe(map((response: Response) =>
<apiRoot>response.json())).subscribe(data => {
apiRootService.apiRoot = data['ApiRoot'];
})
}
}
and then i call this function in the constructor of app.component.ts like this :
this.apiRootService.initialiseApiRoot();
and change the call of the api in every servic elike this :
return this.http.get(apiRootService.apiRoot + .....
Any hlp and thanks
Well, let's suppose you're not facing a cache problem. If it isn't a cache problem, maybe it's a matter of timing.
You can try to set your apiRoot while your app is initializing (before app.component.ts is loaded). You can do that by providing an APP_INITIALIZER as described in Angular docs. If you use a factory that returns a function providing a promise, you'll delay your app initialization until your json file is loaded so you can initialize apiRoot. A factory is a useful approach because it will allow you to inject HttpClient service during initialization in the provider (you'll need it to get your json file).
You can do something like (in your app.module.ts):
...
import {APP_INITIALIZER} from '#angular/core';
...
// Angular will inject the HttpClient because you'll
// tell it that this is a dependency of this factory
// in the providers array
export function getApiRoot(http: HttpClient) {
return () => {
return this.http.get('./assets/apiRoot/apiRoot.json').pipe(
map((response: Response) => <apiRoot>response.json()),
tap((data: any) => apiRootService.apiRoot = data['ApiRoot'])
).toPromise();
};
}
...
#NgModule({
imports: [
...
HttpClientModule,
...
],
providers: [
...
{
provide: APP_INTIALIZER,
useFactory: getApiRoot,
multi: true,
deps: [HttpClient]
}
...
]
})
export class AppModule {}
because you are going with wrong approach. you are seeting url after application is initialized. Refer :- https://medium.com/voobans-tech-stories/multiple-environments-with-angular-and-docker-2512e342ab5a. this will give general idea how to achieve build once and deploy anywhere

Importing JSON from a server within Angular

I am reading data from a JSON, which is one a server and it updates regularly and changes. I need to be able to read this JSON from the server so that I display the most up to date information on my web page.
Currently, the to be able to read the JSONs they are stored within the same project folder as my angular project. (This was because they were not set up on the server when I started).
This is how I currently import the JSON to be able to read it:
import jsonInfo from '../../../info.json'
I thought I would be able to change the file link to the server address, like so:
import jsonInfo from 'http://servername/folder/info.json'
But, VSCode gives me an error: Cannot find module 'http://servername/folder/info.json'
This is definitely the location of the JSON I am trying to load because when I click the link it takes me to the JSON and displays it.
My question is, how do I import the JSON into my .ts from a server so that I can keep getting the updated information from the JSON?
JSON file on a server is just like any other web resource you would try to access (like an API endpoint, for example).
So you should use built in angular http client to access this JSON file.
For example:
import { HttpClient } from '#angular/common/http';
export class SomeService {
constructor(private http: HttpClient) { }
getInfo() {
return this.http.get('http://servername/folder/info.json');
}
}
//...
export class SomeComponent implements OnInit {
info: any;
constructor(private someService: SomeService) {}
ngOnInit() {
this.someService.getInfo().subscribe(info => this.info = info)
}
}
Use HttpClient get method.
this.httpClient.get('http://servername/folder/info.json').subscribe(data => {
// your logic
})
You can use HttpClient and do like as shown below
Working Demo
import { Component, OnInit } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
name = 'Angular';
data = [];
apiUrl = 'http://servername/folder/info.json';
GetData() {
this.http.get<any[]>(this.apiUrl)
.subscribe(data => {
this.data = data;
});
}
ClearData() {
this.data = [];
}
constructor(private http: HttpClient) {}
ngOnInit() {}
}

Unable to get local json file in Angular 5 - persistent 404 error

I've read every other article or post about this I can find. I cannot for the life of me figure out where I'm going wrong with this simple task. (Specifically following this example.) I must be doing something obviously stupid but I've been looking at this so long I can't see it.
I have a json file called isRecognized.json in assets/mockData. I've added the mockData directory to my webpack config file so it's included in the /dist directory. If I go to http:localhost:4200/assets/mockData/isRecognized.json I'm able to see the file, so I know it's available.
However, when I try to retrieve the file using HTTP Client, it throws a 404 no matter what I try.
EDIT: I'm using Webpack, not Angular CLI.
app.component.ts
import { MyService } from './services/my.service';
import { Component, OnInit, Renderer2 } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
/*
* Main app component that houses all views.
*/
#Component({
selector: 'app-comp',
templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
constructor(
private route: ActivatedRoute, private service: MyService
) {}
ngOnInit() {
this.service.isRecognized();
}
}
my.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
#Injectable()
export class MyService {
constructor(private http: HttpClient) { }
isRecognized() {
this.getJSON('isRecognized').subscribe(data => {
console.log(data);
});
}
getJSON(fileName): Observable<any> {
return this.http.get('http://localhost:4200/assets/mockData/' + fileName + '.json');
}
}
The error I get in the browser console is:
AppComponent_Host.ngfactory.js? [sm]:1 ERROR Error: [object Object]
at viewWrappedDebugError (core.js:9795)
at callWithDebugContext (core.js:15101)
at Object.debugCheckAndUpdateView [as checkAndUpdateView] (core.js:14628)
at ViewRef_.webpackJsonp../node_modules/#angular/core/esm5/core.js.ViewRef_.detectChanges (core.js:11605)
at core.js:5913
at Array.forEach (<anonymous>)
at ApplicationRef.webpackJsonp../node_modules/#angular/core/esm5/core.js.ApplicationRef.tick (core.js:5913)
at core.js:5746
at ZoneDelegate.webpackJsonp../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:391)
at Object.onInvoke (core.js:4756)
If I debug the error, I can see the body of the error is:
I have this working successfully in my app just using a URL like this:
private productUrl = 'api/products/products.json';
Notice that it does not have the localhost part of the path.
So try something more like this:
'assets/mockData/' + fileName + '.json'
Also ensure that your angular.json has the path listed under assets:
"assets": [
"src/favicon.ico",
"src/assets",
"src/api"
],
NOTE: I also didn't do anything to modify my webpack configuration. (But I'm using the CLI.)
If you want to look at some working code that accesses a json file, I have an example here:
https://stackblitz.com/edit/github-gettingstarted-deborahk
I would suggest subscribing in the component, and returning an Observable from the service:
Service:
#Injectable()
export class MyService {
constructor(private http: HttpClient) { }
isRecognized(fileName): Observable<any> {
return this.http.get('http://localhost:4200/assets/mockData/' + fileName + '.json');
}
}
Component:
export class AppComponent implements OnInit {
constructor(
private route: ActivatedRoute, private service: MyService
) {}
ngOnInit() {
this.service.isRecognized(fileName)
.subscribe(data => {
console.log(data);
});
}
}
While this might not be a direct solution to your specific problem, you should take a look at the npm package json-server. I use it to mock the API when developing and testing the client.
json-server npm
It will run a node web server on port 3000, and is really easy to use right out of the box.
See this tutorial example of how to use it:
Mock api with json-server
There might be better examples and setting up the proxy isn't necessary, but should be good enough.
Finally figured out the answer! I had this in my app.module.ts file as an import:
InMemoryWebApiModule.forRoot(InMemoryDataService, { dataEncapsulation: false })
Removing this fixed the issue immediately.

Getting the error "No provider for NavController" when trying to pass data from child component to parent component

I'm trying to send data from my home.ts file to the app.component.ts file using EventEmitter and Output. But everytime I reference the home page component in my app.html I get this seemingly random error. When I remove NavController from the constructor in home.ts, the error goes away.
home.ts :
import { Component, EventEmitter, Output } from '#angular/core';
import { NavController } from 'ionic-angular';
#Component({
selector: 'page-home',
templateUrl: 'home.html',
})
export class HomePage {
message : any;
#Output() notify : EventEmitter<Object> = new EventEmitter<Object>();
constructor(public navCtrl: NavController) {
}
ionViewDidLoad(){
this.message = {"Name":"Sid", "Age":17};
this.notify.emit(this.message);
}
}
app.html :
<ion-nav [root]="rootPage"></ion-nav>
<page-home (notify)="getChildData($event)"></page-home>
app.component.ts :
import { Component, ViewChild, ViewChildren } from '#angular/core';
import { Platform } from 'ionic-angular';
import { StatusBar } from '#ionic-native/status-bar';
import { SplashScreen } from '#ionic-native/splash-screen';
import { HomePage } from '../pages/home/home';
#Component({
templateUrl: 'app.html'
})
export class MyApp {
rootPage:any = HomePage;
constructor(platform: Platform, statusBar: StatusBar, splashScreen:
SplashScreen) {
platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
statusBar.styleDefault();
splashScreen.hide();
});
}
getChildData(message){
console.log(message["Name"]);
console.log(message["Age"]);
}
}
How do I fix this error ? I need to use the NavController so I can't remove it. I want to still be able to send data from the child component to the parent component
The error is pointing out that you should provide the NavController in the providers section somewhere. This section could be in several modules of your application but the general one is the app.module.ts file.
In there you have a providers section in which you can provide services or providers for the Angular dependency injection mechanism to work appropriately.
So my first thougth would be to add the navController in the providers section of your app.module.ts file. This ensures that Angular can resolve the dependency to your NavController. Putting it in the app.module.ts file makes it having the same instance fo your navcontroller all over your application.
#NgModule({
bootstrap: [ AppComponent ],
declarations: [
...
],
imports: [
...
],
exports: [
],
providers: [
...
NavController
]
})
However, since you are using ionic, I would check out a related topic on the following url.
Ionic 2 - Runtime Error No provider for NavController

share service data b/w two component in angular 2 using get set method

Here is my first component
requirement:display data in second component which is sent by first component.
current status: i set data into service get set method and get also the data data but unable to display same data which is set by first component template .
import { Component } from '#angular/core';
import { ConfigService } from './myservicedata';
import { Router } from '#angular/router';// add file for navigate from one page to another page
#Component({
selector: 'tab-one',
templateUrl: './tabone.component.html',
providers:[ConfigService]
})
export class taboneComponent {
constructor(public configservice:ConfigService,private router:Router) {}
formData(data:any){
this.configservice.set_service_data(data);
console.log("value of data which is set by me into service"+ data);
}
// for navigate from one url to another url
navigate(){
this.router.navigateByUrl('/tab_two');
}
}
Here is my second component
import { Component ,OnInit} from '#angular/core';
import { ConfigService } from './myservicedata';
import 'rxjs/Rx'; // add this file for use the map feature.
#Component({
selector: 'tab-two',
templateUrl: './tabtwo.component.html',
// providers:[ConfigService]
})
export class tabtwoComponent {
public getterSetter:any=[];
// public store_service_data:any=[];
constructor(private configservice:ConfigService) {}
ngOnInit(){
this.configservice.get_service_data()
}
showdata(){
console.log( this.configservice.get_service_data());
}
};
Here is my service
import {Injectable} from '#angular/core';
import {Http,Response} from "#angular/http";
import {Observable} from 'rxjs/Rx';
import 'rxjs/add/operator/map';
#Injectable()
export class ConfigService {
private _url:string="../mockData.json";
public serviceData:any=[];
get_service_data():any{
return this.serviceData;
// this.serviceData.map(
// (response:Response) => response.json()
// );
};
set_service_data(value:any):void{
this.serviceData=value;
};
constructor(private http:Http) {}
// Uses http.get() to load a single JSON file
getFriendsData():any {
return this.http.get(this._url).map(
(response:Response) => response.json()
);
}
};
Remove the providers array in your TaboneComponent and make sure ConfigService is in the providers array in your app.module.ts
Explanation:
In order to retrieve the information from the service what you want is to make sure that both your components reference the same instance on your ConfigService
Angular uses hierarchical dependency injection, which means whenever a dependency like your ConfigService is requested, Angular will traverse up the component tree to find a place where it has already been provided and pass that instance to the requester.
Because of this, you can easily create a singleton instance by providing a service in your app module as every component is a child of this.
When you provide the service like you have done in your TaboneComponent you are saying give me a new instance of this service even if one has already been provided somewhere else. Any component that is a child of tab-one will be able to get the data you have set in that service, but anything that is not a child will not.
You can read more about Angular's dependency injection here:
https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html