'ANGULAR push element in formArray no rendering

I have a problem, as you will see in the images, when I add a new row that executes the add row function it creates a visual bug of the previous row, the autocoplete component in the html takes care of adding the options to the select and autocompletes what you write inside with a direct search to the db, below I report the images of the html bug of the component, file.ts and the servece form which is an additional ts for the final render, so after pressing the confirm button, what could be the problem? or a possible resolution without touching the compentee custom autocomplete component? enter image description hereenter image description hereenter image description hereenter image description hereenter image description here

esposizione-servizio.component.html

    <!-- Esposizione del servizio -->
<div [formGroup]="servizioForm">
  <h2 class="h5 text-uppercase">
    {{ 'servizio.esposizione.title' | translate }}
    <em  *ngIf="f.esposizioneServizi.value.length == 0" [attr.id]="alertEsposizione" class="text-primary fa fa-exclamation-triangle cursor-pointer ml-2" title="Campo obbligatorio" appTippy></em>
  </h2>
    <!-- modalita visualizza -->
    <div *ngIf="false == panels.panel.esposizioneServizi.inEdit" class="border" [attr.id]="esposizione" title="Punto di accesso, Area e Sezione in cui il servizio è esposto" appTippy>
      <app-btn-modifica
      [userInfo]="userInfo"
      [panels]="panels"
      [panelName]="'esposizioneServizi'"
      (btnClick)="setEdit($event,true)"
    ></app-btn-modifica>
      <div *ngIf="f.esposizioneServizi.length > 0" class="table-responsive">
        <div class="pb-4 pb-5 pl-3 pr-3">
        <table class="table">
          <caption class="sr-only">{{ 'servizio.esposizione.caption' | translate }}</caption>
          <thead>
            <tr>
            <!-- Primo campo nome punto di accesso -->
            <th scope="col">{{ 'servizio.esposizione.puntoAccesso' | translate }}</th>
            <!-- secondo campo nome area -->
            <th scope="col">{{ 'servizio.esposizione.Area' | translate }}</th>
            <!-- Terzo campo nome sezione-->
            <th scope="col">{{ 'servizio.esposizione.Sezione' | translate }}</th>
            </tr>
          </thead>
          <!-- iterazione sugli elementi -->
          <tbody *ngFor="let esp of f.esposizioneServizi.value">
            <tr>
            <td>{{esp.puntoAccesso.nome}}</td>
            <td>{{esp.area.nome}}</td>
            <td>{{esp.sezione.nome}}</td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
     <!-- Nessuna tripla -->
      <p *ngIf="f.esposizioneServizi.length == 0" class="pr-4 pb-5 pl-4" [ngClass]="{'p-top-10': userInfo.data.ruolo == RUOLI.read}">
          <em>{{ 'servizio.esposizione.nessunModello' | translate }}</em>
      </p>
  </div>
   <!-- Modalita modifica -->
   <div *ngIf="true == panels.panel.esposizioneServizi.inEdit" class="pt-4 pr-4 pl-4 border">
    <!-- Tabella mapping presenti -->
      <div *ngIf="f.esposizioneServizi.length > 0" class="table-responsive">
        <table class="table ">
          <caption class="sr-only">{{ 'servizio.esposizione.caption' | translate }}</caption>
          <thead>
            <tr>
            <th scope="col">{{ 'servizio.esposizione.puntoAccesso' | translate }}</th>
            <th scope="col">{{ 'servizio.esposizione.Area' | translate }}</th>
            <th scope="col">{{ 'servizio.esposizione.Sezione' | translate }}</th>
            <th scope="col">
              <span class="sr-only">{{ 'servizio.esposizione.elimina' | translate }}</span>
            </th>
            </tr>
          </thead>
          <tbody *ngFor="let m of this.servizioForm['controls']['esposizioneServizi']['controls']; let $index = index">
            <!-- Autocomplete per ogni mapping-->
            <tr>
            <td>
              <!-- Autocomplete punto di accesso -->
              <autocomplete-component
                [rowId] = "$index"
                [ident]="'puntoAccesso' + $index"
                [label]="'Punto di accesso:'"
                [settings]="apiUrl + '/search/punto-di-accesso'"
                [selectionAttribute]="'puntoAccesso'"
                [language]="'it'"
                [minimumInputLength]="2"
                [allowClear]="false"
                [processResults]="processResults"
                [valid]="panels.getError(m['controls']['puntoAccesso']) == ''"
                [validFormat]="panels.getError(m['controls']['puntoAccesso']) == ''"
                [errorMessage]="panels.getError(m['controls']['puntoAccesso'])"
                (subscription)="setVal($event, m['controls']['puntoAccesso'], 'nome', m['controls'])"
                [headerParams]="headerParams"
              >
              </autocomplete-component>
            </td>
            <td>
              <!-- Autocomplete Area -->
              <autocomplete-component
                [rowId] = "$index"
                [ident]="'area' + $index"
                [label]="'Area:'"
                [settings]="apiUrl + '/search/area'"
                [selectionAttribute]="'area'"
                [language]="'it'"
                [minimumInputLength]="2"
                [allowClear]="false"
                [processResults]="processResults"
                [valid]="panels.getError(m['controls']['area']) == ''"
                [validFormat]="panels.getError(m['controls']['area']) == ''"
                [errorMessage]="panels.getError(m['controls']['area'])"
                (subscription)="setVal($event, m['controls']['area'], 'nome', m['controls'])"
                [headerParams]="headerParams"
              >
              </autocomplete-component>
            </td>
            <td>
              <!-- autocomplete Sezione -->
              <autocomplete-component
                [rowId] = "$index"
                [ident]="'sezione' + $index"
                [label]="'Sezione:'"
                [settings]="apiUrl + '/search/sezione'"
                [selectionAttribute]="'sezione'"
                [language]="'it'"
                [minimumInputLength]="2"
                [allowClear]="false"
                [processResults]="processResults"
                [valid]="panels.getError(m['controls']['sezione']) == ''"
                [validFormat]="panels.getError(m['controls']['sezione']) == ''"
                [errorMessage]="panels.getError(m['controls']['sezione'])"
                (subscription)="setVal($event, m['controls']['sezione'], 'nome', m['controls'])"
                [headerParams]="headerParams"
              >
              </autocomplete-component>
            </td>
            <td class="pb-3 text-right align-bottom">
              <!-- Botton elimina -->
              <button-component
                [aColor]="'lightblue'"
                [aText]="'Elimina'"
                (clickButton)="removeRow($index, f.esposizioneServizi)">
              </button-component>
            </td>
            </tr>
          </tbody>
        </table>
      </div>
      <!-- Messaggio di mapping assenti -->
      <p *ngIf="f.esposizioneServizi.length == 0">
        <em>{{ 'servizio.esposizione.nessunModello' | translate }}</em>
      </p>
      <!-- Aggiungi modello -->
      <p class="mt-3 text-right">
        <button-component
          [aColor]="'default'"
          [aText]="'Aggiungi esposizione'"
          (clickButton)="addRow(f.esposizioneServizi)"
        >
        </button-component>
      </p>
      <!-- Salva modello -->
      <p class="mt-5">
        <button-component
          [aColor]="'lightblue'"
          [aText]="'Conferma'"
          [isDisabled]="f.esposizioneServizi.invalid"
          (clickButton)="panels.patch('esposizioneServizi', true)"
        >
        </button-component>
        &nbsp;
        <!-- Annulla modifiche -->
        <button-component
          [aColor]="'default'"
          [aText]="'Annulla'"
          (clickButton)="panels.resume('esposizioneServizi')"
        >
        </button-component>
      </p>
  </div>
