显示数据库获取的值(如果存在)否则在 ng-select angular 中显示默认文本

show database fetched values if present else show default text in ng-select angular

我在 angular 中有 3 个国家、州、城市的下拉菜单。我使用 ng-select 模块来处理参考 here 中的那些 dowpdowns。在国家/地区更改状态时填充,在状态更改时城市填充。

模板HTML

<ng-select formControlName="country" (change)="onChangeCountry($event)" >
    <ng-option value="dbCountryId ? dbCountryId : ''">{{dbCountryName ? dbCountryName : 'Select Country' }}</ng-option>
    <ng-option *ngFor="let country of countryInfo" [value]="country.id">{{country.name}}</ng-option>
 </ng-select>
 <ng-select formControlName="state" (change)="onChangeState($event)">
    <ng-option value="dbStateId ? dbStateId : ''">{{dbStateName ? dbStateName : 'Select State' }}</ng-option>
    <ng-option *ngFor="let state of stateInfo" [value]="state.id">{{state.name}}</ng-option>
 </ng-select> 
 <ng-select formControlName="city" >
    <ng-option value="dbCityId ? dbCityId : ''">{{dbCityName ? dbCityName : 'Select City' }}</ng-option>
    <ng-option *ngFor="let city of cityInfo" [value]="city.id">{{city.name}}</ng-option>
 </ng-select>

ts代码

 this.userService.getUserDetails(userDetails.id).subscribe((results) => {
  if (results['status'] === true) {

   this.dbCountryName = results.data.country ? results.data.country : null;
              this.dbCountryId = results.data.country_id
                ? results.data.country_id
                : null;
            this.dbStateName = results.data.state ? results.data.state : null;
            this.dbStateId = results.data.state_id
              ? results.data.state_id
              : null;
            this.dbCityName = results.data.city ? results.data.city : null;
            this.dbCityId = results.data.city_id ? results.data.city_id : null;


            this.form.patchValue({
              country:
                results.data.country_id === null ? '' : results.data.country_id,
              state:
                results.data.state_id === null ? '' : results.data.state_id,
              city: results.data.city_id === null ? '' : results.data.city_id,

  });
}
 });

我使用相同的表单来添加和编辑数据。我正在存储国家、州、城市的 ID。在 api 响应中,我得到了存储的 ID、字段名称。我已经用相应的表单控件修补了 id。

我有两个问题。

  1. 'Select country/state/city' 和默认文本一样,它显示在下拉列表中而不是输入框中
  2. 我无法正确显示获取的数据。它显示如下

如何使用 angular 中的 ng-select 解决这些问题?请帮助和指导。谢谢。

编辑

模板代码

<div class="col-sm-6">
                <div class="form-group">
                  <label for="country">Country <b style="color: red">*</b></label><ng-select formControlName="country" (change)="onChangeCountry($event)" [ngClass]="{ 'error_border': submitted && f.country.errors }">
                    <ng-option *ngFor="let country of countryInfo" [value]="country.id">{{country.name}}</ng-option>
                  </ng-select>
                  <div *ngIf="submitted && f.country.errors" class="text-danger">
                    <div *ngIf="f.country.errors.required">Country is required</div>
                  </div>
                </div>
              </div>

   <div class="col-sm-6">
                <div class="form-group">
                  <label for="state">State <b style="color: red">*</b></label>

                  <ng-select formControlName="state"  [ngClass]="{ 'error_border': submitted && f.state.errors }" (change)="onChangeState($event)">
                    <ng-option *ngFor="let state of stateInfo" [value]="state.id">{{state.name}}</ng-option>
                  </ng-select>
                  <div *ngIf="submitted && f.state.errors" class="text-danger">
                    <div *ngIf="f.state.errors.required">State is required</div>
                  </div>
                </div>
              </div>

              <div class="col-sm-6">
                <div class="form-group">
                  <label for="city">City <b style="color: red">*</b></label>
                  <ng-select formControlName="city" [ngClass]="{ 'error_border': submitted && f.city.errors }">
                    <ng-option *ngFor="let city of cityInfo" [value]="city.id">{{city.name}}</ng-option>
                  </ng-select>
                  <div *ngIf="submitted && f.city.errors" class="text-danger">
                    <div *ngIf="f.city.errors.required">City is required</div>
                  </div>
                </div>
              </div>

