Elaborazione ricorsiva dei nodi dell'albero in angolare
Tempo di lettura: 6 minuti

Se li cerchi, troverai strutture ad albero dappertutto – dalla natura, alle città e ai loro quartieri, alla genealogia della tua famiglia (cioè il tuo albero genealogico). Non sorprende che gli alberi spuntino altrettanto spesso nelle strutture di dati programmatici; ad esempio, nel Document Object Model (DOM):

In linguaggi di codifica come JavaScript e Dattiloscrittogli alberi sono generalmente rappresentati utilizzando un array di array e/o oggetti le cui chiavi sono costituite da altri array e/o oggetti.

Una tecnica comune impiegata quando si lavora con le strutture ad albero è ricorsione, per cui una funzione chiama se stessa. Sebbene non sia l’unica soluzione, in molti casi è l’approccio più semplice. Suona complicato? Non temere: la ricorsione è in realtà relativamente facile da padroneggiare, una volta che ci hai lavorato un paio di volte.

A tal fine, in questa serie di tutorial sullo sviluppo web in due parti, mapperemo un albero complesso su uno più semplicistico, e poi di nuovo indietro. In questa prima puntata, tratteremo la struttura di base della funzione ricorsiva e poi la useremo per persistere l’array di Nodi Veicolo che è stato introdotto nel Tracciamento delle selezioni con caselle di controllo in angolare articolo di dicembre. Nella parte 2, ricostruiremo l’originale Nodi Veicolo albero dalle selezioni salvate.

Alberi contro array di alberi in angolare

Ai fini della revisione, ecco il Nodo Veicolo interfaccia e albero con cui lavoreremo in questa serie:

interface VehicleNode {
  name: string;
  id: number | string;
  children?: VehicleNode[];
  selected?: boolean;
  indeterminate?: boolean;
  parent?: VehicleNode;
}

const TREE_DATA: VehicleNode[] = [
  {
    name: 'Infiniti',
    children: [
      {
        name: 'G50',
        children: [
          { name: 'Pure AWD', id: 1 },
          { name: 'Luxe', id: 2 },
        ],
      },
      {
        name: 'QX50',
        children: [
          { name: 'Pure AWD', id: 3 },
          { name: 'Luxe', id: 4 },
        ],
      },
    ],
  },
  {
    name: 'BMW',
    children: [
      {
        name: '2 Series',
        children: [
          { name: 'Coupé', id: 5 },
          { name: 'Gran Coupé', id: 6 },
        ],
      },
      {
        name: '3 Series',
        children: [
          { name: 'Sedan', id: 7 },
          { name: 'PHEV', id: 8 },
        ],
      },
    ],
  },
];

Tecnicamente, tutti gli alberi hanno un tronco, vale a dire un singolo nodo radice. Nel tuo albero genealogico, quel nodo saresti tu. Nella struttura di cui sopra, quella sarebbe ogni casa automobilistica, cioè, Infiniti e BMW. Quindi, possiamo pensare alla nostra struttura dati come a un array di alberi, piuttosto che a un singolo albero. Questo dovrebbe diventare abbastanza ovvio se decomprimiamo l’array e lo ricostruiamo utilizzando gli oggetti ad albero separati:

const infiniti =  {
  name: 'Infiniti',
  children: [
    {
      name: 'G50',
      children: [
        { name: 'Pure AWD', id: 1 },
        { name: 'Luxe', id: 2 },
      ],
    },
    {
      name: 'QX50',
      children: [
        { name: 'Pure AWD', id: 3 },
        { name: 'Luxe', id: 4 },
      ],
    },
  ],
};
const bmw = {
  name: 'BMW',
  children: [
    {
      name: '2 Series',
      children: [
        { name: 'Coupé', id: 5 },
        { name: 'Gran Coupé', id: 6 },
      ],
    },
    {
      name: '3 Series',
      children: [
        { name: 'Sedan', id: 7 },
        { name: 'PHEV', id: 8 },
      ],
    },
  ],
};
const TREE_DATA: VehicleNode[] = [infiniti, bmw];

Vale la pena distinguere tra i due, poiché la loro gestione differisce leggermente.

Leggere: Manipola gli elementi dell’albero DOM senza scrivere JavaScript

Anatomia di una funzione ricorsiva in angolare

Mentre il codice esatto di una funzione ricorsiva dipenderà in gran parte da ciò per cui è progettata, nonché dalla struttura esatta dell’albero, possiamo fornire un’approssimazione rapida e libera di come potrebbe apparire una tale funzione:

function callRecursively(node: Object) {
  doSomethingWithNode;
  if (node.hasChildren) {
    node.children.forEach(childNode => {
      this. callRecursively(childNode);
    });
  }
}

Per applicare il callRecursively() funzione a un array di oggetti albero, tutto ciò che è richiesto è gestire ogni albero dell’array all’interno di un ciclo. Ecco un esempio di pseudo-codice che utilizza per ciascuno():

TREE_DATA.forEach(node => {
  callRecursively(node);
});

Leggere: Divertirsi con JavaScript FOR Loop

Salvataggio delle selezioni di nodi in Angular

