Angular How to check HttpRequest object to set proper content-type for headers? - json

My team is working for some time on rewriting front-end of our application to Angular. Everything was going more or less smoothly until I encountered file importing views. Our default HttpHeaderEnricherInterceptor is setting default Content-Type as application\json. It works well in the whole application but while trying to import any file all I get is 415. However, if I remove .set('Content-Type', 'application/json') importing is working properly... but all other components are failing. I was trying to make some if statements based on HttpRequest params but it doesn't recognize has() method and some others that I've tried.
I know that final option to solve this is to set content-type on each request but it's something I would like, better avoid it as we decided to try finding global solution to the problem.
Here's my intercept() code:
import { TokenService } from './../token/token.service';
import {Injectable} from '#angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '#angular/common/http';
import {Observable} from 'rxjs/Observable';
#Injectable()
export class HttpHeaderEnricherInterceptor implements HttpInterceptor {
constructor(private tokenService: TokenService) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log(`${req.url}?${req.params.toString()}`);
const changedReq = req.clone(
{headers: req.headers
.set('Authorization', this.tokenService.getToken() || '')
.set('Content-Type', 'application/json')
});
return next.handle(changedReq);
}
}
Thanks for any tips!

Looks like there is already opened issue on Github: https://github.com/angular/angular/issues/19730
Workarounds are possible but ugly, i.e. global boolean flag switching before and after call, adding fake header as indicator i.e.:
if(headers.get('X-Set-Content-Type') != null){
headers.set('Content-Type', 'application/json')
}

Related

Pass JSON data from App Component to another component in Angular 6

