Populate a dropdown list from a service after form has loaded

3.8k views Asked by At

I have a similar problem to that described in Drop Down List in Angular 2 Model Driven Form (although with a much simpler model binding to the select box).

The template is very simple, basically a list of 'Dear x' where x is provided by the service:

screenshot of select box

<form novalidate [formGroup]="myForm">
  <div class="form-group">
    <label for="salutation">Dear ...</label>
    <select id="salutation"
            class="form-control"
            formControlName="salutation">
      <option value="">Please select how to address the customer</option>
      <option *ngFor="let sal of salutations"
              [value]="sal">{{sal}}
      </option>
    </select>
  </div>
</form>

In the components I subscribe to a service that gets the data for this select box (the console.log shows that indeed the data does arrive).

  ngOnInit() {
    this.createFormControls();
    this.createForm();
    this.proposalStateService.emitProposal.subscribe(
      data => {
        console.log('subscribe fired: ');
        console.log(data);
        this.salutations = [
          'Mr & Mrs ' + data.last_name,
          'Mrs ' + data.last_name,
          'Ms ' + data.last_name,
          'Mr ' + data.last_name,
          data.first_name
        ];
      }
    );
  }

  createFormControls() {
    this.salutation = new FormControl(this.salutations, Validators.required);
  }

  createForm() {
    this.myForm = new FormGroup({
      salutation: this.salutation
    });
  }

I have tried these methods to get the form to update with the values from the service, but none of them seem to work:

  1. Reset the group

    this.myForm = this.formBuilder.group({
      salutation: [this.salutations]
    });
    
  2. Patch the value on the form

    this.myForm.patchValue(this.salutation, this.salutations);
    
  3. Set the value on the control

    this.salutation.setValue(this.salutations);
    
  4. Set the value via the form

    this.myForm.controls['salutation'].setValue(this.salutations);
    

Clearly I am missing something...but what?

EDIT TO ORIGINAL QUESTION

Sometimes the console was showing the data arriving, but after cleaning up my code and after further testing, the console.log events are now not showing when this component loads. I think this must be a timing issue - possibly the component is loading AFTER the service that emits the data it needs has already fired.

This component is loaded by a parent component on a navigate event, like so:

/parent.component.ts

  ngOnInit() {
    this.newProposal = new Proposal;
    this.newProposal.step = 1;
    this.proposalStateService.emitProposal.subscribe(
      data => {
        this.router.navigate(['pages/proposals/new/step' + data.step]);
      }
    );  

That emitProposal is fired in this component by the user dropping some data onto it, which results in this method being called:

  private initProposal(customer, step) {
    this.newProposal.first_name = customer.first_name;
    this.newProposal.last_name = customer.last_name;
    this.newProposal.customer_id = customer.id;
    this.newProposal.email = customer.email;
    this.newProposal.mobile = customer.mobile;
    this.newProposal.status = 'Draft';
    this.newProposal.step = step;
    this.proposalStateService.pushProposal(this.newProposal);
  }

So it seems this 'pushProposal' is fired before the child component loads, could that be the problem?

(now I am wondering how the log previously was showing the data being received, ha, what the hell did I change while writing this question!?)

1

There are 1 answers

0
rmcsharry On BEST ANSWER

Ok, so I found a solution - thanks to those who helped with comments and links to other questions.

When I move form to the parent, the data arrives and loads into the form using option (4):

this.myForm.controls['salutation'].setValue(this.salutations);

But obviously I don't want it there, and to only load it after the parent has navigated.

So the solution to this problem was to enhance the service by allowing it to 'store' the object pushed by the parent. Thus the emitter has changed from this:

  emitProposal = new EventEmitter<Proposal>();
  pushProposal(value: Proposal) {
    this.emitProposal.emit(value);
  }

to this:

  currentProposal: Proposal;

  getCurrentProposal() {
    return this.currentProposal;
  }

  emitProposal = new EventEmitter<Proposal>();
  pushProposal(value: Proposal) {
    this.currentProposal = value;
    this.emitProposal.emit(value);
  }

and the form now simply has to do this:

 ngOnInit() {
    this.createFormControls();
    this.createForm();
    this.proposal = this.proposalStateService.getCurrentProposal();
    this.salutations = [
          'Mr & Mrs ' + this.proposal.last_name,
          'Mrs ' + this.proposal.last_name,
          'Ms ' + this.proposal.last_name,
          'Mr ' + this.proposal.last_name,
          this.proposal.first_name
    ];
 }