How to remove the previously selected option from a drop-down menu in a table?

问题: I am making a project on angular 7.It has a table with a column having dropdowns. The dropdown contains various languages. When a language is selected in a particular row,...

问题:

I am making a project on angular 7.It has a table with a column having dropdowns. The dropdown contains various languages. When a language is selected in a particular row, then it shouldn't appear in the dropdown in the subsequent row. How do I do that?

I tried deleting the selected language from the array using splice().But as it deletes the object, it is also not shown in the dropdown.

Following is the html -(it is the row of the table that defines the dropdown, and this row is dynamic)

 <tr *ngFor="let field of fieldArray;let i = index">
  <td><button class="btn" (click)="deleteFieldValue(i)"><i class="fa fa-trash"></i></button></td>
  <td class="select">
    <select #selectLang (change)="selected(selectLang.value,i)">
      <option value="undefined" disabled>Select Language</option>
      <option *ngFor="let lang of languageList" value={{lang.name}} [ngValue]="lang.name">{{lang.name}}</option>
    </select>
  </td>
  <td>
    <input id="fileUpload" name="fileUpload" type="file" name="upload_file" (change)=onFileChange($event)>
  </td>
</tr>

following is the typescript code -

languageList = [{'name': "Dothraki"},{'name': "Japanese"}, 
{'name':"German"},{'name':"French"},{'name': "Spanish"}, {'name': 
"Russian"}, {'name': "Italian"}];
  selectedLang;
  optionLang:string;
  fieldArray: Array<any> = [];
  newAttribute: any = {};
  fileUploadName: any;
  selected(lang:string,index:number){
    console.log(lang);

    // this.languageList.splice(index, 1);
    for(let i =0; i< this.languageList.length; i++) {
      if(this.languageList[i]['name'] === lang) {
        this.languageList.splice(i,1);
        break;
      }
    }
  }
  addFieldValue() {
    this.fieldArray.push("hg");
    this.newAttribute = {};
  }
  deleteFieldValue(index: number) {
    this.fieldArray.splice(index, 1);
}
  openFileBrowser(event:any){
    event.preventDefault();
    let element: HTMLElement = document.getElementById('fileUpload') as 
HTMLElement;
    element.click();
  }

  onFileChange(event:any){
    let files = event.target.files;
    this.fileUploadName = files[0].name;
    console.log(files);
  }

回答1:

you can solve the problem by holding a set of selected languages and display options conditionally based on whether an option/language is selected before or not.

create a Set to hold selected langs

selectedLangs = new Set<string>();

create a view query to get a list of all select elements

@ViewChildren("selectLang") langSelects: QueryList<ElementRef<HTMLSelectElement>>;

whenever a selection is made/changed on any of the select elements re-populate the selectedLangs set

  selected() {
    this.selectedLangs.clear();
    this.langSelects.forEach(ls => {
      const selectedVal = ls.nativeElement.value;
      if (selectedVal && selectedVal !== "undefined") this.selectedLangs.add(selectedVal);
    });
 }

whenever a field is deleted just remove that language from selectedLangs

  deleteFieldValue(index: number, lang: string) {
    this.selectedLangs.delete(lang);
    this.fieldArray.splice(index, 1);
  }

and when displaying options for a select check if it is currently selected on current select or already selected in another select *ngIf="selectLang.value === lang.name || !isSelected(lang.name)"

<ng-container *ngFor="let lang of languageList" >
  <option *ngIf="selectLang.value === lang.name || !isSelected(lang.name)" value={{lang.name}} [ngValue]="lang.name">
      {{lang.name}}
  </option>
</ng-container>

where isSelected is defined as

  isSelected(lang: string) {
    return this.selectedLangs.has(lang);
  }

here is a working demo with full source https://stackblitz.com/edit/angular-dqvvf5


回答2:

You can store the langs in an array make a function like

  lang = []; //define the array
  getLang(i, languageList) {
    return i == 0 ? languageList :
      this.getLang(i - 1, languageList.filter(x => x.name != this.lang[i-1]))
  }

So, you can has some like

<div *ngFor="let a of languageList;let i=index">
    <select  [(ngModel)]="lang[i]">
      <option value="undefined" disabled>Select Language</option>
      <option *ngFor="let lang of getLang(i,languageList)" 
          [value]="lang.name" >{{lang.name}}</option>
    </select>
</div>

But I don't like because each change force Angular to calculate all the options. So we are going to improve the code using FormArray and an array langList, and make sure that we can not choose the same language

First our variable and our function changed

  langList=[];
  getLangForFormArray(i, languageList) {
    return i == 0 ? languageList :
      this.getLang(i - 1, this.langList[i-1].filter(x => x.name != this.formArray.value[i-1]))
  }

We create a formArray

  formArray=new FormArray(this.languageList.map(()=>new FormControl(null)))

And in ngOnInit

  ngOnInit()
  {
    this.formArray.valueChanges.pipe(startWith(null)).subscribe(()=>{
      //create the langList array
      for (let i=0;i<this.languageList.length;i++)
         this.langList[i]=this.getLangForFormArray(i,this.languageList)

      //check no repeat values
      if (value)
      {
         value.forEach((x,index)=>{
         if (this.formArray.value.findIndex(v=>v==x)!=index)
             this.formArray.at(index).setValue(null,{emitEvent:false})
         })
      }
    })
  }

See that use formArray valueChanges with pipe(startWith(null)) to create at first the langList

The .html

<div *ngFor="let control of formArray.controls;let i=index">
    <select [formControl]="control">
      <option value="null" disabled>Select Language</option>
      <option *ngFor="let lang of langList[i]" 
          [value]="lang.name" >{{lang.name}}</option>
    </select>
</div>

And the demo in stackblitz

  • 发表于 2019-07-08 00:30
  • 阅读 ( 167 )
  • 分类:sof

条评论

请先 登录 后评论
不写代码的码农
小编

篇文章

作家榜 »

  1. 小编 文章
返回顶部
部分文章转自于网络,若有侵权请联系我们删除