I have two components,
1. App Component
2. Main Component
app.component.ts
ngOnInit () {
this.httpService.get('./assets/batch_json_data.json').subscribe(data => {
this.batchJson = data as string [];
}
I am able to get the JSON from a file into 'batchJson' and need to pass this to my main component for further operations.
There is no event or anything that triggers this.
I have not implemented anything yet, I am trying to read #Input, #Output etc but do not understand how it works and need to go through it some more.
I have just declared basics in the main.component.ts
import { Component, OnInit, ViewChild, Input } from '#angular/core';
import { AppComponent } from '../app.component';
export class MainComponent implements OnInit {
}
Please help me out, I am an absolute rookie in Angular and am unable to try anything because my concepts are not clear and I did browse Stack Overflow, the answers are not matching my requirements.
One solution could be to use a public BehaviorSubject in your app.component.ts.
public batchJson$ = new BehaviorSubject<JSON>(null);
ngOnInit () {
this.httpService.get('./assets/batch_json_data.json').subscribe(data => {
this.batchJson = data as string [];
this.batchJson$.next(JSON.parse(data));
}
Then in your main.component.ts
constructor(private _appComponent : AppComponent )
ngOnInit(){
this._appComponent.batchJson$.subscribe((data)=>{
if(data != null){
//set data to local variable here
}
})
}
Typically I store this kind of logic in a Service, using this in a component will definitely get you pointed in the right direction to learning this concept. Preferably your component should be responsible for interacting with the UI and rendering data, while your services handle retrieving and distributing data.
you can implement common service which does all related http operations and you can inject this service in any component u want and read the json.
Make sure you return the http.get and you subscribe to it where ever you call this method.
If you are not aware of services , you can read about creating and injecting services in angular
You can use rxjs subject to emit the data through out the app and fetch it anywhere by using subject.getValue() method.
First of all you should spare time on understanding the concept of any technology before you start working on it. Else you would be spending most of the time seeking help.
I had created demo here - https://stackblitz.com/edit/angular-lko7pa. I hope it will help you out.

'xdom is not defined' error within autopulous-xdom2jso

I have imported below packages in my service, which I use for SOAP-API processing.
"autopulous-xdom": "~0.0.12"
"autopulous-xdom2jso": "^0.0.12"
I am trying to use these with below lines at top of my service.ts
import 'autopulous-xdom/xdom.js';
import 'autopulous-xdom2jso/xdom2jso.js';
import convert = xdom2jso.convert;
import {Injectable} from '#angular/core';
#Injectable()
export class SoapService {
constructor() {}
}
I get no errors on compiling and building. But while running the application in browser, I get below error.
Has anyone worked with xdom2jso and xdom? Please help.
I got no responses and all I could do was fork and raise a PR myself into autopulous-xdom2jso.
See my fix here: https://github.com/autopulous/xdom2jso/pull/2

Cannot recieve DATA from RESTFUL API using IONIC 2 generated provider

So, i'm currently studying Ionic 2 to make hybrid applications. I'm following a course on Udemy but the course's content about HTTP requests to WEB API's is obsolete(it's from the ionic 2 Beta). This is a long question but some of you's who are more experienced on the Ionic 2 framework can just skip to step 8 to save some time. Thanks a lot guys!
I'm trying to retrieve data from this URL:
https: //viacep.com.br/ws/01001000/json/.
It has a space after https:// because stackoverflow won't allot me to post more than one link.
But I'm missing something to save this data on a variable I created.
What I did to this point is:
1) I generated the provider which I called ConnectionService using the CLI ionic generator.
ionic g provider ConnectionService
2) Created a method called getCEP() inside the ConnectionService Provider, which makes an HTTP GET Request
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import 'rxjs/add/operator/map';
/*
Generated class for the ConnectionService provider.
See https://angular.io/docs/ts/latest/guide/dependency-injection.html
for more info on providers and Angular 2 DI.
*/
#Injectable()
export class ConnectionService {
constructor(public http: Http) {
console.log('Hello ConnectionService Provider');
}
getCep(): Promise<Response>{
let response: any = this.http.get("https://viacep.com.br/ws/01001000/json/");
console.log("Response: " + response);
let responsePromise: any = response.toPromise();
console.log("ResponsePromise: " + responsePromise);
return responsePromise;
}
}
P.S.: Here you can see i'm loggin in two steps of the request: The first one is the response before I turn it into a Promise, so I can return it to the page. The second one is after i cast it to a Promise using the toPromise() method.
3)In my view I have a button which has the (click) directive calling the method buscarCEP()
<ion-header>
<ion-navbar>
<ion-title>Teste</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<button (click)="buscarCep()">Request CEP</button>
</ion-content>
4.1) My TypeScript file has imported the ConnectionService Provider and named it ConnectionService.
4.2) I declared the ConnectionService inside the #Component directive under the "providers:" label
4.3) I create an instance of Connection Provider that I call conServ on the constructor's declaration. Also I created a variable called CEP, to store the data that I pull from it.
import { Component } from '#angular/core';
import { NavController, NavParams } from 'ionic-angular';
import { ConnectionService } from '../../providers/connection-service';
/*
Generated class for the MenuTest page.
See http://ionicframework.com/docs/v2/components/#navigation for more info on
Ionic pages and navigation.
*/
#Component({
selector: 'page-menu-test',
templateUrl: 'menu-test.html',
providers: [ConnectionService]
})
export class MenuTestPage {
public CEP: any;
constructor(public navCtrl: NavController, public navParams: NavParams, public conServ: ConnectionService) {
}
6)Then I modify the method buscarCEP() so that it gets that conServe instance and calls the getCEP() method which makes an HTTP Request to the URL given above.
buscarCep(): void{
this.conServ.getCep().then(data =>{
console.log("DATA:" + data);
this.CEP = data;
}).catch(err => {
console.log("ERRO: " + err);
});
console.log("CEP: " + this.CEP);
}
PS.: As you can see, i'm logging three steps into the request: The data when the getCEP() method executes, a possible error called 'err' and by the end of it, the variable CEP that I created before and saved the data value to.
7)When I run the application and click on the button, I get the following screen with the console.logs:
Image of ionic page with snips of the Chrome console
8) As you can see, my logs are returning as follows:
8.1) The "Hello ConnectionService Provider" is from the console.log inside the provider's constructor, so the import of the provider is fine and it is being instantiated.
8.2) the "Response: [object Object]" is from the first console.log() inside the getCEP() method in the provider itself.
8.3) the "RespondePromise: [object Object]" is from the second console.log() inside the getCEP() method in the provider itself, after i casted the response to a Promise.
8.4)"CEP: undefined" comes from the console.log inside the buscarCEP() method, which is called after I click on the Request CEP Button
8.5)"DATA:Response with status: 200 OK for URL: https://viacep.com.br/ws/01001000/json/" comes from the console.log() inside the buscarCEP() method.
9) From this i'm taking that the getCEP() method is being able to connect to the URL, hence why the Response and ResponsePromise logs have an Object attached to them. Also the DATA log tells me that i recieved an OK Response from the server. My question is in regard to CEP: Undefined log. I can't seem to store that object in the variable I created.
I know that this is a long one but I wanted to lay all my cards on the board and explain everything as thoroughly as I could because i'm new to this framework.
Any help is appreciated, thank you for your time!
The Response object is stored in this.CEP. The issue is console.log(this.CEP) is called before the response from the HTTP request is returned within then.Promises are asynchronous.You can check the contents by doing console.log(this.CEP) within then.
So if you were to print the data in the html side, use safe navigation operator ?. e.g: {{CEP?.property}}.
A couple of issues with your code:
You should extract the json data from your response object. I suggest you do:
this.CEP = data.json();
If you want to print the contents of the object you can try console.log(JSON.stringify(data,null,2)).

angular 2 optimal way to map incoming api json into a array of class objects

