Angular 2 service data can't assign to variable in constructor - javascript

In my Angular 2 application's constructor i need to assign service function returning data to a public variable and show in html view. Console log works well but html data not showing. So i'm assuming data is not assigning to the variable. Following is my code snippet.
export class ListComponent implements OnInit {
public listname ="Laravel list";
constructor(private laraveldataservice: LaravelDataServiceService) { }
public dataList = this.laraveldataservice.getUsers();
ngOnInit() {
this.laraveldataservice.getUsers();
}
}
Basically ng oninit data is loading in console log. But dataList value is not assigning.
Service code,
getUsers(){
const url = 'http://localhost/laravel_app/public/api/users';
this.http.get(url).subscribe(
res => {
const data = res.json();
console.log(data);
return data;
}
);
}

Your console.log works because it fires in your Rx subscription. Your dataList never gets populated with anything due to the async nature of the call. What I do in my code is to convert it to a promise and then await it.
Example:
async ngOninit() {
this.dataList = await this._httpClient
.get<TData>(url)
.toPromise();
}

Place it inside ngOnit and declare variable outside
public dataList : any;
ngOnInit() {
this.dataList = this.laraveldataservice.getUsers();
}

Try this snippet
Service
private defaultDataSub = new Subject<any>();
onDefalultDataLoad = this.defaultDataSub.asObservable();
getUsers(){
const url = 'http://localhost/laravel_app/public/api/users';
this.http.get(url).subscribe(
res => {
const data = res.json();
this.defaultDataSub.next(data);
}
);
}
Component
import {Subscription} from "rxjs";
export class ListComponent implements OnInit, OnDestroy{
public listname ="Laravel list";
constructor(private laraveldataservice: LaravelDataServiceService) { }
public dataList;
public subscription:Subscription;
ngOnInit() {
this.subscription = this.laraveldataservice.onDefalultDataLoad.subscribe(data => {
this.dataList = data;
});
}
ngOnDestroy(){
this.subscription.unsubscribe();
}
}
You are setting dataList property before data coming from server. May be this is the reason for your problem

