Wait for Http response in angular 5 - javascript

I'm working on Angular-calendar.
I want to wait for HTTP response before component loads in Angular-5.
Calendar component.ts :-
I want to show data from service in this.totallist,
events: CalendarEvent[] = this.totallist; // I have show data here which is fetched from database.
ngOnInit() {
this.eventlist.getEvents().subscribe(
data => {
console.log(data);
let MyObj = JSON.parse(data);
let event_data_1 = [];
let outage_list = MyObj.outageList;
console.log(outage_list);
let startdate = outage_list[0].actualStartDateTime;
let enddate = outage_list[0].actualStartEndTime;
// console.log(startdate);
let converted_startdate = this.cleanDate('/Date('+startdate+')/');
let converted_enddate = this.cleanDate('/Date('+enddate+')/');
console.log(converted_startdate);
console.log(converted_enddate);
for(let i=0;i<1;i++)
{
let sub_object = {
start: new Date(),
end: new Date(),
title: 'A 3 day event',
color: colors.red,
actions: this.actions
}
event_data_1.push(sub_object);
}
this.totallist = event_data_1;
console.log(this.totallist);
return this.totallist;
}
);
// this.column_data = column_data;
}
service.ts :-
import { Http, Response } from '#angular/http';
import { Injectable } from '#angular/core';
import { Resolve, ActivatedRouteSnapshot } from '#angular/router';
import { Observable } from 'rxjs/Rx';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { HttpClient, HttpHeaders } from '#angular/common/http';
#Injectable()
export class ApiNewsResolver {
getEvents(): Observable<any>{
return this.http.get('http://localhost:8080/MacromWeb/ws/Calendar').map(data => data['_body']);
}
constructor(private http: Http) {
}
}
Now I want to load API service first and then load component. How can I achieve this task in Angular-5 ?
If any need I can show my whole component code.

Just store your data inside the subscribe:
events: CalendarEvent[]; // I have show data here which is fetched from database.
ngOnInit() {
this.eventlist.getEvents().subscribe(
data => {
console.log(data);
let MyObj = JSON.parse(data);
let event_data_1 = [];
let outage_list = MyObj.outageList;
console.log(outage_list);
let startdate = outage_list[0].actualStartDateTime;
let enddate = outage_list[0].actualStartEndTime;
// console.log(startdate);
let converted_startdate = this.cleanDate('/Date('+startdate+')/');
let converted_enddate = this.cleanDate('/Date('+enddate+')/');
console.log(converted_startdate);
console.log(converted_enddate);
for(let i=0;i<1;i++)
{
let sub_object = {
start: new Date(),
end: new Date(),
title: 'A 3 day event',
color: colors.red,
actions: this.actions
}
event_data_1.push(sub_object);
}
this.events = event_data_1;
}
);
// this.column_data = column_data;
}

Your component must be loaded, but you can load and unload HTML parts with *ngIf directive. Just check if your array is empty.
More on that here.
Also, you may wanna check out Angular Interceptors

Related

Add Service name in the Opentelemetry for a JavaScript application

