Trying to make a simple POST to my server, with some data and have it return some JSON data. (Currently, it runs through the call but I see nothing on my network tab that it actually tried to make it?)
My Service (api.service.ts)
import { Injectable } from '#angular/core';
import { Headers, Http, Response} from '#angular/http';
import {Observable} from 'rxjs/Rx';
// Import RxJs required methods
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
#Injectable()
export class ApiService {
private headers = new Headers({'Content-Type': 'application/json'});
private myUrl = 'https://www.mywebsite.com'; // URL to web api
private payLoad = {"thingy1":"Value1", "thingy2":"value2"};
constructor(private http:Http) { }
getData () {
this.http.post(this.myUrl, JSON.stringify(this.payLoad), this.headers)
.map((res:Response) => res.json())
.catch((error:any) => Observable.throw(error.json().error || 'Server error'));
}
}
Then I just have the getData method ran in a header component just to fire it.
(header.component.ts)
import { Component, OnInit } from '#angular/core';
//Service
import { ApiService } from '../services/api.service';
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss']
})
export class HeaderComponent implements OnInit {
constructor(private service:ApiService) {
this.service.getData(); //Fire getData from api.service
}
ngOnInit() { }
}
Observable are lazy, even if you initialize them, they will not be executed. At least one subscriber has to explicitly subscribe to that observable to listen to that stream. In short: They will only be executed when you subscribe to it.
this.service.getData().subscribe(
(data) => console.log(data)
);
Make sure you are subscribing your observable data. The RxJS observer has 2 parts,
next() - it push data which is done by angular itself.
subscribe - you need in your component to get data from observable.
Try something below
this.service.getData().subscribe(
(data) => {
//process your data here
}
);
Related
I wanted to know how I do to use an array in two angular components without the need to make two http.get requests.
My service.
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import { environment } from '../../../environments/environment';
#Injectable()
export class CategoryService {
constructor(
private http: Http
) {
}
getAll(): Promise<any> {
return this.http.get(`${environment.apiUrl}/categories`)
.toPromise()
.then(response => response.json());
}
}
First component
Here I am making the first requisition.
import { Component, OnInit } from '#angular/core';
import { PostService } from './../core/service/post.service';
import { CategoryService } from './../core/service/category.service';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
categories = [];
constructor(
private postService: PostService,
private categoryService: CategoryService
) { }
ngOnInit() {
this.getAllCategories();
}
getAllCategories() {
return this.categoryService.getAll()
.then(categories => {
this.categories = categories;
})
.catch(error => {
throw error;
});
}
}
Second component
Here I am making the second requisition.
import { Component, OnInit } from '#angular/core';
import { CategoryService } from './../core/service/category.service';
#Component({
selector: 'app-footer',
templateUrl: './footer.component.html',
styleUrls: ['./footer.component.css']
})
export class FooterComponent implements OnInit {
categories = [];
constructor(
private categoryService: CategoryService
) { }
ngOnInit() {
this.getAllCategories();
}
getAllCategories() {
return this.categoryService.getAll()
.then(categories => {
this.categories = categories;
})
.catch(error => {
throw error;
});
}
}
Could I create a public array in service? or it would be bad practice
A better approach would be to leverage “caching”.
1) To enhance your architecture, create one http-wrapper.service.ts, which wraps all http methods and have one property here to either read from cache or make an actual http get call. Donot cache post etc request.
2) Moreover create an http-cache.interceptor.ts, where in you can write logic to make an actual http get call or reutrn result from cache
These are the two best approaches which will not only solve your problem, but also enhance your architecture and handle all generic requests
Cheers (y)
I have an Angular 7 project with a PHP Laravel API backend that is returning json from get requests.
return Response::json($genres);
I'm using httpClient in a service which is being called from a component (see both below). When I console log the data it is a string, not a json object. Also I am unable to access any of the properties that it has because it is just a long string.
In most online examples people used map and then pipe but those are both deprecated now and apparently httpClient just returns JSON as default but that is not what appears to be happening in this case.
Could someone give me a solution to this? I need access to the data in the response as JSON.
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class ApiService {
constructor(private http:HttpClient) { }
getGenres(){
return this.http.get('http://www.localhost:7888/api/genres', this.genre);
}
}
import { Component, OnInit } from '#angular/core';
import { ApiService } from '../../services/api.service';
#Component({
selector: 'app-genres',
templateUrl: './genres.component.html',
styleUrls: ['./genres.component.css']
})
export class GenresComponent implements OnInit {
constructor(private apiService: ApiService) { }
ngOnInit() {
// make http request to http://www.localhost:7888/api/genres
this.apiService.getGenres().subscribe(data => {
console.log(data);
},
error => {
console.log(error);
});
}
}
I have a questions about passing data in Angular.
First, I don't have a structure as <parent><child [data]=parent.data></child></parent>
My structure is
<container>
<navbar>
<summary></summary>
<child-summary><child-summary>
</navbar>
<content></content>
</container>
So, in <summary /> I have a select that do send value to <child-summary /> and <content />.
OnSelect method is well fired with (change) inside <summary /> component.
So, I tried with #Input, #Output and #EventEmitter directives, but I don't see how retrieve the event as #Input of the component, unless to go on parent/child pattern. All examples I've founded has a relation between component.
EDIT : Example with BehaviorSubject not working (all connected service to API works well, only observable is fired at start but not when select has value changed)
shared service = company.service.ts (used to retrieve company data)
import { Injectable } from '#angular/core';
import { Headers, Http, Response } from '#angular/http';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class SrvCompany {
private accountsNumber = new BehaviorSubject<string[]>([]);
currentAccountsNumber = this.accountsNumber.asObservable();
changeMessage(accountsNumber: string[]) {
this.accountsNumber.next(accountsNumber);
}
private _companyUrl = 'api/tiers/';
constructor(private http: Http) { }
getSociete(): Promise<Response> {
let url = this._companyUrl;
return this.http.get(url).toPromise();
}
}
invoice.component.ts (the "child")
import { Component, OnInit, Input } from '#angular/core';
import { Headers, Http, Response } from '#angular/http';
import { SrvInvoice } from './invoice.service';
import { SrvCompany } from '../company/company.service';
#Component({
selector: 'invoice',
templateUrl: 'tsScripts/invoice/invoice.html',
providers: [SrvInvoice, SrvCompany]
})
export class InvoiceComponent implements OnInit {
invoice: any;
constructor(private srvInvoice: SrvInvoice, private srvCompany: SrvCompany)
{
}
ngOnInit(): void {
//this.getInvoice("F001");
// Invoice data is linked to accounts number from company.
this.srvCompany.currentAccountsNumber.subscribe(accountsNumber => {
console.log(accountsNumber);
if (accountsNumber.length > 0) {
this.srvInvoice.getInvoice(accountsNumber).then(data => this.invoice = data.json());
}
});
}
//getInvoice(id: any) {
// this.srvInvoice.getInvoice(id).then(data => this.invoice = data.json());
//}
}
company.component.ts (the trigerring "parent")
import { Component, Inject, OnInit, Input } from '#angular/core';
import { Headers, Http, Response } from '#angular/http';
import { SrvCompany } from './company.service';
#Component({
selector: 'company',
templateUrl: 'tsScripts/company/company.html',
providers: [SrvCompany]
})
export class CompanyComponent implements OnInit {
societes: any[];
soc: Response[]; // debug purpose
selectedSociete: any;
ville: any;
ref: any;
cp: any;
accountNumber: any[];
constructor(private srvSociete: SrvCompany)
{
}
ngOnInit(): void {
this.getSocietes();
}
getSocietes(): void {
this.srvSociete.getSociete()
.then(data => this.societes = data.json())
.then(data => this.selectItem(this.societes[0].Id));
}
selectItem(value: any) {
this.selectedSociete = this.societes.filter((item: any) => item.Id === value)[0];
this.cp = this.selectedSociete.CodePostal;
this.ville = this.selectedSociete.Ville;
this.ref = this.selectedSociete.Id;
this.accountNumber = this.selectedSociete.Accounts;
console.log(this.accountNumber);
this.srvSociete.changeMessage(this.accountNumber);
}
}
This is a case where you want to use a shared service, as your components are structured as siblings and grandchildren. Here's an example from a video I created a video about sharing data between components that solves this exact problem.
Start by creating a BehaviorSubject in the service
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class DataService {
private messageSource = new BehaviorSubject("default message");
currentMessage = this.messageSource.asObservable();
constructor() { }
changeMessage(message: string) {
this.messageSource.next(message)
}
}
Then inject this service into each component and subscribe to the observable.
import { Component, OnInit } from '#angular/core';
import { DataService } from "../data.service";
#Component({
selector: 'app-parent',
template: `
{{message}}
`,
styleUrls: ['./sibling.component.css']
})
export class ParentComponent implements OnInit {
message:string;
constructor(private data: DataService) { }
ngOnInit() {
this.data.currentMessage.subscribe(message => this.message = message)
}
}
You can change the value from either component and the value will be updated, even if you don't have the parent/child relationship.
import { Component, OnInit } from '#angular/core';
import { DataService } from "../data.service";
#Component({
selector: 'app-sibling',
template: `
{{message}}
<button (click)="newMessage()">New Message</button>
`,
styleUrls: ['./sibling.component.css']
})
export class SiblingComponent implements OnInit {
message:string;
constructor(private data: DataService) { }
ngOnInit() {
this.data.currentMessage.subscribe(message => this.message = message)
}
newMessage() {
this.data.changeMessage("Hello from Sibling")
}
}
if component are not related than you need use Service
https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service
There are two solutions for this.
This can be done through shared service by using observable's.
You can use ngrx/store for this. This is similar to Redux arch. You will be getting data from state.
Here is the simplest example of sharing data between two independent components, using event emitter and service
https://stackoverflow.com/a/44858648/8300620
When you mention non related components, I'm gonna assume that they don't have any parent component. If assumption isn't correct, feel free to read another of my answers where both cases are addressed.
So, as there's no common parent, we can use an injectable service. In this case, simply inject the service in the components and subscribe to its events.
(Just like the next image shows - taken from here - except that we'll inject the service in two Components)
The documentation explains it quite well how to Create and register an injectable service.
I'm having trouble implementing a service that loads the data (gyms array) once, then allows all other components to use it, without making other HTTP requests.
My application works fine if the user started at the title page and loaded all the data, but when I go to a specific detail page (.../gym/1) and reload the page, the object isn't in the service array yet. How can I make the component that tries to access the service array wait until the data is loaded? More specifically, how can I delay the call of gymService.getGym(1) in the GymComponent until the getAllGymsFromBackEnd() function is done populating the array?
I've read about resolvers but my tinkering led me nowhere.
Any help would be appreciated.
This is the code I was working on:
Service:
import {Injectable} from "#angular/core";
import {Gym} from "../objects/gym";
import {BaseService} from "./base.service";
import {Http, Response} from "#angular/http";
import {HttpConstants} from "../utility/http.constants";
#Injectable()
export class GymService extends BaseService {
private gyms: Gym[] = [];
constructor(protected http: Http, protected httpConstants: HttpConstants) {
super(http, httpConstants);
this.getAllGymsFromBackEnd();
}
getAllGymsFromBackEnd() {
return super.get(this.httpConstants.gymsUrl).subscribe(
(data: Response) => {
for (let gymObject of data['gyms']) {
this.gyms.push(<Gym>gymObject);
}
}
);
}
getGyms() {
return this.gyms;
}
getGym(id: number) {
return this.gyms.find(
gym => gym.id === id
)
}
}
Component:
import {Component, OnDestroy, AfterViewInit, OnInit} from "#angular/core";
import {ActivatedRoute} from "#angular/router";
import {Subscription} from "rxjs";
import {Gym} from "../../objects/gym";
import {GymService} from "../../services/gym.service";
declare var $:any;
#Component({
selector: 'app-gym',
templateUrl: './gym.component.html',
styleUrls: ['./gym.component.css']
})
export class GymComponent implements OnInit, OnDestroy, AfterViewInit {
private subscription: Subscription;
private gym: Gym;
constructor(private activatedRoute: ActivatedRoute,
private gymService: GymService
) {}
ngOnInit(): void {
this.subscription = this.activatedRoute.params.subscribe(
(param: any) => {
this.gym = this.gymService.getGym(parseInt(param['id']));
}
);
}
ngAfterViewInit(): void {
$( document ).ready(function() {
$('.carousel').carousel();
});
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
}
You can use Resolver as well. Check it here https://angular.io/docs/ts/latest/api/router/index/Resolve-interface.html or use Observable. So the private gym: Gym; will become private gym$:Observable<Gym>;, and in your template, use async pipe to get the data.
I tried everything and I cannot get an http request to go out to my node server on heroku. I can hit the route manually so its not the server. I will paste my service and my page.
**Class is subscription.service.ts
import {Http, Response} from '#angular/http'
import {Injectable} from '#angular/core'
import 'rxjs/add/operator/map';
#Injectable()
export class SubscriptionService {
http:Http;
constructor(http:Http){
this.http = http;
}
getEntries() {
return this.http.get('my url here *****').map(res => res.json());
}
}
**Class is dashboard.component.ts
import {Component, ViewEncapsulation} from '#angular/core';
import {SubscriptionService} from '../../_services/subscription.service';
import 'rxjs/Rx';
#Component({
selector: 'dashboard',
providers: [SubscriptionService],
encapsulation: ViewEncapsulation.None,
styles: [require('./dashboard.scss')],
template: require('./dashboard.html')
})
export class Dashboard {
getData: string;
constructor(private subscriptionService: SubscriptionService) {
}
ngOnInit() {
console.log("test!!");
this.subscriptionService.getEntries()
.subscribe(data => this.getData = JSON.stringify(data),
error => console.log("Error!"),
() => console.log("finished!")
);
}
}
My ngOnInit() is being called, I see the console print, but no request shows up in logs on heroku. Also no errors show up in console.
Make sure you have imported the HttpModule in root.
I don't see anything else which can cause this. For make sure http is working you can put a break point in SubscriptionService on getEntries method and follow where it leads you.
Update:- as pointed out by #peeskillet there is nothing wrong with your dependency. try to debug and update your question with more information.