angularangular-materialmat-autocomplete

mat-autocomplete with filter and selected items in mat-table not working


I am new to angular and angular material. I'm using a tutorial to do a small project where I have an angular material autocomplete to filter actors and when I click on one of them, it should be displayed in a mat-table with drag drop feature. So, from the menu - first click on "create movie" to view the actor autocomplete(there's a mat-table to which the selected actor is added). I am not using any database for this feature of actors-autocomplete but using the hard coded actors in the code. But the actors in the autocomplete are not added to the mat-table after I select on it and click on it. But in the browser console, a row is added after I select an actor from the autocomplete but there's no error - Row added on inspection

What am I missing here. Entire github code in stackblitz is available in the link below.

My entire project is available in stackblitz Project link

create-movie.component.html

        <h2>Create Movie</h2>

    <h4>Select the Actors</h4>
    <app-actors-autocomplete></app-actors-autocomplete>

create-movie.component.ts

      import { Component, OnInit } from '@angular/core';
  import { multipleSelectorModel } from '../../utilities/multiple-selector/multiple-selector.model';
  import { MoviesService } from '../movies.service';
  import { movieCreationDTO } from '../movies.model';
  import { map } from 'rxjs/operators';

  @Component({
    selector: 'app-create-movie',
    templateUrl: './create-movie.component.html',
    styleUrls: ['./create-movie.component.css'],
  })
  export class CreateMovieComponent implements OnInit {
    constructor() {}

   
    ngOnInit(): void {}

    saveChanges(event: Event): void {}
  }

actors-autocomplete.component.html

            <form>
        <mat-form-field>
            <input
            type="text"
            placeholder="Select the actors"
            matInput
            [formControl]="control"
            [matAutocomplete]="auto"
            />
        </mat-form-field>

        <mat-autocomplete #auto (optionSelected)="optionSelected($event)">
            <mat-option *ngFor="let actor of actorsToDisplay" [value]="actor">
            <img [src]="actor.picture" /> {{ actor.name }}
            </mat-option>
        </mat-autocomplete>
        </form>

        <table
        *ngIf="selectedActors.length > 0"
        mat-table
        [dataSource]="selectedActors"
        cdkDropList
        [cdkDropListData]="selectedActors"
        (cdkDropListDropped)="dropped($event)"
        >
        <ng-container matColumnDef="picture">
            <td mat-cell *matCellDef="let element">
            <img [src]="element.picture" style="width: 50px" />
            </td>
        </ng-container>

        <ng-container matColumnDef="name">
            <td mat-cell *matCellDef="let element">
            {{ element.name }}
            </td>
        </ng-container>
        <ng-container matColumnDef="character">
            <td mat-cell *matCellDef="let element">
            <mat-form-field appearance="outline" style="margin-top: 10px;">
                <mat-label>Character</mat-label>
                <input matInput [(ngModel)]="element.character" />
            </mat-form-field>
            </td>
        </ng-container>
        <ng-container matColumnDef="actions">
            <td mat-cell *matCellDef="let element">
            <mat-icon mat-list-icon (click)="remove(element)">close</mat-icon>
            </td>
        </ng-container>
        <tr
            mat-row
            *matRowDef="let row; columns: columnsToDisplay"
            cdkDrag
            [cdkDragData]="row"
        ></tr>
        </table>

actors-autocomplete.component.ts

        import { actorsMovieDTO } from './../actors.model';
    import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
    import { analyzeAndValidateNgModules } from '@angular/compiler';
    import { Component, Input, OnInit, ViewChild } from '@angular/core';
    import { FormControl } from '@angular/forms';
    import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
    import { MatTable } from '@angular/material/table';
    import { ActorsService } from '../actors.service';

    @Component({
      selector: 'app-actors-autocomplete',
      templateUrl: './actors-autocomplete.component.html',
      styleUrls: ['./actors-autocomplete.component.css'],
    })
    export class ActorsAutocompleteComponent implements OnInit {
      constructor(private actorService: ActorsService) {}
      control: FormControl = new FormControl();

      // @Input()
      // selectedActors : []= [];
      // actorsToDisplay:actorsMovieDTO[]=[];
      actorsToDisplay = [
        {
          name: 'Tom Holland',
          picture:
            'https://moviesapis.blob.core.windows.net/actors/bb1533c8-fe83-4145-8f8f-d1535afbe01e.jpg',
          character: '',
        },
        {
          name: 'Chris Hemsworth',
          picture:
            'https://moviesapis.blob.core.windows.net/actors/f39dd82f-5b41-4d54-887b-8430500bb0e5.jpg',
          character: '',
        },
        {
          name: 'Samuel L Jackson',
          picture:
            'https://moviesapis.blob.core.windows.net/actors/1d99e4df-dfa5-4dde-9b4b-2d197f5fb116.jpg',
          character: '',
        },
        {
          name: 'Chris Evans',
          picture:
            'https://moviesapis.blob.core.windows.net/actors/346b6791-6b61-42b7-8a57-da136463217e.jpg',
          character: '',
        },
      ];

      selectedActors: any[]=[];

      originalActors = this.actorsToDisplay;
      columnsToDisplay!: ['picture', 'name', 'character', 'actions'];
      @ViewChild(MatTable) table: MatTable<any>;

      ngOnInit(): void {
        this.control.valueChanges.subscribe((value) => {
          this.actorsToDisplay = this.originalActors;
          this.actorsToDisplay = this.actorsToDisplay.filter(
            (actor) => actor.name.indexOf(value) !== -1
          );
        });
      }

      optionSelected(event: MatAutocompleteSelectedEvent) {
        console.log(event.option.value);

        this.selectedActors.push(event.option.value);
        this.control.patchValue('');

        // this.control.patchValue('');
        if (this.table !== undefined) {
          this.table.renderRows();
        }
      }

      remove(actor) {
        const index = this.selectedActors.findIndex((a) => a.name === actor.name);
        this.selectedActors.splice(index, 1);
        this.table.renderRows();
      }

      dropped(event: CdkDragDrop<any[]>) {
        const previousIndex = this.selectedActors.findIndex(
          (actor) => actor === event.item.data
        );
        moveItemInArray(this.selectedActors, previousIndex, event.currentIndex);
        this.table.renderRows();
      }
    }

actors-autocomplete.component.css

            form{
            width:100%;
            max-width: 500px;
        }

        mat-form-field{
            width: 100%;
        }
        img{
        vertical-align: middle;
        margin-right: 8px;
        width: 35px;  
        height: 45px;
        }
        table{
            width: 100%;
            margin-bottom: 1rem;
        }

        mat-icon{
            cursor: pointer;
        }

Solution

  • The issue is with the below property declaration in actors-autocomplete.component.ts:

        columnsToDisplay!: ['picture', 'name', 'character', 'actions'];
    

    It should be initialized with = as:

        columnsToDisplay = ['picture', 'name', 'character', 'actions'];