I am trying to integrate Opentelemetry (Otl) in my Angular application to trace the frontend calls. Everything works fine and I am able to see the calls in the Zipkin.
But the only problem is that it is showing it as "unknown_service" in the Zipkin interface.
Below is my entire Angular code and Zipkin screenshot as well. This is just a sample application. But my requirement is that I am going to integrate the Opentelemetry code in the http interceptor so that it will be easy to maintain at one place instead of every service call. Also service.name should be passed dynamically so that it will be traced in Zipkin.
How can I add a service name before it gets called?
import { Component, OnInit } from '#angular/core';
import {ZipkinServicesService} from './zipkin-services.service';
// Opentelemetry components
import { context, trace } from '#opentelemetry/api';
import { ConsoleSpanExporter, SimpleSpanProcessor } from '#opentelemetry/tracing';
import { WebTracerProvider } from '#opentelemetry/web';
import { XMLHttpRequestInstrumentation } from '#opentelemetry/instrumentation-xml-http-request';
import { ZoneContextManager } from '#opentelemetry/context-zone';
import { CollectorTraceExporter } from '#opentelemetry/exporter-collector';
import { B3Propagator } from '#opentelemetry/propagator-b3';
import { registerInstrumentations } from '#opentelemetry/instrumentation';
import { ZipkinExporter } from '#opentelemetry/exporter-zipkin';
#Component({
selector: 'app-zipkin-integration',
templateUrl: './zipkin-integration.component.html',
styleUrls: ['./zipkin-integration.component.scss']
})
export class ZipkinIntegrationComponent implements OnInit {
respData: string;
webTracerWithZone;
constructor(
public zipkinService: ZipkinServicesService,
) {
const providerWithZone = new WebTracerProvider();
const options = {
url: 'http://localhost:9411/api/v2/spans',
serviceName: 'interceptor-example',// This is NOT working.
}
const exporter = new ZipkinExporter(options);
const zipKinProcessor = new SimpleSpanProcessor(exporter);
providerWithZone.addSpanProcessor(zipKinProcessor);
providerWithZone.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));
providerWithZone.addSpanProcessor(new SimpleSpanProcessor(new CollectorTraceExporter()));
providerWithZone.register({
contextManager: new ZoneContextManager(),
propagator: new B3Propagator(),
});
registerInstrumentations({
instrumentations: [
new XMLHttpRequestInstrumentation({
ignoreUrls: [/localhost:8090\/sockjs-node/],
propagateTraceHeaderCorsUrls: [
'https://httpbin.org/post',
],
}),
],
});
this.webTracerWithZone = providerWithZone.getTracer('example-tracer-web');
}
ngOnInit(): void {
}
zipGet (){
let i = 10;
const span1 = this.webTracerWithZone.startSpan(`files-series-info-${i}`);
let postData = [{
no : 2,
emp : 3
}];
context.with(trace.setSpan(context.active(), span1), () => {
this.zipkinService.httpGet(postData).subscribe( (data: any) => {
this.respData = data;
// Opentelemetry after response.
trace.getSpan(context.active()).addEvent('fetching-span1-completed');
span1.end();
});
});
}
zipPost (){
let postData = [{
no : 1,
emp : 2
}];
let i = 10;
const span1 = this.webTracerWithZone.startSpan(`files-series-info-${i}`);
context.with(trace.setSpan(context.active(), span1), () => {
this.zipkinService.httpPost(postData).subscribe( (data: any) => {
this.respData = data;
// Opentelemetry after response.
trace.getSpan(context.active()).addEvent('fetching-span1-completed');
span1.end();
});
});
}
}
Service name must be set via resource as per the specification. I am not sure which version of js libs you are using. This should get you the service name.
import { Resource } from '#opentelemetry/resources';
import { ResourceAttributes } from '#opentelemetry/semantic-conventions'
...
...
const provider = new WebTracerProvider({
resource: new Resource({
[ResourceAttributes.SERVICE_NAME]: "interceptor-example"
}),
});
use providerConfig to set service name. follow code set service name to "SPA Test".
import { Resource } from '#opentelemetry/resources';
import { SemanticResourceAttributes } from '#opentelemetry/semantic-conventions'
import { BatchSpanProcessor } from '#opentelemetry/sdk-trace-base';
import { WebTracerProvider } from '#opentelemetry/sdk-trace-web';
import { ZipkinExporter, ExporterConfig } from '#opentelemetry/exporter-zipkin';
const providerConfig = {
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: "SPA Test"
}),
};
const provider = new WebTracerProvider(providerConfig);
const zipkinOptions: ExporterConfig = {
url: "http://localhost:9411/api/v2/spans"
};
const exporter = new ZipkinExporter(zipkinOptions);
const zipkinProcessor = new BatchSpanProcessor(exporter);
provider.addSpanProcessor(zipkinProcessor);
provider.register();
var tracer = provider.getTracer(CustomersComponent.name, "0.1.0");
var span = tracer.startSpan(CustomersComponent.name);
console.info(span);
span.end();

Save data from Javascript subscription

