Call JS function after HTTP request is completed and rendered in Angular - javascript

I would like to call a function from external JS file to handle elements which are in a component (content coming from an HTTP request). The script is loading fine but way before the HTTP response so I cannot use my function. Here is my code:
import { Component, OnInit } from '#angular/core';
import { HttpService } from '../http.service'
import * as $ from 'jquery';
declare const myFunc: any;
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
items: object;
constructor(private _http: HttpService) { }
ngOnInit(): void {
this._http.myList().subscribe(
data =>{
this.items= data;
console.log(this.items);
},
myFunc(),
)
}
}
Any idea how I can load my script after all the data of the component is loaded?
thanks!

If I understood your problem correctly, you should leverage ngAfterViewInit. This method is called once your view is initialized, and by this time data returned from api is used in html.
export class HomeComponent implements OnInit, AfterViewInit {
items: object;
constructor(private _http: HttpService) { }
ngOnInit(): void {
this._http.myList().subscribe(
data => {
this.items = data;
console.log(this.items);
}
)
}
ngAfterViewInit() {
myFunc();
}
}
Your current func call doesn't make sense as it will work as the error callback function of subscribe as pointed by #Robin.
ngAfterViewInit

Related

Angular Template not updating after http request

I am can't get my Angular template to update after i sent out a postrequest,
Here's the component:
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'example',
templateUrl: './example',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ExampleComponent implements AfterViewInit, OnInit {
public mailResult: String;
constructor(private apiService: ApiService) {
this.mailResult = 'notsent'; //updating in template
}
onSubmit() {
this.mailResult = 'pending'; // updating in template
this.apiService.testPostRequest().subscribe((res)=>{
console.log("res", res); // working fine loging res
this.mailResult = 'requestsucess'; // not update in template
});
}
}
and that's the template:
<div class="example-component">
<h1>stateTest {{this.mailResult}}</h1>
</div>
and the apiService
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { of } from 'rxjs';
#Injectable()
export class ApiService {
constructor(private http: HttpClient) {}
testPostRequest () {
return this.http.post('http://localhost:1337/email', {});
}
}
All I need is to update this.mailResult in my Template after the request was successful, all is working fine, but the value in the template just won't update after the request. Any Ideas where the Issue might be hidden?
It's not working because you have your component's change detection set to 'onPush' and mailResult is not decorated with #Input (which it obviously shouldn't).
So, to fix this, you first need to add a changeDetector to your class:
constructor(private apiService: ApiService, private changeDetector: ChangeDetectorRef) {
this.mailResult = 'notsent'; //updating in template
}
And then you use that changeDetector's markForCheck function to let angular know that something has changed and it needs to update the view:
this.apiService.testPostRequest().subscribe((res)=>{
console.log("res", res); // working fine loging res
this.mailResult = 'requestsucess'; // not update in template
this.changeDetector.markForCheck();
});

wait till the request completes in angular

