observable undefined in component - Angular - javascript

I have a service that connects with api
export class ConsolidadoApi {
constructor(private http: HttpClient) { }
getInvestiments(search?: any): Observable<any> {
return this.http.get<any>(`${environment.basePosicaoConsolidada}`);
}
}
Response this api:
https://demo5095413.mockable.io/consolidado
This one is responsible for the logic before reaching the component
#Injectable({
providedIn: 'root'
})
export class CoreService {
public test;
constructor(private api: ConsolidadoApi, private state: StateService) { }
public createMenu() {
this.api.getInvestiments()
.subscribe(response => {
console.log(response.carteiras[0])
this.products = response.carteiras[0]
return this.products;
})
}
In my component
export class MenuComponent implements OnInit {
constructor( private coreService : CoreService ) {}
ngOnInit(): void {
console.log(this.coreService.createMenu())
}
}
But when createMenu is called in menu.component.ts it comes undefined.

The raw response is an object. forEach works only on an array. If you are aiming for forEach in 'categorias', you should try
this.test.categorias.forEach()

When you return Observable<any>, that means the argument of the lambda you create when you do subscribe (which you named response) is type any. This doesn't necessary have the function forEach defined (unless the API returns an object with that prototype). That's generally why using any is not good practice; you can't have any expectations on what the object can contain. In fact, it's possible that it's not on object (it could be an array since any is not exclusively an object). If you do want to use forEach, you will want to make sure that response is type array. You can inspect the object's type before using it (e.g. using typeof) and make a judgement on what to call or even just check if the function you're trying to use is defined first, e.g. if (response.forEach !== undefined). You don't actually need to compare to undefined though, so if (response.forEach) suffices. In the examples, I used response, but you can use this.test since they are the same object after the first line in the lambda.

Based on the link you shared, the response is an object. You can log it to the console to confirm.
You can only call for each on an array, so for example, based on the response api, you can call forEach on the property ‘categorias’ and on that array’s children property ‘produtus’
Edit: this answer was based on the op original api and question
https://demo5095413.mockable.io/carteira-investimentos
public createMenu() {
return this.api.getInvestiments()
}
ngOnit() {
this.coreService.createMenu().subscribe(x => console.log(x.categorias))};
{
"codigo":1,
"categorias":[
{
"nome":"Referenciado",
"valorTotal":23000.0,
"codigo":"2",
"produtos":[
{
"nome":"CDB Fácil Bradesco",
"valor":2000.0,
"codigo":1,
"quantidade":0.0,
"porcentagem":0.5500,
"aplicacaoAdicional":500.0,
"codigoInvest":1,
"salaInvestimento":"CDB",
"permiteAplicar":true,
"permiteResgatar":true,
"movimentacaoAutomatica":false,
"ordemApresentacao":37,
"horarioAbertura":"08:30",
"horarioFechamento":"23:59",
"codigoGrupo":0,
"codigoMF":"001

Related

Angular TypeScript: waiting for a void method to run before another method

I have a method in an Angular component that pulls data via HttpClient subscription, and assigns it to an attributes this.allData, then instantiates an empty dictionary of parameters based on this, to pass to a second function:
export class TestComponent implements OnInit {
allData: object[] = []
activeData: object = {}
constructor(private http: HttpClient) { }
ngOnInit(): void {
this.getData()
this.makeRequestBasedOnData()
}
getData() {
this.http.get(this.url).subscribe(res => {
for (let datum of res["data"]) {
this.allData.push({
"key": Object.keys(datum)[0],
"values": Object.values(datum)[0]
})
this.activeData[Object.keys(datum)[0]] = ""
}
})
}
makeRequestBasedOnData() {
let testParams = this.activeData
console.log(testParam)
}
}
I need these steps to happen sequentially. At the moment, logging the testParams in makeRequestBasedOnData() simply shows an empty object {}. When I try to return arbitrarily in the first method, I get a TypeScript error that you cannot assign a promise to type void.
How do I enforce synchronicity here even though neither method actually returns anything?
You can return Observable from getData method and proceed with any other methods within subscribe:
ngOnInit(): void {
this.getData().subscribe(() => this.makeRequestBasedOnData());
}
getData() {
return this.http.get(this.url).pipe(
tap(res => {
// update allData
})
);
}
where:
pipe method allows us to provide any kind of transformation with the data returned from http.get(...) call.
tap is a rxjs operator for side-effects, meaning that we can do everything we want with the response without modifying Observable flow.

JavaScript to TypeScript type/interface mapping

I'm using this library in a TypeScript project.
And this is how my class looks like:
import OnvifManager from 'onvif-nvt'
Class OnvifApi {
// device: any = undefined
device = {} as OnvifDevice
constructor (...params) {
// definition
}
connect (): Promise<any> {
OnvifManager.connect(...params).then((response: OnvifDevice) => {
this.device = response
resolve(response)
}
}
coreService(): Promise<Type[]> {
this.device.add('core')
}
}
And this this interface I created for the response from onvif-nvt
interface OnvifDevice {
address: string
// type
// type
// type
add(name: string): void
}
This class is always giving an error in coreService saying that
this.device.add is not a function.
EDIT: This is the return of the connect method, which returns the whole Camera object.
Things are working when device is defined to any.
How can I do this interface mapping from JavaScript to TypeScript?
The reason is, in the interface, OnvifDevice, add is a mandatory parameter, but the device object is initialised with an empty object.
The dynamic mapping at this.device = response is not happening.
Also please check if the response has add method or not while you debug.
These are the two possible symptoms I see.

How to pass optional arguments to callback functions in typescript

I have a callback function which returns some data to the component.
export class AppComponent {
constructor(
private service: AppService
) {
this.processSomething(true);
this.processSomething(false);
}
private processSomething(isZoom: boolean = false) {
this.service.handleAsyncResponses(
this,
this.processDataReceived
);
}
private processDataReceived(
attributeValueList: any,
isZoom?: boolean
) {
console.log("isZoom:", isZoom);
}
}
I need to send some value isZoom parameter from the component and access the same in console.log("isZoom:", isZoom). Now console.log is loggin undefined.
A working sample is here: https://stackblitz.com/edit/angular-service-oqkfmf?file=app/app.component.ts
I think you're getting a little lost.
I took the freedom to clean your stackblitz from non-used code and show you how to use callbacks : you can check it there.
Let's start with the component :
constructor(
private service: AppService
) {
this.processSomething(true);
this.processSomething(false);
}
private processSomething(isZoom: boolean = false) {
this.service.handleAsyncResponses(isZoom, this.processDataReceived);
}
private processDataReceived(isZoom: boolean) {
console.log("isZoom:", isZoom);
}
You don't need to define your parameters as optional, since you give your isZoom value a default value, hence making it always defined.
As you can see, you don't need to pass your full object as an argument : the function can be called without it.
In your service, all you have left is
public handleAsyncResponses(zoom: boolean, callback: Function) {
callback(zoom);
}
Simply call the function as you would in any other context. simply rename this.processDataReceived(zoom) with the name of the parameter (here it being callback).
This is how callbacks are handled.
In your case, you need to wrap the function call in local closure:
private processSomething(isZoom: boolean = false) {
this.service.handleAsyncResponses(
this, (attributeValueList: any) => {
this.processDataReceived(attributeValueList, isZoom);
}
);
}
changed example

Angular HttpClient get, wrong this object

in my Angular App i make a simple call to a node.js server. the HttpClient "get"
function returns the right answer. This answer I want to store in a variable of my component "interfaces". But in the "subscribe" function of the get request my "this" pointer doesn't point to my component. Instead it tells me that it is of type "SafeSubscriber". Any call to my member "interfaces" lead to the following error:
TypeError: Cannot read property 'interfaces' of undefined
export class SettingsComponent implements OnInit {
public interfaces : string[];
constructor(private http: HttpClient) {
this.interfaces = [];
this.interfaces.push("huhu");
}
ngOnInit() : void {
this.http.get('http://localhost:3000/settings/interfaces').subscribe((data) => {
// Read the result field from the JSON response.
console.log(data);
this.interfaces.push("xxx");
Object.keys(data).forEach(function(k) {
console.log(k);
this.interfaces.push("xxx");
});
}),
err => {
console.log("error " + err);
};
}
}
As you can see I also tried to enter some values manually into the array just to make sure, that not the server response is causing the problem.
Any help is appreciated.
I used this code as a blueprint which is from:
https://angular.io/guide/http
#Component(...)
export class MyComponent implements OnInit {
results: string[];
// Inject HttpClient into your component or service.
constructor(private http: HttpClient) {}
ngOnInit(): void {
// Make the HTTP request:
this.http.get('/api/items').subscribe(data => {
// Read the result field from the JSON response.
this.results = data['results'];
});
}
}
You're losing reference to the correct this in this statement:
Object.keys(data).forEach(function(k) {..})
Inside the function block code this refers to the calling context , which is the subscribe method itself, that's why interfaces is undefined, since it's not a property of the subscribe method.
You can change the function for a lambda en it should be fine:
Object.keys(data).forEach((k) => {..})

Set global variable of class from inside a promise Angular 2

I am facing a weird issue in assigning response to a class's global variable from inside a observable. So my program logic is as follows:
Get latest playlists ID's from elastic search (i use elastic search from a type definition file). This returns me a PromiseLike to which i hook a then operator.
Inside the promise resolution, i make another http get call (i.e an observable)
In Observable subscription, i assign my global array with the response from the server.
Code is working correctly, I am getting responses as they should be but i cant assign the variable to the global one.
Here is my code:
import {Component, OnInit} from '#angular/core';
import {PlaylistService} from '../api/services'
#Component({
selector: 'app-playlists',
templateUrl: './playlists.component.html',
styleUrls: ['./playlists.component.css']
})
export class PlaylistsComponent implements OnInit {
public playlists: any[] = [];
constructor(private playlistService: PlaylistService) {
}
ngOnInit() {
let that = this;
this.playlistService.listIds().then((val) => { // <-- promise resolution
return this.playlistService.getByIds(val).toPromise(); // <-- http get call which i then convert to promise for simplicity
}).then((res) => { // <-- resolution of the http get call
console.log(this.playlists); <-- in this log, i get my desired results
// here is my problem, this assignment doesn't happens
this.playlists = res.data;
});
}
}
The listIds function is as follows:
listIds() {
return this.api.listing('playlist').then((body) => {
let hits = body.hits.hits;
return _.keys(_.groupBy(hits, '_id'));
});
}
and here is my api.listing function (elastic search client)
listing(type: string) {
let es = this.prepareES();
return es.search({
index: 'test',
_source: ["_id"],
type: type
});
}
The return type of es.search is
search(params: SearchParams): PromiseLike>;
Any ideas why i am not being able to assign value to global variable?
It looks like the promise returned by this.playlistservice.listIds() doesn't run inside Angulars zone. This is why Angular2 doesn't run change detection and doesn't recognize the change.
You can invoke change detection explicitly after the change:
constructor(private playlistService: PlaylistService, private cdRef:ChangeDetectorRef) {
...
ngOnInit() {
let that = this;
this.playlistService.listIds().then((val) => { // <-- promise resolution
return this.playlistService.getByIds(val).toPromise(); // <-- http get call which i then convert to promise for simplicity
}).then((res) => { // <-- resolution of the http get call
console.log(this.playlists); <-- in this log, i get my desired results
// here is my problem, this assignment doesn't happens
this.playlists = res.data;
this.cdRef.detectChanges();
});
}
Can you try passing
this.playlistService.listIds()
call inside your
return this.playlistService.getByIds(val)
replace val with first service call and see if your view gets updated. Just for testing purpose like
return this.playlistService.getByIds(this.playlistService.listIds())
.then((results)=>{/*rest of logic here*/});

Categories