Formbuilder setvalue() is not working as expected when used on dropdown selects

流过昼夜 提交于 2021-02-18 14:02:49

问题


Angular 2,4 formbuilder setvalue() is not working as expected when used on dropdown selects.

I have the following dropdown select that gets populated with Github organizations:

  <select name="org" formControlName="organizations">
    <option *ngFor="let org of organizations" [ngValue]="org">{{org.organization.login}}</option>
  </select>

Here is the javascript code that sets the Github organization that should be selected.

  this.pipelineForm = fb.group({
      name:'',
      organizations:'',
      repos: '',
      branches:'',
      runtime:'',
      versions:''
    });

  userService.getOrganizations().subscribe((org:github)=>{
  let organizationName = org.data[0];
   this.organizations = org.data;
      this.projects.subscribe((p)=> {
        p[1].project.subscribe((f)=>{

         this.pipelineForm.get("organizations").setValue(f.organizations, {onlySelf: true});

          //this.pipelineForm.patchValue(f);
        });
      });

    });

I expect the corresponding dropdown option to be selected when I pass the value to the setValue(). Instead, I get a blank option. I also tried with patchValue(). No luck.


回答1:


I struggled with the same problem and found Igor's answer. It was too vague so I dug around more. I finally figured it out, and Igor's answer is correct albeit lacking in detail. Hopefully this will help anyone else in the future trying to get setValue() on dropdowns and object models to work happily together.

Generally, the object model passed into setValue() should be the same as the objects you inject into your dropdown selector. E.g. if of type Organization, then the setValue should be also be of type Organization. It's not necessary, however, to share completely identical properties.

To get the setValue() or patchValue() to work with an object model (as opposed to some primitive type), use the [compareWith] function as Igor pointed out.

From node_modules/@angular/material/select/typings/select.d.ts:

compareWith is a function to compare the option values with the selected values. The first argument is a value from an option. The second is a value from the selection. A boolean should be returned.

In your html template,

<select [compareWith]="compareOrgs" name="org" formControlName="organizations">
    <option *ngFor="let org of organizations" [ngValue]="org">{{org.organization.login}}</option>
</select>

And in your component.ts file, define the compareOrgs() function:

compareOrgs(c1: any, c2: any): boolean {
   return c1 && c2 ? c1.orgId === c2.orgId : c1 === c2; 
}

Don't forget to call the setValue() function, for e.g. in ngOnInit() or in the callback function if it's async and you're fetching data.

E.g. in the callback from OP's code above,

// "organization" is whatever object you're trying to patch on the dropdown
this.pipelineForm.get("organizations").setValue(organization)

So what all this does is, the compareOrgs function will compare the value of the selected value (which is the object you passed into setValue, now labelled as c2 in compareOrgs) with each of the option values (c1). Specifically, it compares the orgId properties of the selected and option values. This is how the FormControl knows which value to pre-select on the dropdown.

You can now access the object with [ngValue].

This is a good resource that helped me: https://codeburst.io/angular-material-select-module-the-comparewith-function-9dfdb4035373




回答2:


A little example, we want to save a number. see [ngValue]="org.id" and this.myForm.controls['organization'].setValue(value);

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
interface Iorganization {
  id: number;
  name: string;
}
@Component({
  selector: 'app-select',
  template: `
  <form [formGroup]="myForm" novalidate>
    <select name="org" formControlName="organization">
      <option *ngFor="let org of organizations" [ngValue]="org.id">{{org.name}}</option>
    </select>
  </form>
  <button (click)="setOrg(1)">Org 1</button>
  <button (click)="setOrg(2)">Org 2</button>
  {{myForm.value |json}}
  `,
  styleUrls: ['./select.component.css']
})
export class SelectComponent implements OnInit {
  organizations: Iorganization[];
  myForm: FormGroup;
  constructor(private fb: FormBuilder) { }
  ngOnInit() {
    this.organizations = [
      { id: 1, name: "org 1" },
      { id: 2, name: "org 2" },
      { id: 3, name: "org 3" },
      { id: 4, name: "org 4" }
    ];
    this.myForm = this.fb.group({
      organization: 1
    })
  }
  setOrg(value: number) {
    this.myForm.controls['organization'].setValue(value);
  }
}

if we want to save a object -see {{myForm.value |json}}- we need make a few changes. see that we put [ngValue]="org", but the setOrg() function use a object to make setValue. And not, it's not valid setValue({id:1,name:'org1'})

import { Component, OnInit } from '@angular/core';
....
template: `
<form [formGroup]="myForm" novalidate>
    <select name="org" formControlName="organization">
      <option *ngFor="let org of organizations" [ngValue]="org">{{org.name}}</option>
    </select>
  ....`
export class SelectComponent implements OnInit {
  ...
ngOnInit() {
   this.organizations = [
     { id: 1, name: "org 1" },
        ....
    ];
    this.myForm = this.fb.group({
      organization: null
    })
}
setOrg(value: number) {
   let organization=this.organizations.find(o=>o.id==value);
   this.myForm.controls['organization'].setValue(organization);
}



回答3:


Use the attribute compareWith on your select tag:

<select ... [compareWith]="compareByName">

Then implement a simple function in your component

compareByName(org1: Org, org2: Org): boolean {
  return org1 && org2 ? org1.name === org2.name : org1 === org2;
}


来源:https://stackoverflow.com/questions/46780829/formbuilder-setvalue-is-not-working-as-expected-when-used-on-dropdown-selects

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!