In my angular application, I have one parent component i.e. Dashboard Component having 2 Sub Components i.e. Analytics Component & Stats Component. My dashboard.component.html looks like this
<div class="row">
<div class="col-lg-6"><app-analytics></app-analytics></div>
<div class="col-lg-6"><app-stats></app-stats></div>
</div>
I am also using a Global Component which is available to all the components works like a global storage. Now in the dashboard.component.ts. I am making a HTTP call to the server, getting the data and saving it into the Global component.
dashboard.component.ts
import { Component, OnInit } from '#angular/core';
import { Global } from 'app/shared/global';
import { Http } from '#angular/http';
#Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent implements OnInit {
constructor(private http : Http){
};
ngOnInit(){
this.http.get('/api/getUserPreferences').subscribe(response=>{
var data = response.json();
Global.userPreferences = data.preferences;
})
}
User preferences I am using in the sub components i.e. Analytics Component and Stats Component.
analytics.component.ts
import { Component, OnInit } from '#angular/core';
import { Global } from 'app/shared/global';
import { Http } from '#angular/http';
#Component({
selector: 'app-analytics',
templateUrl: './analytics.component.html',
styleUrls: ['./analytics.component.css']
})
export class AnalyticsComponent implements OnInit {
public analyticsUserPreferences : any;
constructor(private http : Http){
};
ngOnInit(){
this.analyticsUserPreferences = Global.userPreferences.analytics;
// Printing just for the question purpose
console.log(this.analyticsUserPreferences);
}
}
stats.component.ts
import { Component, OnInit } from '#angular/core';
import { Global } from 'app/shared/global';
import { Http } from '#angular/http';
#Component({
selector: 'app-stats',
templateUrl: './stats.component.html',
styleUrls: ['./stats.component.css']
})
export class StatsComponent implements OnInit {
public statsUserPreferences : any;
constructor(private http : Http){
};
ngOnInit(){
this.statsUserPreferences = Global.userPreferences.stats;
// Printing just for the question purpose
console.log(this.statsUserPreferences);
}
}
Now, in these sub components. I am getting undefined every time in the console. Is there any way that it should wait till the Global.userPreferences doesn't contain the values. Or is there other way to do the same. I just want that it should wait till the http request is completed and print whenever the values are store inside the Global.userPreferences.
You can use the async pipe and an *ngIf to wait for the http request to be completed before rendering the child components. Then use binding to pass the data down to the child component and receive it with an #Input().
dashboard.component.ts
public userPreferences$: Observable<any>;
ngOnInit(){
this.userPreferences$ = this.http.get('/api/getUserPreferences').subscribe();
})
dashboard.html
<app-analytics *ngIf="userPreferences$ | async as userPreferences" [userPreferences]="userPreferences"></app-analytics>

Angular - communication from child-component to parent

I don't get i, how to communicate between components and services.. :(
I have read and tried a lot about even if some examples somehow work, I do not understand why (?)
what I want to achieve:
I have one parent and two child-components:
dashboard
toolbar
graph
in the toolbar-component I have a searchfield, which gets it's result from a external source (works via service).. when the result arrives, I need to trigger the updateGraph()-Method in the graph-component
toolbar.component.ts
import { Component, OnInit, Output, EventEmitter } from '#angular/core';
import { FormControl } from '#angular/forms';
import { WebsocketsService } from '../../../services/websockets/websockets.service';
import { DataService } from '../../../services/data/data.service';
#Component({
selector: 'toolbar',
templateUrl: './toolbar.component.html',
styleUrls: ['./toolbar.component.scss'],
providers: [WebsocketsService, DataService]
})
export class ToolbarComponent implements OnInit {
#Output() newGraphData: EventEmitter<boolean> = new EventEmitter();
searchField: FormControl;
search: string;
private isNewGraph = false;
constructor(private _websocketsService: WebsocketsService, private _dataService: DataService) {
}
ngOnInit() {
this.searchField = new FormControl();
this.searchField.valueChanges
.subscribe(term => {
this.search = term;
});
}
private applySearch() {
const res = this._websocketsService.sendQuery(this.search);
this._dataService.setGraphData(res);
this.newGraphData.emit(true);
this.search = '';
this.searchField.reset();
}
}
graph-component.ts
import { Component, OnInit} from '#angular/core';
import { HttpService } from '../../../services/http/http.service';
import { DataService } from '../../../services/data/data.service';
#Component({
selector: 'graph',
templateUrl: './graph.component.html',
styleUrls: ['./graph.component.scss'],
providers: [HttpService, DataService]
})
export class GraphComponent implements OnInit, AfterViewInit {
constructor( private _httpService: HttpService, private _dataService: DataService ) {
}
ngOnInit() {
}
public renderResult() {
console.log( this._dataService.getGraphData() );
}
}
data.service.ts
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class DataService {
private graphData: Subject<string> = new Subject<string>();
public setGraphData(data) {
this.graphData.next( data );
}
public getGraphData() {
return this.graphData;
}
constructor() { }
}
I simply want ´renderResult()´to be executed after the searchresult has been written to ´graphData´. please help i am confused.
If I understand, you want communication between components and service.
A[component] (make a information) -----(notification)-----> B[service] ----(send)----> C[component] (consume the information)
It's correct? Let's go.
You need create a subscription of graphData(data.service.ts) in GraphComponent.
import { Subscription } from 'rxjs/Subscription';
export class GraphComponent implements OnInit, AfterViewInit {
constructor( private _httpService: HttpService, private _dataService: DataService ) {
}
private subscription: Subscription;
ngOnInit() {
this.subscription = this._dataService.getGraphData().asObservable().subscribe((data) => {
console.log(data);
});
}
}
Look here to help you.
http://jasonwatmore.com/post/2016/12/01/angular-2-communicating-between-components-with-observable-subject
Short answer, I think you need to subscribe to the getGraphData subject, something like this (NOT RECOMMENDED):
public renderResult() {
this._dataService.getGraphData().subscribe(d => {
console.log(d)
});
}
It is not recommended as per the lead of RxJS says: https://medium.com/#benlesh/on-the-subject-of-subjects-in-rxjs-2b08b7198b93
Better answer, create an observable in your service and subscribe to that instead.
data.service.ts
graphObservable = this.graphData.asObservable();
graph-component.ts
public renderResult() {
this._dataService.graphObservable().subscribe(d => {
console.log(d)
});
}