ts代码

export class EditProfileComponent implements OnInit {

  stateInfo: any[] = [];
  countryInfo: any[] = [];
  cityInfo: any[] = [];

  dbCountryName = '';
  dbCountryId = 0;
  dbStateName = '';
  dbStateId = 0;
  dbCityName = '';
  dbCityId = 0;

  ngOnInit() {

 this.form = this.formBuilder.group({
   
      country: ['Select Country', Validators.required],
      state: ['Select State', Validators.required],
      city: ['Select City', Validators.required],
    
    });

 this.userService.getUserDetails(userDetails.id).subscribe((results) => {
     

          if (results['status'] === true) {
          
            this.dbStateName = results.data.state ? results.data.state : null;
            this.dbStateId = results.data.state_id
              ? results.data.state_id
              : null;
            this.dbCityName = results.data.city ? results.data.city : null;
            this.dbCityId = results.data.city_id ? results.data.city_id : null;
            this.dbCountryName = results.data.country ? results.data.country : null;
            this.dbCountryId = results.data.country_id
              ? results.data.country_id
              : null;

            this.cscService.getCountries().subscribe((result) => {
              this.countryInfo = result.data;
              this.form.patchValue({
                country: this.dbCountryId
              });
            });
            this.cscService.getStates(this.dbCountryId).subscribe((result) => {
              this.stateInfo = result.data;
              this.form.patchValue({
                state: this.dbStateId
              });
            });
            this.cscService
            .getCities(this.dbStateId)
            .subscribe((result) => {
              this.cityInfo = result.data;
              this.form.patchValue({
                city: this.dbCityId
              });
            }
           );

            this.form.patchValue({
           
              // country:
              //   results.data.country_id === null ? 'Select Country' : results.data.country_id,
              // state:
              //   results.data.state_id === null ? 'Select State' : results.data.state_id,
              // city: results.data.city_id === null ? 'Select City' : results.data.city_id,
            
            });
          }
        });

  }

  getCountries() {
    this.cscService.getCountries().subscribe((result) => {
      this.countryInfo = result.data;
    });
  }

  onChangeCountry(countryId: number) {
    if (countryId) {
      this.cscService.getStates(countryId).subscribe((result) => {
        this.stateInfo = result.data;
        this.cityInfo = null;
      });
      this.form.patchValue({ 
        state: "Select State",
        city: "Select City"
      });
    } else {
      this.stateInfo = null;
      this.cityInfo = null;
    }
  }

  onChangeState(stateId: number) {
    if (stateId) {
      this.cscService
        .getCities(stateId)
        .subscribe((result) => (this.cityInfo = result.data));
        this.form.patchValue({ city: "Select City" });
    } else {
      this.cityInfo = null;
    }
  }

}

国家数据响应

状态数据响应 - 进入国家 select(我有 selected 国家 ID =1)

城市数据响应 - 进入状态 select(我有 selected 状态 id =42)

这个解决方案似乎有效。您需要控制器的 ngOnInit 函数中的 formControl 功能。希望这有帮助。

在 html 文件中,我有:

<div class="position">
  <form [formGroup]="FormOne">
    <ng-select
      [formControlName]="city"
      (change)="onChangeCity($event)"
      placeholder="Select City"
      ><ng-option *ngFor="let city of cities" [value]="city.id">{{
        city.name
      }}</ng-option>
    </ng-select>
  </form>
 </div>

在控制器中我有这样的例子:

import { Component, OnInit } from '@angular/core';
import {
  FormGroup,
  Validators,
  FormControl,
  AbstractControl,
} from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
  cities = [
    { id: 1, name: 'abc' },
    { id: 2, name: 'def' },
    { id: 3, name: 'ghi' },
    { id: 4, name: 'jkl' },
    { id: 5, name: 'mno' },
  ];
  FormOne: FormGroup;

  constructor() {}

  ngOnInit() {
    this.FormOne = new FormGroup({
      city: new FormControl(null, Validators.required),
    });
  }
 
}

