kyserslick kyserslick - 2 months ago 10
TypeScript Question

Adding a new and removing previously selected value using ngModelChange

I have a select dropdowd on a row colomn and I'm trying to add a new row with a new select box with the previously selected value removed. I used ngModelChange to call a function that assigns the value selected to my ngModel and removes it from the selection list. The problem is everytime I add a new row the value is removed from all rows.

NB: I was unable to create a plunker for my project

my grid look like this:

<tbody>
<tr *ngFor="let row of rows">
<td></td>
<td>
<select [ngModel]="row.color" (ngModelChange)="onSelectColor(row, $event)"> // value is a string or number
<option *ngFor="let obj of availableColors">{{obj}}</option>
</select>
</td>
<td *ngFor="let headItem of headList"><input class="input" type='text' #qty/></td>
</tr>
</tbody>
<button (click)='addRow()'>Add a row</button>


my componant I have this:

export class ReferenceTableComponent implements OnInit {

observable: Observable<any>;

//couleurs exp: ['first', 'seconde']
@Input() public datas: Array<string>;

public availableColors: Array<string>;

//exp: ['1' ,'2', '3']
@Input() public headList: Array<string>;


public rows: Array<any> = [{}];

public rowIDs: Array<any> = [];

constructor(private injector: Injector) {
}

ngOnInit() {
this.computeAvailableColors();
}

addRow() {

this.rows.push({});
}

computeAvailableColors() {
this.availableColors = _.concat([''], this.datas);
console.log(_.map(this.rows, 'color'))
this.rowIDs = _.map(this.rows, 'color')
this.availableColors = _.difference(this.availableColors, this.rowIDs);
}

onSelectColor(row, color) {
row.color = color;
this.availableColors = this.availableColors.filter(c => c !== color);
}

}


datas and headlist are injected by my service

enter image description here
enter image description here

As you can see the 1st selected color 'BLANC NEIG' was removed from the selection as desired but also from the first row

Answer Source

I do not really understand what you want to achieve. However If you want to remove the select color from the available colors you just have to something like this:

 public onSelectColor(row, color) {
    row.color = color;
    this.availableColors = this.availableColors.filter(c => c !== color);
  }

That will change all select elements rerendering them without the removed color.

If you don not want to remove the color from all the existing selects, then for each select you should have a different array of available colors. I would make the row a angular component itself.

 constructor(private _cr: ComponentFactoryResolver, private _viewContainerRef: ViewContainerRef) { };
 public onSelectColor(row, color) {
        //workout the new colors
        const newAvailableColors = this.availableColors.filter(c => c !== color);
        // Create new row component on the fly   
        const row: ComponentRef<RowComponent> = 
          this._viewContainerRef.createComponent(
           this._cr.resolveComponentFactory(RowComponent)
          );
        //Add whatever you want to the component
        row.instance.addColors(newAvailableColors);
        row.instance.addSelectedColor(color);
      }

The child component should emit an event when that select changes. So the child should have something like this:

@Output()
public changed = new EventEmitter();

and then when select has change emit the event with the color as you say in the comment. Something like this:

this.changed.emit({ color: color, row: row });

And then the parent, when is placing the child component in the html will be able to catch the event. Something like that:

(changed)="onSelectColor($event)"

Obviously, the onSelectColor has to change its arguments. Now it should be accepting the event. (Create an interface for the event, instead of using any)

public onSelectColor(event: any) {
   //Do the same but retrieving info from the event 
event.color;
event.row;
}