Problem in getting data in dropdown in Angular

Viewed 428

I'm trying to populate a dropdown from a get API. I've created a service and using the observables returning the data to the component, which then subscribes to the service. I'm able to get the data but it it reflects in Drop down only when i click once anywhere outside the dropdown menu. The data comes instantly in console.log when the page refresh but the dropdown gets data only when I click somewhere outside the dropdown. I have tried reading many posts regarding the dropdown but could not resolve this issue. My code is below, the behavior remains same if i remove the clause (change)=selectedHandlerProjectId($event):

sample.component.html

<div class="form-group">
 <label>
   <h3>Select ProjectName</h3>
 </label>
 <select (change)=selectedHandlerProjectId($event) ngModel="selectedProjectName" class="form-control">
     <option *ngFor="let Project of ProjectDetails" >{{Project.ProjectName}}</option>
 </select>
</div>

sample.component.ts

@Component({
  selector: 'app-sample',
  templateUrl: './sample.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class sampleComponent implements OnInit {

  public ProjectDetails: TestProject[] = []; 

  constructor(private route:ActivatedRoute,
    private projService:projectService) { }

    getSelectedProject(): void{  
      this.projService.generateProjectList().subscribe(
        projectDetails => {
          this.projectDetails = projectDetails;
          console.log(this.projectDetails);
        }  
      );
    }


  //methods to get dropdown values
  dropDownProjectId: string = '';
  selectedHandlerProjectId(event : any)
  {
    if(event.target.value != 'default') { 
      this.dropDownProjectId = event.target.value;}
    else {this.dropDownProjectId = null;}
  }

  ngOnInit() {
    this.getSelectedProject();
  }

sample.service.ts

import { Injectable } from '@angular/core';
import {ServiceConstants} from "../../shared/service-constants";
import {HttpClient} from "@angular/common/http";
import {Observable} from "rxjs";
import {TestProject} from "../models/model.component";

@Injectable({
  providedIn: 'root'
})
export class projectService {

  rootUrl: string = `${ServiceConstants.project_URL}/test-project-manager`;

  constructor(private authHttp: HttpClient) {
  }

  generateProjectList(): Observable<any> {
    return this.authHttp.get<Array<TestProject>>(`${this.rootUrl}/testprojects`);  
  }

}
2 Answers

It is due to changeDetection: ChangeDetectionStrategy.OnPush

Remove that line and everything will be working or

Change

 public ProjectDetails: TestProject[] = [];

TO

public ProjectDetails: Array<TestProject>;

Means without any default value;

Or

this.projectDetails = projectDetails;
this.projectDetails = this.projectDetails.slice();

This way this.projectDetails refers a different array instance and Angular will recognize the change.

When using changeDetection: ChangeDetectionStrategy.OnPush Angular needs to be informed of when you have updated a field on the class.

So when you are setting this.projectDetails = projectDetails; angular doesn't know that projectDetails now contains your data.

constructor(private route:ActivatedRoute, private cdr: ChangeDetectorRef, private projService:projectService) { }

    getSelectedProject(): void{  
      this.projService.generateProjectList().subscribe(
        projectDetails => {
          this.projectDetails = projectDetails;
          this.cdr.detectChanges();
          console.log(this.projectDetails);
        }  
      );
    }

To make angular detect your change to the component you import the ChangeDetectorRef and call detectChanges() on it.

Related