Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 5 / Material 2 - Wrong value in field after option is selected

I'm facing a problem with my Angular Material 2 Autocomplete fields.

This is my setup:

hardwareCreate.component.ts

myControl: FormControl = new FormControl();
availableFirmware = [];
filteredFirmware: Observable<any[]>;
selectedFirmware = null;
selectedFirmwareName = '';



this.availableFirmware = [];

    this.terminalService.getFirmware().subscribe(firmware => {
      this.availableFirmware = firmware.firmware;
    });
    this.filteredFirmware = this.myControl.valueChanges
    .pipe(
      startWith(''),
      map(val => this.filterFirmware(val))
    );

filterFirmware(val: any): any[] {
    return this.availableFirmware.filter(firmware => {
      return firmware.name.toLowerCase().indexOf(val.toLowerCase()) > -1;
    });
  }

hardwareCreate.component.html

<div class="form-group">
        <mat-form-field class="example-full-width">
            <input type="text" placeholder="Firmware auswählen" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto1" [(ngModel)]="selectedFirmwareName">
            <mat-autocomplete #auto1="matAutocomplete">
                <mat-option *ngFor="let firmware of filteredFirmware | async" [value]="firmware._id">
                    {{ firmware.name }}
                </mat-option>
            </mat-autocomplete>
        </mat-form-field>
    </div>

So my problem now is, that when I type I get the firmware.name attribute which is correct and looks like this: enter image description here

But when I now select a firmware, the value changes to the _id of firmware. enter image description here

So I could change [value]="firmware._id" to [value]="firmware.name" but I need the ID for my mongodb ->

firmware: {
        type: mongoose.Schema.Types.ObjectId, 
        ref: 'Firmware'
    },

Question: How can I change the display value to the name, but still get the Id for my Database when the user selects a specific firmware?


The solution now is a combination of both of vsoni and JEYs answers. The problem at the end was, that val was an object. By converting it to a string, anything works like a charme! Thanks to both of you!

like image 751
Sithys Avatar asked Jan 19 '18 11:01

Sithys


2 Answers

You can use displayWith function.

Your component.html would become

<div class="form-group">
    <mat-form-field class="example-full-width">
        <input type="text" placeholder="Firmware auswählen" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto1">
        <mat-autocomplete #auto1="matAutocomplete" [displayWith]="displayFn">
            <mat-option *ngFor="let firmware of filteredFirmware | async" [value]="firmware" (onSelectionChange)="getXXX(firmware)>
                {{ firmware.name }}
            </mat-option>
        </mat-autocomplete>
    </mat-form-field>
</div>

Then define following display function in you component.ts

displayFn(firmware: any): string {
  return firmware? firmware.name : firmware;
}

You can access firmware id in getXXX(firmware) function which you define in your component.ts. This function will be called on selection change.

getXXX(firmware) {
   this.selectedFirmware = firmware;
   // here you can get id 
  // firmware._id
}

and filter function

filterFirmware(val: any): any[] {
    let name = val.name ? val.name : val;
    return this.availableFirmware.filter(firmware => {
      return firmware.name.toLowerCase().indexOf(name.toLowerCase()) > -1;
    });
  }
like image 144
vsoni Avatar answered Sep 27 '22 20:09

vsoni


You can use the displayWith attribute like:

In your component:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import { map } from 'rxjs/operators/map';
import {startWith} from 'rxjs/operators/startWith';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  implements OnInit, OnDestroy {
  
  myControl = new FormControl();
  availableFirmware = [{name: 'Firmware 1', id: 1}, {name: 'Firmware 2', id: 2}, {name: 'Firmware 3', id: 3}];
  selected = null;
  filteredFirmware: Observable<any>;
  subcribtion: Subscription;

  displayFirmware(firmware?: any) {
    return firmware ? firmware.name : undefined;
  }

  filterFirmware(val: any): any[] {
    return this.availableFirmware.filter(firmware => {
      // when no selection occured value is a string
      // but once a firmware is selected value is an object
      let name = val.name ? val.name : val;
      return firmware.name.toLowerCase().indexOf(name.toLowerCase()) === 0;
    });
  }

  ngOnInit() {
    this.subcribtion = this.myControl.valueChanges.subscribe(value => this.selected = value);
    this.filteredFirmware = this.myControl.valueChanges.pipe(
      startWith(''),
      map(val => this.filterFirmware(val))
    );

  }

  ngOnDestroy() {
    this.subcribtion.unsubscribe();
  }
}

In the template:

<div class="form-group">
    <mat-form-field class="example-full-width">
        <input type="text" placeholder="Firmware auswählen" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto1">
        <mat-autocomplete #auto1="matAutocomplete" [displayWith]="displayFirmware">
            <mat-option *ngFor="let firmware of filteredFirmware | async" [value]="firmware">
                {{ firmware.name }}
            </mat-option>
        </mat-autocomplete>
    </mat-form-field>
</div>

<p>
  {{selected | json}}
</p>

In this solution the value is the whole firmware object so you can retrieve anything from it.

You can find a running example here https://stackblitz.com/edit/angular-ptg4i1

like image 39
JEY Avatar answered Sep 27 '22 19:09

JEY