data not accessible outside of subscription function

I am passing data between components onload. When the component receives the information within the subscribe function it is able to use the data and do whatever with it, so a console.log works fine, so it is clearly receiving it, but immediately outside of the subscribe function the information is unaccessible and is undefined. So I can't run a console.log or do anything with the information. In the html, it says that it is undefined as well.
The component.
import { Observable } from 'rxjs/Rx';
import { User } from '../User';
import { AccountInfo } from './../AccountInfo';
import { LoginService } from './../login.service';
import { Component, OnInit, AfterViewInit, OnDestroy, ElementRef, ViewChild } from '#angular/core';
import {ActivatedRoute} from '#angular/router';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/operator/map';
#Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent implements OnInit, AfterViewInit {
public user: User;
public subscription: Subscription;
constructor(route: ActivatedRoute, private loginService: LoginService) {}
ngOnInit() {
this.loginService.getMessage().subscribe(data =>
{
this.user = data;
console.log(this.user.vendorname);
});
console.log(this.user.vendorname);
}
ngAfterViewInit() {
//Called after ngAfterContentInit when the component's view has been initialized. Applies to components only.
//Add 'implements AfterViewInit' to the class.
}
}
relevant section of html
<h1>Welcome {{user.vendorname}}</h1>
Yes.. that's how async functions work. The thing you pass into the subscribe function is another function.
data => {
this.user = data;
console.log(this.user.vendorname);
}
This will be called once the getMessage() has received answer from your server. Any statements after the subscribe will be called immediately, and that's why this.user.vendorname is still undefined when you try to log it there.
If you are receiving an error in your html you should use the safe navigation operator (?.):
<h1>Welcome {{user?.vendorname}}</h1>

(Angular2) JSON data (http.get()) is undefined, and data is not updated in the component