Per acquisire familiarità con il processo di sviluppo di una funzione ricorsiva basata sul modello di cui sopra, eseguiremo il refactoring dell’app che abbiamo creato nel Tracciamento delle selezioni con caselle di controllo in angolare articolo in modo che le selezioni dei veicoli possano essere mantenute e richiamate da un elenco di collegamenti. Ecco come apparirà l’app finita:

Strutture ad albero angolare

Nell’immagine sopra, possiamo vedere un elenco di selezioni salvate nella parte superiore del controllo ad albero, nonché un pulsante e un campo di immissione testo per salvare le selezioni correnti. Esaminando il formato del salvato Selezioni infinitepossiamo ottenere una comprensione del formato dell’oggetto salvato:

public savedSelections: SavedSelection[] = [
 {
    name: "Infinity Selections",
    selections: [{
      id: "infiniti",
      children: [
        {
          id: "g50",
          children: [ { id: 2 } ]
        },
        { id: "qx50" }
      ]
    }]
  },
  // ...
}

La struttura dell’oggetto ricorda da vicino quella del DATI_ALBEROma con un paio di differenze fondamentali:

  1. La struttura complessiva si basa sul Selezione salvata interfaccia:
    interface SavedSelection {
      name: string;
      selections: VehicleSelection[];
    }
    
  2. Le selezioni vengono memorizzate come una matrice di oggetti basata su Selezione del veicolo interfaccia:
    interface VehicleSelection {
      id: number | string;
      children?: VehicleSelection[];
    }
    

    Questi includono un IDche useremo per cercare VehicleNodes in DATI_ALBERO.

  3. Sono inclusi solo i nodi selezionati; tutti gli altri vengono ignorati in modo che gli oggetti archiviati non consumino memoria inutilmente.

C’è solo un problema con l’approccio attuale: il DATI_ALBERO non include gli ID! Risolviamolo aggiungendone alcuni ora:

Tutorial Dom e albero angolare

Certo, potremmo usare il nome campo per le ricerche, ma l’esperienza impone che non è mai saggio usare nomi per le ricerche poiché questi possono cambiare nel tempo e potrebbero richiedere la traduzione ad un certo punto.

Leggere: Filtra i nodi DOM usando un TreeWalker

La funzione save() in angolare

Fatto ciò, siamo pronti per implementare il Salva() metodo. Esegue solo una convalida prima di aggiungere le selezioni del nodo corrente a Selezioni salvate Vettore:

public save() {
  this.errMsg = '';
  const saveName = this.saveNameRef.nativeElement.value.trim();
  if (saveName === '') {
    this.errMsg = 'Please provide a save name.';
    this.saveNameRef.nativeElement.focus();
  } else {
    this.savedSelections.push({
      name: this.sanitizer.sanitize(SecurityContext.HTML, saveName),
      selections: this.saveSelectedNodes(this.dataSource.data)
    });
  }
}

Il disinfettare() metodo è fornito dal Disinfettante Dom che viene iniettato tramite il costruttore:

constructor(private sanitizer: DomSanitizer) {
  // ...
}

Pulisce il Salva nome in modo che sia adatto per l’inserimento nel DOM.

Le selezioni stesse vengono raccolte dal ricorsivo saveSelectedNodes() metodo. Essendo un metodo ricorsivo, segue il modello introdotto in precedenza. Puoi vedere la chiamata ricorsiva in cui passa i figli del nodo:

private saveSelectedNodes(vehicleNodes: VehicleNode[]): VehicleSelection[] {
  let vehicleSelections = [] as VehicleSelection[];
  vehicleNodes.forEach(node => {
    if (node.selected || node.indeterminate) { 
      const vehicleSelection: VehicleSelection = { id: node.id };
      if (node.children) {
        vehicleSelection.children = this.saveSelectedNodes(node.children);
      }
      vehicleSelections.push(vehicleSelection);
    }
  });
  return vehicleSelections;
}

Se aggiungiamo un console.log() appena prima di ciascuno selezione del veicolo viene spinto sul veicoliSelezioni array, possiamo osservare l’avanzamento mentre il metodo itera su ogni nodo:

Salva registro console

Funziona dal nodo foglia fino a ciascuna voce di livello radice.

Andare avanti con le strutture ad albero DOM

C’è un demo della nostra applicazione che include la funzionalità di salvataggio. Nella prossima puntata, aggiungeremo il codice per ripristinare le selezioni salvate.

Per saperne di più Tutorial di sviluppo web angolare.

Source link

Di Simone Serra

Web Designer Freelancer Realizzazione Siti Web Serra Simone Realizzo siti web, portali ed e-commerce con focus specifici sull’usabilità, l’impatto grafico, una facile gestione e soprattutto in grado di produrre conversioni visitatore-cliente. Elaboro siti internet, seguendo gli standard Web garantendo la massima compatibilità con tutti i devices. Sviluppo e-commerce personalizzati, multilingua, geolocalizzati per potervi mettere nelle migliori condizioni di vendita. Posiziono il tuo sito su Google per dare maggiore visibilità alla tua attività sui motori di ricerca con SEO di base o avanzato.