0

I have component with tabs (ProfileComponent is parent for others). I want to load data from server using services - roles and projects - in ProjekteingangComponent.

I wrote for this functions for loading data (loadRoles() and loadProjects()) and call it on ngOnInit(), but the problem is that value not sets for this.roles and this.projects. Here and here is output to console in browser.

As you see in output value in loadRoles() and loadProjects() writes after the error. How can I improve this and set this.projects and this.roles in ngOnInit() correctly?

projekteingang.component.ts:

import {Component, Input, OnInit} from '@angular/core';
import {ProfileComponent} from '../profile.component';
import {UserOfferMapping} from "../../../_models/UserOfferMapping";
import {UseroffermappingService} from "../../../_services/useroffermapping.service";
import {Userproject} from "../../../_models/Userproject";
import {Userrole} from "../../../_models/userrole";
import {UserprojectService} from "../../../_services/userproject.service";
import {UserroleService} from "../../../_services/userrole.service";
import {ProjectUtils} from "../../../utils/ProjectUtils";

@Component({
  selector: 'app-projekteingang',
  templateUrl: './projekteingang.component.html',
  styleUrls: ['./projekteingang.component.css']
})
export class ProjekteingangComponent implements OnInit {
  public userOfferMapping: any[];
  public  userId: number;
  public offerDataSource: any[] = [];
  public projects: Userproject[] = [];
  public roles: Userrole[] = [];
  constructor(
    private profileComponent: ProfileComponent,
    private useroffremappingService: UseroffermappingService,
    private userProjectService: UserprojectService,
    private userRoleService: UserroleService,) {
  }

  loadRoles() {
    this.userRoleService.getAllRoles().toPromise().then((value) => {
      console.log("value roles");
      console.log(value);
      this.roles = value;
    });
  }

  loadProjects(){
    this.userProjectService.getAllProjects().toPromise().then((value) => {
      console.log("value projects");
      console.log(value);
      this.projects = value;
    });
  }

  ngOnInit() {
    this.userId = this.profileComponent.currentUser.id;
    this.loadProjects();
    this.loadRoles();
    console.log("roles ngoinit");
    console.log(this.roles);
    console.log("projects ngoinit");
    console.log(this.projects);
    this.loadUserOfferMappings();
  }

  findProjectByID(id) {
    let project = null;
    this.projects.map(elem => {
      if (elem.id == id) {
        project = elem;
      }
    })
    return project;
  }

  findRoleByID(id) {
    let role = null;
    this.roles.map(elem => {
      if (elem.id == id) {
        role = elem;
      }
    })
    return role;
  }

  loadUserOfferMappings() {
    console.log("roles in func");
    console.log(this.roles);
    console.log("projects in func");
    console.log(this.projects);
    this.useroffremappingService.getAllUseroffermappingsByUserId(this.userId).subscribe(value => {
      console.log("Useroffermapping");
      console.log(value);
      this.userOfferMapping = value;
      value.map(elem => {
        console.log(elem);
        let project = this.findProjectByID(elem.projectId);
        let role = this.findRoleByID(elem.roleId);
        console.log(role);
        console.log(project);
        this.offerDataSource.push({
          projectId: elem.projectId,
          roleId: elem.roleId,
          projectName: project.name,
          roleName: role.name,
          roleTooltip: role.description
        });
      })
    });
  }

  get profile() {
    return this.profileComponent;
  }

}

profile.component.html:

  <mat-tab-group>
    <-- ... -->
    <-- another tabs -->
    <-- ... -->
    <mat-tab label="Projekt eingang">
      <app-projekteingang></app-projekteingang>
    </mat-tab>
</mat-tab-group>
Se Br
  • 101
  • 3
  • 14
  • because you are calling an async operation, and when the component is created the values are not available to display and then later the values are received from the backend. just add *ngIf to mat-tab-group and check if they are valid(is there somthing to display). – Keryanie Sep 20 '19 at 09:50

2 Answers2

2

I see multiple misunderstood things:

  1. no need to use .toPromise()

use .subscribe(...) instead:

loadProjects(){
    this.userProjectService.getAllProjects().subscribe((value) => {
      console.log("value projects");
      console.log(value);
      this.projects = value;
    });
  }
  1. You are getting asynchronous data

you have to wait for them before beeing able to use them.

  1. Your critacal code needs the answer of all 3 calls

So you have to extract this code to an other method, called on each data reception (which check if you have all data or not yet)

loadRoles() {
  this.userRoleService.getAllRoles().subscribe((value) => {
    console.log("value roles");
    console.log(value);
    this.roles = value;
    this.feedOfferDataSource();
  });
}

loadProjects(){
  this.userProjectService.getAllProjects().subscribe((value) => {
    console.log("value projects");
    console.log(value);
    this.projects = value;
    this.feedOfferDataSource();
  });
}

loadUserOfferMappings() {

  console.log("roles in func");
  console.log(this.roles);
  console.log("projects in func");
  console.log(this.projects);
   this.useroffremappingService.getAllUseroffermappingsByUserId(this.userId).subscribe(value => {
    console.log("Useroffermapping");
    console.log(value);
    this.userOfferMapping = value;
    this.feedOfferDataSource();
  }
}

feedOfferDataSource() {
  if(!this.projects || !this.roles || !this.userOfferMapping) return;

  this.offerDataSource = this.userOfferMapping.map(elem => {
    console.log(elem);
    let project = this.findProjectByID(elem.projectId);
    let role = this.findRoleByID(elem.roleId);
    console.log(role);
    console.log(project);
    return {
      projectId: elem.projectId,
      roleId: elem.roleId,
      projectName: project.name,
      roleName: role.name,
      roleTooltip: role.description
    };
}
  1. You used map without using its value
Random
  • 3,158
  • 1
  • 15
  • 25
1

getAllRoles, getAllProjects and getAllUseroffermappingsByUserId are three separate asynchronous calls. One can not guarantee the order in which the three will be completed.

Now the function loadUserOfferMappings actually depends upon the data being fetched for roles and projects. So that means getAllUseroffermappingsByUserId should be called only after projects and roles are already fetched.

Now there are multiple ways to solve this problem:

  1. The most novice way is to check if roles and projects are available before using those in the getAllUseroffermappingsByUserId;

  2. More sensible way is to actually call the loadUserOfferMappings method once the roles and projects calls are resolved only.(with observables or promises anything )

  3. One more way can be to maintain the loading state for you promise calls. e.g. an asynchronous call be in loading, loaded or error state. So call the getAllUseroffermappingsByUserId only when getRoles and getProjects are in loaded states.

Vishal Sharma
  • 316
  • 1
  • 8