0
votes

I'm working on a Angluar 7 project with Angular-Material and I'm using reactive forms the first time. I've implemented an AutoComplete so the user can select an employee from a list of EmployeeObjects. The form uses Angular reactive forms. The objects of the employees are much more complex than the user needs to know.

The selection of an employee and the storing and updating of the reactive form works fine. The problem is that the shown value in the input field looks like [object: object], because the whole object will set to the [value] of the input field.

When I implement a getter to show only the employee name the shown value looks correct, but the reactive form value only stores the formatted string instead of the whole object.

How is it possible to do both, show the correct string to the user and store the whole value in the form?

Thanks for your help.

Stackblitz

https://stackblitz.com/edit/angular-autocomplete-employee?file=app%2Fautocomplete-simple-example.html

enter image description here

Class

@Component({
  selector: 'app-employee-data',
  templateUrl: './employee-data.component.html',
  styleUrls: ['./employee-data.component.scss']
})
export class EmployeeDataComponent implements OnInit {

  employeeForm: FormGroup;
  employeeCtrl = new FormControl();
  employees: Employee[] = EmployeeList as Employee[];
  filteredEmployees: Observable<Employee[]>;
  result;
  @Input() employee: Employee;
  @ViewChild(MatAutocomplete) matAutocomplete: MatAutocomplete;

  constructor(private formBuilder: FormBuilder) {
    this.initEmployeeFilters(this.employeeCtrl);
    this.employeeForm = this.createFormGroupWithBuilder(formBuilder);
  }

  ngOnInit() {
  }

  private _filterStates(value: string): Employee[] {
    const filterValue = value.toLowerCase();

    return this.employees.filter(employee => employee.lastname.toLowerCase().indexOf(filterValue) === 0);
  }

  public getFullname(employee: Employee): string {
    return employee.lastname + ', ' + employee.firstname;
  }

  public getEmployee(): Employee {
    return JOHN_DOE;
  }

  private initEmployeeFilters(formCtrl: FormControl) {
    this.filteredEmployees = formCtrl.valueChanges
      .pipe(
        startWith(''),
        map(employee => employee ? this._filterStates(employee) : this.employees.slice())
      );
  }

  createFormGroupWithBuilder(formBuilder: FormBuilder) {
    return formBuilder.group({
      employeeData: formBuilder.group({
        employee: new FormControl(),
      }),
    });
  }

  revert() {
    this.employeeForm.reset();
    this.employeeForm.reset({ employeeData: new Employee() });
  }

  onSubmit() {
    this.result = Object.assign({}, this.employeeForm.value);
    this.result.employeeData = Object.assign({}, this.result.employeeData);
  }
}

Template

<form [formGroup]=„employeeForm" (ngSubmit)="onSubmit()" novalidate>
  <div formGroupName="employeeData" novalidate>
      <mat-form-field>
        <input
          matInput
          type="text"
          placeholder="Name"
          [matAutocomplete]="auto"
          formControlName="name"
        >
        <mat-autocomplete #auto="matAutocomplete">
          <mat-option
            *ngFor="let employee of employees | async"
            [value]="getEmployee(employee)"
          >
            <img
              [src]="employee.profilePicture"
              height="25"
            >
            <span>{{employee.lastname}}, {{employee.firstname}}</span>
          </mat-option>
        </mat-autocomplete>
      </mat-form-field>
  </div>
  <button type="submit" [disabled]="employeeForm.pristine">Save</button>
</form>

Employee class and List of Employees

export class Employee {
  id = 1;
  lastname = '';
  firstname = '';
}

export const JANE_DOE = {
    id: 1,
    lastname: 'Doe',
    firstname: 'Jane',
    profilePicture: ‚url‘
};

export const JOHN_DOE = {
    id: 2,
    lastname: 'Doe',
    firstname: 'John',
    profilePicture: ‚url‘
};

export const EmployeeList: Employee[] = [
    JANE_DOE,
    JOHN_DOE,
];
1
What is your expected output? can you provide a screenshot? I think you can use {{value | json }} to view the responsePrashant Pimpale
The output should show the Lastname, Firstname of an employee, not the [object].kulosos
@SkyHigh No, there're no errorskulosos
@PrashantPimpale I'm already using {{ employeeForm.value | json }} to see what's in the form. But I need the string values within the input field and there I've to set the selected value, which is a object of the list.kulosos
Can you produce the StackBlitz so I can try on itPrashant Pimpale

1 Answers

3
votes

You can use the displayWith input on mat-autocomplete to accomplish this.

<mat-autocomplete #auto="matAutocomplete" [displayWith]="valueMapper">

Then in your component

public valueMapper = (key) => {
  return key.firstname + ' ' + key.lastname;
};