My http-data.service accepts json for output in the component template. Initially, the console shows that the first few calls are given undefined, and the following calls are already taking json, but also if you check the component, then the component shows that the method that outputs the data to the component is called only once and since the data has not yet arrived it writes undefined , But not updated after the arrival of json. Help please understand why? Thank you
My http-data.service:
import {Injectable} from '#angular/core';
import {Http} from '#angular/http';
import {Response} from '#angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class HttpService{
constructor(private http: Http) {}
getDataOrganizations(): Observable<any[]>{
return this.http.get('http://localhost:3010/data')
.map((resp:Response)=>{
let dataOrganizations = resp.json().organization;
return dataOrganizations;
});
}
getDataModules(): Observable<any[]> {
return this.http.get('http://localhost:3010/data')
.map((resp: Response)=> {
let dataModules = resp.json().modules;
return dataModules;
});
}
getDataPresets(): Observable<any[]> {
return this.http.get('http://localhost:3010/data')
.map((resp: Response)=> {
let dataPresets = resp.json().presets;
return dataPresets;
});
}
getDataModuleItems(): Observable<any[]> {
return this.http.get('http://localhost:3010/data')
.map((resp: Response)=> {
let dataModuleItems = resp.json().module_items;
return dataModuleItems;
});
}
}
My data-all.service
import { Injectable, EventEmitter } from '#angular/core';
import {Response} from '#angular/http';
import { ModuleModel } from './model-module';
import { ModuleItemsModel } from './model-module-items';
import data from '../data/data-all';
import { PriceService } from './price.service';
import { HttpService } from './http-data.service';
#Injectable()
export class ModuleDataService {
constructor(private priceService: PriceService, private httpService: HttpService){
this.dataMinMaxSum = {minSum: 0, maxSum: 0}
}
private currentPopupView: EventEmitter<any> = new EventEmitter<any>();
private dataModules: ModuleModel[] = this.getDataModules();
private dataMinMaxSum: {};
private dataCalculateVariationOrg: any[];
private dataChangeExecutor: any[];
subscribe(generatorOrNext?: any, error?: any, complete?: any) {
this.currentPopupView.subscribe(generatorOrNext, error, complete);
}
calculte(){
return this.priceService.getDataPrice();
}
getDataModules(){
this.httpService.getDataModules().subscribe(((modules)=>{this.dataModules = modules; console.log(this.dataModules);}));
console.log('dataModules');
console.log(this.dataModules);
return this.dataModules;
}
---------------------------------------------------------------------------
}
My left-block.component
import { Component, OnInit} from '#angular/core';
import { ModuleDataService } from '../../service/data-all.service';
import { ModuleModel } from '../../service/model-module';
#Component({
moduleId: module.id,
selector: 'modules-left-block',
templateUrl: './modules-left-block.html',
styleUrls: ['modules-left-block.css']
})
export class ModuleLeft implements OnInit{
modules: ModuleModel[];
constructor(private modulesAll: ModuleDataService){}
ngOnInit(){
this.modules = this.modulesAll.getDataModules();
console.log("view");
console.log(this.modulesAll.getDataModules());
}
onToggle(module: any){
this.modulesAll.toggleModules(module);
}
}
My left-block.component.html
<div class="modules-all">
<div class="modules-all-title">Все модули</div>
<div class="module-item" *ngFor="let module of modules" [ngClass]="{ 'active': module.completed }" (click)="onToggle(module)">{{module?.title}}</div>
</div>
In the component this.modulesAll.getDataModules () method is why it is executed only once without updating (write in console => undefined), if there are any thoughts, write, thanks.
This behaviour is due to the .subscribe() method does not wait for the data to arrive and I'm guessing you already know this. The problem you're facing is because, you have .subscribe to the getDataModules() service in the wron place. You shouldn't subscribe to a service in another service (at leat in this case). Move the subscribe method to the left-block.component and it should work.
getDataModules() {
this.httpService.getDataModules().subscribe(((modules) => {
this.dataModules = modules;
console.log(this.dataModules);
}));
console.log('dataModules');
console.log(this.dataModules);
return this.dataModules;
}
It should look somethig like this:
#Component({
moduleId: module.id,
selector: 'modules-left-block',
templateUrl: './modules-left-block.html',
styleUrls: ['modules-left-block.css']
})
export class ModuleLeft implements OnInit {
modules: ModuleModel[] = new ModuleModel();
constructor(private modulesAll: ModuleDataService, private httpService: HttpService) {}
ngOnInit() {
this.getDataModles();
//this.modules = this.modulesAll.getDataModules();
console.log("view");
//console.log(this.modulesAll.getDataModules());
}
onToggle(module: any) {
this.modulesAll.toggleModules(module);
}
getDataModules(): void {
this.httpService.getDataModules().subscribe(((modules) => {
this.modules = modules;
console.log(this.dataModules);
}));
}
}

Categories

Resources