I'm new to this. I want to get data from Rest API. Loading data from the endpoint is ok, but I want to use it later, outside the method. For example I want to sum one of the attributes of the todos in another function. In funcion loadTodos() the first console log shows the data, but the second one shows only "undefined". How can I save the values what loadTodos() gives back and use it later?
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { TodoDomainService } from '../services/todo-domain.service';
import { Todo } from 'app/model/todo';
#Component({
selector: 'app-todo-listing',
templateUrl: './todo-listing.component.html',
styleUrls: ['./todo-listing.component.scss']
})
export class TodoListingComponent implements OnInit {
todo: Todo;
constructor(private todoService: TodoDomainService, private router:Router) { }
public todos;
ngOnInit() {
this.loadTodos();
this.todo = new Todo();
}
private loadTodos() {
this.todoService.getTodos().subscribe(
data => { this.todos = data },
err => console.error(err),
() => console.log("todos loaded." +this.todos)
);
console.log(this.todos)
}
}
private getSum(todos) {
var sum = 0;
for(var i = 0; i < todos.length; i++){
sum += todos.price[i]}
return this.aggregatedSales;
}
console.log("todos loaded." +this.todos) will show a response because it is executed after the observable has completed.
console.log(this.todos) after your .subscribe(...) shows undefined because the observable hasn't yet finished, that is, the line data => { this.todos = data } hasn't been executed.
You are saving the data correctly for use. If you update your next called for the subscription to look like the following then the sum will execute:
// from
data => { this.todos = data }
// to
data => {
this.todos = data;
this.getSum(this.todos);
}
Here is a stackblitz example of fetching a todos array and adding up the userId values into a sum variable then displaying the value.

Angular Component: Impossible to loop through an array of object with TypeScypt

can any one please tell me why I can not loop through this array?
In ngOnInit, everything works fine. I got an array that I successfully display in the template.
But in ngAfterViewInit, console.log show the array but when looping through with "for of" or "forEach", nothing works.
import { JobsService } from '../jobs.service';
import {Job} from '../models/Job';
#Component({
selector: 'app-job',
templateUrl: 'job.component.html'
})
export class JobComponent implements OnInit, AfterViewInit {
title = 'Job';
jobs: Job[] = [];
InProcess = '';
CurrentPartner = '';
ShowProcess = false;
sended = '';
constructor(private jobsService: JobsService) {
}
ngOnInit() {
this.jobs = this.jobsService.getJobs();
}
ngAfterViewInit() {
console.log(this.jobs); // Show the array
// Nothing happened when looping through the array
this.jobs.forEach((oneJob) => {
console.log(oneJob);
});
}
}
Screenshot of the console in Google Chrome
The content of the service:
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import {Job} from './models/Job';
interface IJob {
message: string;
jobs: any[];
}
#Injectable({
providedIn: 'root'
})
export class JobsService {
constructor(private httpClient: HttpClient) { }
private REST_API_SERVER = 'http://localhost:8080/myband/api/getjobs.php';
private REST_API_SERVER_SEND = 'http://localhost:8080/myband/api/sendjob.php';
jobList: Job[] = [];
errorMessage: any;
message: string;
static handleError(err: HttpErrorResponse) {
let errorMessage = '';
if (err.error instanceof ErrorEvent) {
errorMessage = `An error occurred: ${err.error.message}`;
} else {
errorMessage = `Server returned code: ${err.status}, error message is: ${err.message}`;
}
console.error(errorMessage);
return throwError(errorMessage);
}
public getJobs() {
this.requestJobs().subscribe(
iJob => {
this.message = iJob.message;
for (const job of iJob.jobs) {
const oneJob: Job = new Job(job);
this.jobList.push(oneJob);
}
},
error => this.errorMessage = error as any
);
return this.jobList;
}
public requestJobs(): Observable<IJob> {
return this.httpClient.get<IJob>(this.REST_API_SERVER).pipe(
catchError(JobsService.handleError)
);
}
}
The first thing I want to say to you is about isolation of responsibilities.
Your service must have just one job: provider one way to access your data; It means your logic inside getJobs() method could be done in your component.
export class JobsService {
constructor(
private httpClient: HttpClient,
) {}
private REST_API_SERVER = 'http://localhost:8080/myband/api/getjobs.php';
public requestJobs(): Observable<IJob> {
return this.httpClient.get<IJob>(this.REST_API_SERVER);
}
}
Now, you can handler your data in your component.
import { JobsService } from '../jobs.service';
#Component({
selector: 'app-job',
templateUrl: 'job.component.html'
})
export class JobComponent implements OnInit, AfterViewInit {
title = 'Job';
jobs$;
InProcess = '';
CurrentPartner = '';
ShowProcess = false;
sended = '';
constructor(private jobsService: JobsService) {
}
ngOnInit() {
this.jobs$ = this.jobsService.requestJobs();
}
ngAfterViewInit() {
this.jobs$
.pipe(
map(() => {}), // change your data here
catchError(() => {}) // handler your error here;
)
.subscribe(
() => {} // have access to your final data here.
);
}
}
Things to know:
You can remove the subscribe() execution and use the async pipe in your template;
The use of the operator map in pipe() is optional, you can handler your final data directly from your first callback subscribe().
You can convert your Observable to Promise using toPromise() method in one observable. Don't forgot async / await in your ngAfterViewInit.
Let me know if there is something I can help.
Try:
Object.keys(this.jobs).forEach(job => {
console.log(this.jobs[job]);
});
Try to assign an iterator function with below part replacement by this code:
// Nothing happened when looping through the array
this.jobs.forEach(oneJob, function(value, key) {
console.log(key + ': ' + value);
});
Usage of forEach in AngularJS:
For documentation try to check AngularJS forEach Docs
Syntax:
someIterable.forEach(object, iterator, [context])
Please check below example
class Job {
id: any;
status: any;
constructor(obj: any) {
this.id = obj.id;
this.status = obj.status;
}
}
let arr = [
{
id: 1,
status: "job"
}, {
id: 2,
status: "job2"
}
];
let newArr: any = [];
arr.forEach(a => {
let obj: Job = new Job(a);
newArr.push(obj);
})
console.log(newArr);
newArr.forEach((a: any) => {
console.log(a);
})