What you need to do is :
getUsers(){
const url = 'http://localhost/laravel_app/public/api/users';
return this.http.get(url).pipe(map(res => res.json()))
}
And in your component.ts file :
export class ListComponent implements OnInit {
public listname ="Laravel list";
constructor(private laraveldataservice: LaravelDataServiceService) { }
public dataList ;
ngOnInit() {
this.laraveldataservice.getUsers().subscribe(res => {
this.dataList = res
}
}

Related

Pass value to subject service Angular js

i try passa values to child component using subject service, I am based on Documentatio, but somethink is wrong and when a try print data dont show nothing.
i have the navComponent which request api base on select input. When changing select update request and send new data to child page.
Service:
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs';
#Injectable()
export class SearchService {
// Observable string sources
private data = new Subject<any>();
// Observable string streams
value$ = this.data.asObservable();
// Service message commands
setValeu(value: any) {
this.data.next(value);
}
}
NavComponent:
async consultaIndicadores() {
try {
this.loading = true
let con = await this.rq.postRequest(... request ...).toPromise()
this.loading = false
if (con.status == 200) {
this.dados = con.data;
this.search.setValeu(this.dados)
}
}
} catch (error) {
this.loading = false
console.log(error)
}
}
childComponent
constructor(private trans: SearchService) {
this.subscription = trans.value$.subscribe(val => {
console.log(val);
})
}
in each component i seted provider, pass service to constructor etc... I dont know what its wrong. please help me

NestJS Http Module

I took this example from the nestJS docs (https://docs.nestjs.com/techniques/http-module#http-module), which is a minimal example of my problem:
#Injectable()
export class CatsService {
constructor(private httpService: HttpService) {}
findAll(): Observable<AxiosResponse<Cat[]>> {
return this.httpService.get('http://localhost:3000/cats');
}
}
How can I extract the actual Array of Cats from the Observable<AxiosResponse<Cat[]>>?
I tried the following but it gives me a subscriper object which I also don't know how to unwrap to get the actual data.
const cats = await this.catsService.findAll().subscribe((val) => val);
Using .toPromise() is deprecated now check this : https://rxjs.dev/deprecations/to-promise
Listing below is an example using the new approach
UPDATE Oct 2021
import { lastValueFrom } from 'rxjs';
.
.
.
const observable = await this.httpService.get('url').pipe(map((res) => res.data));
// you can use the data object now !!
const data = await lastValueFrom(observable);
#Injectable()
export class CatsService {
constructor(private httpService: HttpService) {}
findAll(): Promise<Cat[]> {
return this.httpService.get('http://localhost:3000/cats').toPromise();
}
}
const cats = await this.catsService.findAll().data
worked for me.
Try this:
findAll(): Observable<Cat[]> {
return this.httpService.get('http://localhost:3000/cats')
.pipe(map(response => response.data);
}

Why the GET request from Angular in the NodeJS sometimes come as null although there are data

I am having sometimes a little problem with the GET request in Angular.
I am using ReplaySubject and return the Observable but sometimes in the reload of the app the get request it is working but it is getting null data or the message No content although there are the data.
In 3-4 tries then it shows the data.
The request get works but sometimes is null and sometimes give me the data.
Can someone help me on this ?
And if it possible to give any idea to use the ReplaySubject or something like, because i need to reload page everytime to fetch new data.
This is my Frontend part.
export class ModelDataService {
public baseUrl = environment.backend;
private data = new ReplaySubject<any>(1);
public userID = this.authService.userID;
constructor(private http: HttpClient, private authService: AuthService) {
}
public getJSON() {
return this.http.get(`${this.baseUrl}/data/${this.userID}`).subscribe(res => this.data.next(res));
}
public dataModel(): Observable<any> {
return this.data.asObservable();
}
public setData(data: Model) {
const api = `${this.baseUrl}/data`;
const user_id = this.authService.userID;
this.http.post(api, data, {
headers: {user_id}
}).subscribe(res => this.data.next(res));
}
public updateDate(id: string, dataModel: Model) {
const api = `${this.baseUrl}/data/${id}`;
return this.http.put(api, dataModel).subscribe(res => res);
}
}
This is the component which I get data
ngOnInit() {
this.authService.getUserProfile(this.userID).subscribe((res) => {
this.currentUser = res.msg;
});
this.modelDataService.getJSON();
this.model$ = this.modelDataService.dataModel();
this.model$.subscribe((test) => {
this.model = test;
});
this.model$.subscribe((test) => {
this.model = test;
});
}
This is the backend part.
const ModelData = require("../models/data");
async show(req, res) {
let modelData;
await ModelData.findOne({user: req.params.id}, (error, user) => {
modelData = user;
});
if (!modelData) {
res.status(204).json({error: "No Data"});
return;
}
return res.status(200).send(modelData);
},
routes.get("/data/:id", ModelDataController.show);
routes.post("/data", ModelDataController.store);
routes.put("/data/:id", ModelDataController.update);
If you pass in a callback function, Mongoose will execute the query asynchronously and pass the results to the callback. Sometimes you don't get the data because the query is not finished excuting. To fix this, you can change your code to:
let modelData = await ModelData.findOne({user: req.params.id});
if (!modelData)...

Angular - get synchronously from Promise

I want to print history of products. I have an id of product in ActivatedRoute.params. In ngOnInit method I have to get all history of product and assign to variable. Then I want to map product to productHistory, because I want to have last version with history toghether. But the problem is with getting history. Method to getting history return Promise and I cannot get length of productsHistory when I use this property and I get undefined. How can I get this property after loading from service?
I want to execute method after execution getHistory().
My code:
ProductService.ts:
import { Injectable } from '#angular/core';
import { Headers, Http } from '#angular/http';
import 'rxjs/add/operator/toPromise';
// rest imports
#Injectable()
export class ProductService {
// URL to web api
private projectsUrl = 'http://localhost:8080/products';
private headers = new Headers({'Content-Type': 'application/json'});
constructor(private http: Http) {}
getHistory(id: number): Promise<ProductHistory[]> {
const url = `${this.projectsUrl}/projectId/${id}`;
return this.http.get(url)
.toPromise()
.then(response => response.json() as ProductHistory[])
.catch(this.handleError);
}
handleError() {
//...
// implementation is irrelevant
}
}
ProductHistoryComponent.ts:
import { Component, Input, OnInit } from '#angular/core';
import { ActivatedRoute, Params } from '#angular/router';
import { Location } from '#angular/common';
import { ProductService } from './product.service';
import { ProductHistory } from './product-history';
import { Product } from './model/product';
import 'rxjs/add/operator/switchMap';
#Component({
selector: 'product-history',
templateUrl: './product-history.component.html',
styleUrls: [ './product-history.component.css' ]
})
export class ProductHistoryComponent implements OnInit {
auditProducts: ProductHistory[] = new Array<ProductHistory[]>();
selectedProduct: ProductHistory;
constructor(
private route: ActivatedRoute,
private location: Location,
private productService: ProductService
) {}
ngOnInit(): void {
let id: number = this.route.snapshot.params['id'];
this.productService.getHistory(id)
.then(history => this.historyProducts = history);
this.productService.getProduct(id)
.then(product => {
let lastVersion: ProductHistory = this.createLastVersion(product);
this.auditProducts.push(lastVersion);
});
}
onSelect(ProductHistory: ProductHistory): void {
this.selectedProduct = ProductHistory;
this.compare(this.selectedProduct);
}
goBack(): void {
this.location.back();
}
compare(history: ProductHistory): void {
let previous: ProductHistory;
if (history.changeNumber != null && history.changeNumber > 1) {
previous = this.historyProducts[history.changeNumber - 2];
if (typeof previous != 'undefined') {
this.setPreviousDiffsFalse(previous);
if (previous.name !== history.name) {
history.nameDiff = true;
}
if (previous.price !== history.price) {
history.priceDiff = true;
}
}
}
}
createLastVersion(product: Product): ProductHistory {
let lastVersionProduct: ProductHistory = new ProductHistory();
lastVersionProduct.id = this.historyProducts.length + 1;
lastVersionProduct.name = product.name;
lastVersionProduct.price = product.price;
lastVersionProduct.changeNumber = this.historyProducts[this.historyProducts.length - 1].changeNumber + 1;
return lastVersionProduct;
}
setPreviousDiffsFalse(previous: ProductHistory): void {
previous.nameDiff = false;
previous.priceDiff = false;
}
}
You can't run it synchronously, you have to wait for each promise to return a result before you can do something with that result. The normal way to do this is to nest code inside then blocks when using promises. Alternatively you can also use async/await with the latest version of typescript and you only have to change your component code as you are already returning the Promise type from your service. This makes code easier to read (IMO) although the emitted javascript code will still use function/callback nesting (unless you are targeting es7 I believe, maybe someone will correct or confirm this).
// note the use of async and await which gives the appearance of synchronous execution
async ngOnInit() {
let id: number = this.route.snapshot.params['id'];
const history = await this.productService.getHistory(id);
this.historyProducts = history;
const product = await this.productService.getProduct(id);
let lastVersion: ProductHistory = this.createLastVersion(product);
this.auditProducts.push(lastVersion);
}
I would suggest using observables instead of promises ... but to answer your question, you just need to perform the second request after the first is received. Something like this:
ngOnInit(): void {
let id: number = this.route.snapshot.params['id'];
this.productService.getHistory(id)
.then(history => {
this.historyProducts = history);
this.productService.getProduct(id)
.then(product => {
let lastVersion: ProductHistory = this.createLastVersion(product);
this.auditProducts.push(lastVersion);
});
}
}
I just moved the second request within the then of the first request. NOTE: I did not syntax check this.

Array undefined on map and subscribe Ionic 2

I creted a service that I want to get data from a JSON file and assign to an array (countries) to use at entire application (a lots of Pages), but when I call getCountries method, the countries is undefined, what is wrong at my approach?
import { Http } from '#angular/http';
import { Injectable } from '#angular/core';
#Injectable()
export class CountryService {
private countries: any;
private isLoaded: boolean;
private url: string = 'http://localhost:8100/assets/data/countriesV2.json';
constructor(private http: Http) {
if (!this.isLoaded) {
this.http.get(this.url)
.map(res => res.json())
.subscribe(result => {
this.countries = result;
});
this.isLoaded = true;
}
}
public getCountries() {
console.log(this.countries);
return this.countries();
}
}
Maybe changing return this.countries(); to return this.countries; may help
Also, check that your result is not empty :
.subscribe(result => {
this.countries = result;
console.log(result)
});
You should always map the data in the service, and subscribe in your component. The reason why this.countries is undefined because it IS undefined, even though you are trying to do the request in the constructor, it's not going to work. Change your service to this:
#Injectable()
export class CountryService {
private url: string = 'http://localhost:8100/assets/data/countriesV2.json';
constructor(private http: Http) { }
public getCountries() {
this.http.get(this.url)
.map(res => res.json())
}
}
And then in your components you call getCountries and subscribe to the request.
countries: any[] = [];
constructor(private countryService: CountryService) { }
ngOnInit() {
this.countryService.getCountries()
.subscribe(data => {
this.countries = data;
// here countries is not undefined, so call your "randomCountry" method here!
this.getRandomCountry();
});
}
Since this is an async operation, I suggest you use the safe navigation operator in that view, that does not try to show the property of country in case country is null. More info here. So the usage would be:
<div *ngFor="let country of countries">
{{country?.yourProperty}} // replace "yourProperty" with your actual property
</div>
Some more detailed explanations about HTTP here from the official docs
Hope this helps! :)

Categories

Resources