您应该尝试以下选项状态的项目

<ng-container ngFor="let country in countries"> \ you can apply forloop like this and it will show default value
    <ng-select
      [items]="{{country.name}}"
      formControlName="country"
      bindLabel="label"                 
      [multiple]="true"
      placeholder="Select Country"
      [(ngModel)]="selectedAttributes">
    </ng-select>
</ng-container>

添加以下模块

import { NgSelectModule } from '@ng-select/ng-select';
import { FormsModule } from '@angular/forms';


@NgModule({
  imports: [
    FormsModule,
    NgSelectModule,
 

有关更多信息,您可以查看此 link ng-select docs

据我了解,您正在使用 Reactive Forms 来管理这些控件。如果这不是真的,请告诉我。

我建议的 HTML 模板与您的类似,但更简单。我建议不要为所选值/默认消息添加单独的 <ng-option>

<ng-select formControlName="country" (change)="onChangeCountry($event)" style="width: 200px;">
  <ng-option *ngFor="let country of countries" [value]="country.id">{{country.name}}</ng-option>
</ng-select>

<ng-select formControlName="state" (change)="onChangeState($event)" style="width: 200px;">
  <ng-option *ngFor="let state of statesToShow" [value]="state.id">{{state.name}}</ng-option>
</ng-select>

<ng-select formControlName="city" (change)="onChangeCity($event)" style="width: 200px;">
  <ng-option *ngFor="let city of citiesToShow" [value]="city.id">{{city.name}}</ng-option>
</ng-select>

我还包含了一个 TS 文件,其中包含有关如何设置的工作示例

  1. 适当的占位符消息。
  2. 已选择的值。请参阅 prefefinedValues() 函数。

请注意,为了使 2. 按预期工作,元素的 ID 需要在当前为控件选择的数据源中(在我的示例中 statesToShowcitiesToShow ).如果没有,它将显示为文本(可能是您遇到的)。

import { Component } from '@angular/core';
import { FormBuilder, FormGroup, FormsModule } from '@angular/forms';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'test2';

  guestForm: FormGroup;

  selectedCar: number = 0;

  // this is the data source for the STATES drop-down (initially empty)
  // => will be populated when a COUNTRY is selected
  public statesToShow: Array<any> = [];
  // this is the data source for the CITIES drop-down (initially empty)
  // => will be populated when a STATE is selected
  public citiesToShow: Array<any> = [];

  // TEST data start
  public countries = [
      { id: 1, name: 'Romania' },
      { id: 2, name: 'USA' },
      { id: 3, name: 'France' },
      { id: 4, name: 'Spain' },
  ];

  public states = [
    [],
    [
      {id: 1, name: "Cluj"},
      {id: 2, name: "Valcea"},
      {id: 3, name: "Sibiu"},
      {id: 4, name: "Mures"},
    ],
    [
      {id: 5, name: "New York"},
      {id: 6, name: "Oregon"},
      {id: 7, name: "Arizona"},
      {id: 8, name: "Texas"},
    ],
    [
      {id: 9, name: "Normandie"},
      {id: 10, name: "Ile-de-France"},
      {id: 11, name: "Grand Est"},
      {id: 12, name: "Occitanie"},
    ],
    [
      {id: 13, name: "Alicante"},
      {id: 14, name: "Valencia"},
      {id: 15, name: "Sevilla"},
      {id: 16, name: "Malaga"},
    ]
  ];


  public cities = [
    [],
    [
      {id: 1, name: "Cluj-Napoca"},
      {id: 2, name: "Turda"},
      {id: 3, name: "Huedin"},
    ],
    [
      {id: 4, name: "Ramnicul Valcea"},
      {id: 5, name: "Horezu"},
      {id: 6, name: "Olanesti"},
    ],
    [],
    [],
    [
      {id: 10, name: "New York city 1"},
      {id: 11, name: "New York city 2"},
      {id: 12, name: "New York city 3"},
    ],
    [
      {id: 13, name: "Oregon city 1"},
      {id: 14, name: "Oregon city 2"},
      {id: 15, name: "Oregon city 3"},
    ]
  ]

  // TEST data end

  private dbCountryId: number | null = null;
  private dbStateId: number | null = null;
  private dbCityId: number | null = null;
  
  constructor(private _fb: FormBuilder) {
    // add default placeholder messages for all the controls
    this.guestForm = this._fb.group({
      country: ['Please select country', null],
      state: ['Please select state', null],
      city: ['Please select city', null]
    });
  }

  ngOnInit() {
    
  }

  onChangeCountry(value: number) {
    // set the data source for the STATES drop-down
    this.statesToShow = this.states[value];
    // display placeholders for STATES and CITIES until the user
    // selects the values
    this.guestForm.patchValue({ 
      state: "Please select state !",
      city: "Please select city !"
    });
  }

  onChangeState(value: number) {
    // set the data source for the CITIES drop-down
    this.citiesToShow = this.cities[value] ? this.cities[value] : [];
    // display the placeholder until the user selects a new city
    this.guestForm.patchValue({ city: "Please select city !" });
  }

  onChangeCity(value: number) {
    console.log(value);
  }

  // example on how to correctly set preselected values
  // in the controls
  predefinedValues() {
    // preselected values (MUST BE A VALID COMBINATION)
    this.dbCountryId = 2;
    this.dbStateId = 6;
    this.dbCityId = 14;

    // set the sources for STATES and CITIES drop-downs
    this.statesToShow = this.states[this.dbCountryId];
    this.citiesToShow = this.cities[this.dbStateId];

    // set the preselected IDs as current value in all drop-downs
    this.guestForm.patchValue({ 
      country: this.dbCountryId,
      state: this.dbStateId,
      city: this.dbCityId
    });
  }
}

编辑: 从服务器加载数据

当从服务器接收到数据时,我们需要等待信息到达,然后再对表单控件进行任何修补。 predefinedValues函数变为:

predefinedValues() {
  // read saved database values:
  this._dataService.getSavedDatabaseValues()
    .then(data => {
      this.dbCountryId = data.dbCountryId;
      this.dbStateId = data.dbStateId;
      this.dbCityId = data.dbCityId;

      // now that we have the saved IDs, 
      // load countries, states & cities for the saved data
      this._dataService.getCountries()
        .then(data => {
          this.countriesToShow = data;
          // now that the data binding to the control is complete
          // we can do the patching
          this.guestForm.patchValue({
            country: this.dbCountryId
          });
        });

      this._dataService.getStates(this.dbCountryId)
        .then(data => {
          this.statesToShow = data;
          // now that the data binding to the control is complete
          // we can do the patching
          this.guestForm.patchValue({
            state: this.dbStateId
          });
        });

      this._dataService.getCities(this.dbStateId)
        .then(data => {
          this.citiesToShow = data;
          // now that the data binding to the control is complete
          // we can do the patching
          this.guestForm.patchValue({
            city: this.dbCityId
          });
        });
    })
}

ngOnInit中可以直接调用该函数来加载之前保存的数据。此外,当 countriesstates 选择之一发生变化时,我们需要从服务器加载数据。

onChangeCountry(value: number) {
  // set the data source for the STATES drop-down
  this._dataService.getStates(value)
    .then(data => {
      this.statesToShow = data;
    });
  // display placeholders for STATES and CITIES until the user
  // selects the values
  this.guestForm.patchValue({
    state: "Please select state !",
    city: "Please select city !"
  });
}

编辑 2: 为了解决 required 验证的问题,我建议使用自定义验证器:

import {AbstractControl, ValidatorFn} from '@angular/forms';

export function selectIsRequired(): ValidatorFn {  
    return (control: AbstractControl): { [key: string]: any } | null =>  {
      console.log("validator:", control.value);
        return control.value === 'Select Country' 
                || control.value === 'Select State'
                || control.value === 'Select City'
                || isNaN(parseInt(control.value))
            ? {required: control.value} : null;
    }
}

这将像这样应用于 select 控件:

this.guestForm = this._fb.group({
      country: ['Select country', selectIsRequired()],
      state: ['Select state', selectIsRequired()],
      city: ['Select city', selectIsRequired()]
    });