Angular DataTable not sorting asynchronous data

On a datatable that I am using, I am having mixed results when attempting to sort them by using the toggles in the table headers. It appears that columns that get populated by the response in my initial GET call populate and sort as expected. However, the columns with data that comes from the GET calls inside the outer subscription don't sort as expected. Ideally all columns would be able to sort properly.
When wrapping the this.dtTrigger.next() in a setTimeout() of 5000ms, all columns sorted as expected. So the question here is, how would I wait until the innermost GET call finishes to call the dtTrigger.next()so that all of the columns will be sortable once all of the data is finished loading in.
import { Component, OnInit, Input, OnChanges } from '#angular/core';
import { Subject } from 'rxjs';
import { BuildersService } from '#/services/builders/builders.service';
import { CommunitiesService } from '#/services/communities/communities.service';
import { ContactsService } from '#/services/contacts/contacts.service';
import { DataTableDirective } from 'angular-datatables';
#Component({
selector: 'datatable-builders',
templateUrl: './builder-datatable.component.html',
styleUrls: ['./builder-datatable.component.scss']
})
export class BuilderDatatableComponent implements OnInit {
#Input() title: string = '';
#Input() showFooter: boolean = true;
#Input() rowsPerPage: number = 10;
#Input() viewRoute: string = '';
#Input() viewText: string = 'View';
rows = [];
dtOptions: DataTables.Settings = {};
dtTrigger: Subject<any> = new Subject();
constructor(
private buildersService: BuildersService,
private communitiesService: CommunitiesService,
private contactsService: ContactsService
) { }
dataTablesInit() {
this.dtOptions = {
pagingType: 'simple_numbers',
lengthChange: false,
info: this.showFooter,
paging: this.showFooter,
columnDefs: [
{
targets: [4],
orderable: false,
searchable: false
}
],
initComplete: () => {
let searchLabels = document.querySelectorAll('.dataTables_filter > label');
searchLabels.forEach((label) => {
label.setAttribute('aria-label', 'Search/Filter Table');
});
}
};
}
ngOnInit() {
this.dataTablesInit();
// Get the table data
this.buildersService.getBuilders().subscribe((result: any) => {
// Get all Rows
let rows = result.body.map(row => {
let communities = [],
communitiesColumn = [],
managersColumn = [];
// Get the builders info and set up output
row.CommunityIDs.forEach((id) => {
this.communitiesService.getCommunity(id).subscribe((result: any) => {
communities.push(result.body);
communitiesColumn.push( result.body.Name );
});
});
row.Contacts.Managers.forEach((id) => {
this.contactsService.getContact(id).subscribe((result: any) => {
managersColumn.push(result.body);
});
});
console.log(row);
// Set additional row data
row.Communities = communities;
row.CommunitiesColumn = communitiesColumn;
row.ManagersColumn = managersColumn;
return row;
});
this.rows = result.body;
this.dtTrigger.next();
console.log(this.rows)
});
}
}
This is the synchronicity of JavaScript in general, you can use the keyword Async in the top level GET and Await keyword in the down level GET
e.g :
// Get the table data
async this.buildersService.getBuilders().subscribe((result: any) => {
...
//
// Get the builders info and set up output
row.CommunityIDs.forEach((id) => {
await this.communitiesService.getCommunity(id).subscribe((result: any) => {
...
//
communities.push(result.body);
communitiesColumn.push( result.body.Name );
});
});
row.Contacts.Managers.forEach((id) => {
this.contactsService.getContact(id).subscribe((result: any) => {
managersColumn.push(result.body);
});
});
console.log(row);
// Set additional row data
row.Communities = communities;
row.CommunitiesColumn = communitiesColumn;
row.ManagersColumn = managersColumn;
return row;
});
this.rows = result.body;
this.dtTrigger.next();
console.log(this.rows)
});
}
}