</div>

esposizione-servizio.component.ts

import {ChangeDetectorRef, Component, Input} from '@angular/core';
import {FormArray, FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms';
import {ResourcesService} from '@services/resources/resources.service';
import {RUOLI, servizi} from '../../../../model/Environment';
import {MessageService} from '@services/messages/message.service';
import {AutocompleteHelper} from 'noipa-kituxui/ngx'
import {AppConfigService} from '@services/app-config/app-config.service';


@Component({
    selector: 'app-esposizione-servizio',
    templateUrl: './esposizione-servizio.component.html',
    styleUrls: ['./esposizione-servizio.component.css']
})
export class EsposizioneServizioComponent {

    //costante per verificare se il ruolo è di sola lettura o lettura e scrittura
    readonly RUOLI = RUOLI

    /*
     Informazioni utente ricevute da parent
    */

    @Input() userInfo: any

    /*
     Helpers ricevuti da parent
    */

    @Input() panels: any

    /*
     Elenchi dati condivisi con parent
    */

    @Input() lists: any

    /*
     Dati del servizio ricevuti da parent
    */

    @Input() servizio: any

    /*
     Oggetto form del servizio ricevuto da parent
    */

    @Input() servizioForm: FormGroup

    /*
    Struttura ausiliaria per la memorizzazione dei dati
   */

    data: any = []

    /*
   Headers per request nelle chiamate di autocomplete
  */

    headerParams: any

    /*
    Url per request nelle chiamate di autocomplete
   */

    apiUrl: string

    tipologia: any

    /*
    Id progressivo fake per la generazione di oggetti custom
   */

    idCustom = 0

    //variabili per la gestione dei tooltip
    public esposizione = 'esposizione';
    public alertEsposizione = 'alertEsposizione';


    /*
     Alias per l'oggetto form del servizio
    */

    get f() {
        return this.servizioForm['controls']
    }

    /*
  Override - Attiva/disattiva la modifica del pannello mappings
 */

    setEdit(panel, value) {
        this.panels.setEdit(panel, value)
        /*
         Inizializza l'autocomplete sui campi di mapping
        */
        this.f['esposizioneServizi'].value.forEach((mapping, index) => {

            new AutocompleteHelper().select('puntoAccesso' + index, mapping.puntoAccesso.nome)
            new AutocompleteHelper().select('area' + index, mapping.area.nome)
            new AutocompleteHelper().select('sezione' + index, mapping.sezione.nome)
        })
    }

    /*
     Rimuove un mapping dalla lista
     index: indice ordinale
     fa: oggetto form
    */

    removeRow(index, fa: FormArray) {
        fa.removeAt(index)
        this.servizioForm.markAsDirty()
        this.servizioForm.markAsTouched()
    }

    /*
     Aggiunge una riga vuota all'oggetto form mappings
      integrando la validazione sui campi
    */

    addRow(fa: FormArray) {
        fa.push(
            this.formBuilder.group({

                id: ['', []],
                puntoAccesso: this.formBuilder.group({
                    nome: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(2000)]],
                    id: ['', []]
                }),
                area: this.formBuilder.group({
                    nome: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(2000)]],
                    id: ['', []]
                }),
                sezione: this.formBuilder.group({
                    nome: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(2000)]],
                    id: ['', []]
                })
            })
        )

    }

    /*
    Elabora la lista dei risultati ricevuta da servizio di autocomplete
    Vale per puntoAccesso, area, sezione
   */

    processResults = (data, params) => {
        /*
         Rielabora la lista ritornata da servizio
        */

        let list = {
            results: data.map(item => {
                return {id: item.id, text: item.nome}
            })
        }

        /*
         Se il valore non è presente, ne crea uno custom
        */

        if (list.results.filter(l => (l.text || '').toUpperCase() == params.term.toUpperCase()).length == 0)
            list.results.push({id: '$' + (++this.idCustom), text: params.term})

        /*
         Elimina i duplicati dalla lista
        */

        list.results = list.results.filter(item => (new RegExp(params.term, 'i')).test(item.text))

        /*
         Ordina i valori per nome
        */

        list.results.sort((e1, e2) => e1.text.localeCompare(e2.text))

        /*
         Resituisce la lista rielaborata per l'autocomplete
        */

        return list
    }


    /*
     Imposta il valore del model sull'onChange del campo
     event: il valore
     fc: oggetto form
     field: nome del campo (puntoAccesso, area, sezione)
     fg: oggetto form del campo
    */

    setVal(event, fc: FormControl, field, fg) {
        const {id} = event.$event
        const {text} = event.$event
        const {rowId} = event.$event


        let value: any = {}

        /*
         Ripulisce l'id se il valore è custom
        */

        value.id = /^\$/.test(id) ? '' : id

        /*
         Assegna il valore
        */

        value[field] = text

        /*
         Invia il valore al servizio
        */

        fc.patchValue(value)

        /*
           Ripulisce il campo
          */
        fg['id'].setValue('')

        this.servizioForm.markAsDirty()
        this.servizioForm.markAsTouched()
        new AutocompleteHelper().select(field + rowId, text)
    }
    constructor(
        private appConfig: AppConfigService,
        private formBuilder: FormBuilder,
        private messageService: MessageService,
        private resourcesService: ResourcesService
    ) {
        const config = this.appConfig.getConfig()
        this.apiUrl = config[servizi].apiUrl
    }

  ngOnInit() {
      /*
     Riceve l'headers per l'invocazione del servizio di autocomplete
    */
     this.headerParams = this.resourcesService.getHeaders(servizi)

  }

}


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source