I'm working with Angular 2. I'm trying to figure out the correct way to map a incoming json object into a array of class
./book.ts
export class book{
constructor(
public id: number,
public title: string,
public publication_date: string){}
}
./book.service.ts
import { Injectable } from '#angular/core';
import { RequestOptions, URLSearchParams, QueryEncoder, Http, Headers, Response } from '#angular/http';
import { Trip } from './trip';
import {Observable} from 'rxjs/Observable';
import 'rxjs/Rx';
#Injectable()
export class BookService {
constructor(private http: Http) { }
fetchbooks(): Observable<book[]>{
return this.http.get('some_url')
.map((res: Response) => <book[]>res.json().books);
}
}
but the
fetchbooks()
doesn't map the incoming json to the book class object.. so i cant do type check.
is there any other way to do this??
Indeed your thoughts on this matter are correct. Because the objects are created by JSON.parse it really makes no sense to use a class to describe their shape. The instance of tests will of course fail and it results in a misleading API.
However there is a simple solution. Typescript has a notion interfaces which are used to describe the shape of various API but have no run-time manifestation. This is a perfect use case for an interface and not a class. Please note that interfaces in typescript are not at all related to interfaces in languages such as Java.
I am aware that my advice is in conflict with the official style guide on this point. In general it makes some good recommendations but it is wrong on this one. Do not use classes to describe the shape of objects returned to remote services via JSON.
You actually don't need to test the types of the objects coming back if you know and trust the end point that is serving them. Your current approach which asserts the type of the response using a generic is a good approach. Just use interface instead of a class.
you can of course associate specific objects with specific interface types. You could correlate this based on server-side serialized fields so you have the option if you need to but you still wouldn't want to use a class for that on the client.

Dynamic path segment OR 404

I have an app that needs to check with a backend API before rendering 404. The routing flow works something like this:
Request comes in to /{INCOMING_PATH}, and the application attempts to fetch and render data from api.com/pages/{INCOMING_PATH}.
If the API returns 404, then the app should return 404. If not, the data is rendered.
I'm not sold on using for this use case. {INCOMING_PATH} will be dynamic, potentially with slashes and extensions in the path. Is this possible to implement in React Router (with proper SSR behavior too)? If so, how should I proceed?
(This question was originally posted on github by another user. They were requested to post it here as it is a support request. But it doesn't seem they did. I am now stuck on exactly the same issue.)
I've solved this with the React Nested Status module.
I'm using https://github.com/erikras/react-redux-universal-hot-example so this code is geared towards that. See React Nested Status for a more generic solution.
Edits to server.js:
at the top
import NestedStatus from 'react-nested-status';
at the bottom replace:
const status = getStatusFromRoutes(routerState.routes);
if (status) {
res.status(status);
}
res.send('<!doctype html>\n' +
ReactDOM.renderToString(<Html assets={webpackIsomorphicTools.assets()} component={component} store={store}/>));
with:
const repsonse = ReactDOM.renderToString(
<Html assets={webpackIsomorphicTools.assets()} component={component} store={store}/>
);
const status = getStatusFromRoutes(routerState.routes);
if (status) {
res.status(status);
}
const nestedStatus = NestedStatus.rewind();
if (nestedStatus !== 200) {
res.status(nestedStatus);
}
res.send('<!doctype html>\n' + repsonse);
Then in what ever container/component you need to serve a 404 :
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import connectData from 'helpers/connectData';
import { fetchApiData } from 'redux/modules/foo/fetchApiData';
import { NotFound } from 'containers';
#connectData(null, (getReduxState, dispatch, state, params) => {
return dispatch(fetchApiData(params.fooId));
})
#connect(
(reduxState) => ({
fooData: reduxState.foo.data,
})
)
export default class ProductType extends Component {
static propTypes = {
fooData: PropTypes.object,
}
render() {
let content;
// ... whatever your api sends back to indicate this is a 404
if (!this.props.fooData.exists) {
content = <NotFound/>;
} else {
content = (
<div className={styles.productType}>
Normal content...
</div>
);
}
return content;
}
}
Finally replace /src/containers/NotFound/NotFound.js
import React, { Component } from 'react';
import NestedStatus from 'react-nested-status';
export default class NotFound extends Component {
render() {
return (
<NestedStatus code={404}>
<div className="container">
<h1>Error 404! Page not found.</h1>
</div>
</NestedStatus>
);
}
}
I'm not sure what kind of state implementation you are using. But, if you are using redux, then I think the simplest way is to use redux-simple-router. With it, your Routes are synchronized within your state, so you can dispatch action creators to change the router path. I would try to update satate with action creators instead of pushing the state directly from a component. The truth point must be always the state, in your case I would act as follows:
The component that requires to fetch the data will be subscribed to the "dataReducer" which is the isolated state part that this component should care about. Maybe the initial state of dataReducer is an empty array. Then, in componentWillMount you dispatch an action like: dispatch(fetchDataFromApi)) If the response code is 404, then in the action fetchDataFromApi you can dispatch another action, that is just an object like this one:
{type:SET_NOT_FOUND_ERROR}
That action will be handled by the reducer dataReducer, and will return a new state with an object (consider Immutability) that will have a property error, which will be a string with the reason, or whatever you want.
Then, in componentWillReceiveProps method, you, can check if the nextProps have or not have an error. If Error, you can render your error component, or even dispatch an action to go to the error page handled by react-router.
If no error, then you can dispatch an action (thanks to redux-simple-router) to go to the path y