Why change in Observable variable triggers change in View Angular

I'm learning Angular, so I'm building todo app. Todos are fetched from API, and every one of them has a project as parent. In a view I'm adding new project which triggers addNewProject method in service which in turn triggers POST request to the API in another service. Projects are listed in the sidebar, bind to input from parent component. ProjectsService holds array of projects in a private field, and has observable which is used by main component.
I'm struggling to understand why on earth appending private property _projects after API call in the service triggers change in MainComponent property even though _projects is private and change in Observable from array should not trigger functions passed from Observers.
Parent:
#Component({
selector: 'app-main',
templateUrl: './main.component.html',
styleUrls: ['./main.component.css']
})
export class MainComponent implements OnInit {
projects: Project[] = new Array<Project>();
constructor(private projectsService: ProjectsService) {
}
ngOnInit() {
this.projectsService.projects$.subscribe((projects) => {
this.projects = projects;
});
}
addNewProject(newProjectName: string) {
this.projectsService.addNewProject(newProjectName);
}
}
Sidebar:
#Component({
selector: 'app-sidebar',
templateUrl: './sidebar.component.html',
styleUrls: ['./sidebar.component.css']
})
export class SidebarComponent implements OnInit {
#Input() projects: Project[];
#Output() projectEntered = new EventEmitter<string>();
constructor() {
}
ngOnInit() {
}
projectAdded(projectName: string) {
this.projectEntered.emit(projectName);
}
}
Service:
export class ProjectsService {
private _projects$: Observable<Array<Project>> = new Observable<Array<Project>>();
private _projects: Project[] = [];
private _loadedProject$: Observable<Project>;
private _projectsLoaded: boolean;
private _taskForProject: object;
constructor(private userService: UserService, private api: ApiService) {
}
loadAllProjects() {
this._projects$ = this.api.getAllProjectsByUserId(this.userService.userId).pipe(
map((projects) => {
this._projects = projects;
return this._projects;
})
);
}
get projects$() {
if (!this._projectsLoaded) {
this.loadAllProjects();
}
return this._projects$;
}
getAllTasks() {
return this.api.getAllTasksByUserId(this.userService.userId);
}
getProject(projectId: string) {
this._loadedProject$ = this.api.getProjectById(projectId);
return this._loadedProject$;
}
getTodayTasksForProject(tasks: Task[]) {
const todayTasks: Task[] = [];
const todayDate = new Date();
tasks.forEach((task) => {
if (new Date(task.completionPlannedDate).getDate() === todayDate.getDate()) {
tasks.splice(tasks.indexOf(task), 1);
todayTasks.push(task);
}
});
return todayTasks;
}
getTomorrowTasksForProject(tasks: Task[]) {
const tomorrowTasks: Task[] = [];
const tomorrowDate = new Date(new Date().getDate() + 1);
tasks.forEach((task) => {
if (new Date(task.completionPlannedDate).getDate() === tomorrowDate.getDate()) {
tasks.splice(tasks.indexOf(task), 1);
tomorrowTasks.push(task);
}
});
return tomorrowTasks;
}
getUpcomingTasks(tasks: Task[]) {
const upcomingTasks: Task[] = [];
const upcomingDate = new Date(new Date().getDate() + 2);
tasks.forEach((task) => {
if (new Date(task.completionPlannedDate).getDate() > upcomingDate.getDate()) {
tasks.splice(tasks.indexOf(task), 1);
upcomingTasks.push(task);
}
});
return upcomingTasks;
}
addNewProject(projectName: string) {
this.api.postNewProject({
id: null,
userId: this.userService.userId,
title: projectName,
tasks: []
}).subscribe((project: Project) => {
this._projects.push(project);
});
}
}
Please see here:
Basically application state change can be caused by three things:
Events - click, submit
XHR - Fetching data from a remote server
Timers - setTimeout(), setInterval()
If you don't want change detection to fire try changing to ChangeDetectionStrategy.OnPush

Categories

Resources