English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Realizzazione di un menu ad albero ripiegabile con componenti ricorsivi Vue.js (demo)

 

In Vue.js, un componente ricorsivo chiama se stesso, come:

Vue.component('recursive-component', {
  template: `<!--Chiamandomi stesso!-->
       <recursive-component></recursive-component>`
 });

Il componente ricorsivo viene spesso utilizzato per visualizzare i commenti, i menu嵌套, o in generale di tipo padre e figlio identico, anche se i contenuti specifici sono diversi. Ad esempio:

 

Ora ti mostrerò come utilizzare efficacemente i componenti ricorsivi, passo dopo passo, costruendo un menu ad albero espandibile/ritractabile.

Struttura dati

Un componente ricorsivo ad albero UI è una rappresentazione visiva di una struttura dati ricorsiva. In questo tutorial, useremo una struttura ad albero, in cui ogni nodo è un oggetto:

Un attributo label.

Se ha figli, un attributo nodes, è un array di un o più nodi.

Come tutte le strutture ad albero, deve avere un nodo radice, ma può essere infinitamente profonda.

let tree = {
  label: 'root',
  nodes: [
   {
    label: 'item1',
    nodes: [
     {
      label: 'item1.1'
     },
     {
      label: 'item1.2',
      nodes: [
       {
        label: 'item1.2.1'
       }
      ]
     }
    ]
   }, 
   {
    label: 'item2' 
   }
  ]
 }

Componente ricorsivo

Facciamo un componente ricorsivo per visualizzare la nostra struttura dati chiamata TreeMenu. Mostra solo l'etichetta del nodo corrente e si chiama a se stesso per visualizzare qualsiasi nodo figlio. Il file TreeMenu.vue è il seguente:

<template>
  <div class="tree-menu">
   <div>{{ label }}</div>
   <tree-menu 
    v-for="node in nodes" 
    :nodes="node.nodes" 
    :label="node.label"
   >
   </tree-menu>
  </div>
 </template>
 <script>
  export default { 
   props: [ 'label', 'nodes' ],
   name: 'tree-menu'
  }
 </script>

Se usi un componente ricorsivo, devi prima fare una definizione globale di Vue.component, o dargli un attributo name. Altrimenti, qualsiasi componente figlio non potrà chiamarlo ulteriormente e riceverai un messaggio di errore "undefined component" incerto.

Evento di base

Come qualsiasi funzione ricorsiva, hai bisogno di un evento di base per terminare la ricorsione, altrimenti la rendering continuerà indefinitamente e alla fine causerà un overflow di stack.

Nel menu a albero, quando raggiungiamo un nodo senza figli, vogliamo fermare la ricorsione. Puoi farlo con v-if, ma abbiamo scelto di usare v-for che lo farà implicitamente per noi; se l'array nodes non ha ulteriori definizioni, il componente tree-menu verrà chiamato. Il file template.vue è il seguente:

<template>
  <div class="tree-menu">
   ...
   <!--Se `nodes` è undefined, questo non verrà visualizzato-->
   <tree-menu v-for="node in nodes"></tree-menu>
 </template>

Uso

Come possiamo utilizzare questo componente? Prima di tutto, dichiariamo un'istanza Vue con una struttura dati che include l'attributo data e il componente definito treemenu. Il file app.js è il seguente:

import TreeMenu from './TreeMenu.vue'
 let tree = {
  ...
 }
 new Vue({
  el: '#app',
  data: {
   tree
  },
  components: {
   TreeMenu
  }
 )

Ricorda che la nostra struttura dati ha un nodo radice. Nell'inizio del template principale chiamiamo ricorsivamente il componente TreeMenu, utilizzando l'attributo nodes radice per props:

<div id="app">
  <tree-menu :label="tree.label" :nodes="tree.nodes"></tree-menu>
 </div>

 

Ecco come appare attualmente:

Postura corretta

Riconoscere visivamente la "profondità" del componente figlio è utile, in modo che l'utente possa ottenere una sensazione della struttura dati dall'UI. Rendiamo spaziati i nodi figli di ogni livello per raggiungere questo obiettivo.

 

Questo è fatto aggiungendo un prop depth, che viene implementato tramite TreeMenu. Utilizzeremo questo valore per abbinare dinamicamente lo stile inline con la trasformazione: useremo la regola CSS transform: translate per ogni etichetta del nodo per creare lo spaziamento. La modifica del file template.vue è la seguente**:**

<template>
  <div class="tree-menu">
   <div :style="indent">{{ label }}</div>
   <tree-menu 
    v-for="node in nodes" 
    :nodes="node.nodes" 
    :label="node.label"
    :depth="depth + 1"
   >
   </tree-menu>
  </div>
 </template>
 <script>
  export default { 
   props: [ 'label', 'nodes', 'depth' ],
   name: 'tree-menu',
   computed: {
    indent() {
     return { transform: `translate(${this.depth * 50}px)` }
    }
   }
  }
 </script>

L'attributo depth parte da zero nel template principale. Nel template del componente sopra, puoi vedere che questo valore aumenta ogni volta che viene passato a qualsiasi nodo figlio.

<div id="app">
  <tree-menu 
   :label="tree.label" 
   :nodes="tree.nodes"
   :depth="0"
  </tree-menu>
 </div>

Attenzione: ricorda di usare v-bind per il valore depth per assicurarti che sia un numero JavaScript e non una stringa.

Espandere/Restringere

Poiché la struttura dati ricorsiva può essere molto grande, un ottimo trucco UI per visualizzarli è nascondere tutti i nodi tranne il nodo radice, in modo che l'utente possa espandere o restringere i nodi come necessario.

Per questo, aggiungeremo una proprietà locale showChildren. Se il suo valore è False, i nodi figli non verranno visualizzati. Questo valore dovrebbe essere cambiato tramite il clic sul nodo, quindi dobbiamo utilizzare un metodo di ascolto dell'evento di singolo clic chiamato toggleChildren per la gestione. La modifica del file template.vue è la seguente**:**

<template>
  <div class="tree-menu">
   <div :style="indent" @click="toggleChildren">{{ label }}</div>
   <tree-menu 
    v-if="showChildren"
    v-for="node in nodes" 
    :nodes="node.nodes" 
    :label="node.label"
    :depth="depth + 1"
   >
   </tree-menu>
  </div>
 </template>
 <script>
  export default { 
   props: [ 'label', 'nodes', 'depth' ],
   data() {
    return { showChildren: false }
   },
   name: 'tree-menu',
   computed: {
    indent() {
     return { transform: `translate(${this.depth * 50}px)` }
    }
   },
   methods: {
    toggleChildren() {
     this.showChildren = !this.showChildren;
    }
   }
  }
 </script>

Conclusione

Così, abbiamo un menu ad albero funzionante. Un modo per aggiungere il tocco finale è quello di aggiungere un'icona di più/meno, il che rende la visualizzazione dell'UI più evidente. Ho anche migliorato il font e le prestazioni di calcolo rispetto a showChildren originale.

Vai su CodePen ( https://codepen.io/anthonygore/pen/PJKNqaPuoi vedere come l'ho implementato.

 

Ti potrebbe interessare