Пример использования компонента lightning-tree

Недавно наткнулся на LWC элемент lightning-tree. Когда нахлынувшие воспоминания о С++ разработке немного отступили, решил попробовать использовать его на каком-то простом примере. Поскольку аккаунты с контактами уже успели порядком поднадоесть, посвятим пример чему-либо интересному. Например, футболу.
Во избежание недоразумений уточню, что дерево, о котором идёт речь, не красно-чёрное, не бинарное и даже не хвойное, а выглядит примерно так:

image

Итак, пусть у нас будет список стран, которые являются ветвями нашего дерева и, раскрываясь по нажатию, показывают список своих топовых футбольных клубов. А чтобы было интереснее, дадим пользователю возможность выбрать свой любимый клуб, и выведем название выбранного клуба на UI где-нибудь неподалёку. А чтобы не хардкодить JSON со структурой дерева, как сделано в примерах из документации, создадим кастомные объекты для стран и клубов, где страна является мастером, а клуб - деталью (как-то топорно звучит на русском, да? :slight_smile: ). Для работы с базой данных напишем апекс класс, который вытащит нужные записи из базы и отдаст их нам в нужном виде:

public with sharing class FootballService {

    @AuraEnabled(cacheable=true)
    public static List<TeamWrapper> getTeamsDataForTree(){
        List<Football_country__c> countriesWithTeamsList = [SELECT Name,(SELECT Name FROM Football_teams__r) FROM Football_country__c];
        List<TeamWrapper> countryWrappersList = new List<TeamWrapper>();
        for(Football_country__c country : countriesWithTeamsList){
            TeamWrapper countryWrapper = new TeamWrapper() ; 
            countryWrapper.name =country.Name ;
            countryWrapper.label =country.Name ;
            countryWrapper.expanded =false;
            countryWrapper.isCountry = true;
            List<TeamWrapper> teamsList = new List<TeamWrapper>();
            for(Football_team__c team : country.Football_teams__r){
                TeamWrapper teamWrapper = new TeamWrapper();
                teamWrapper.name =team.Name ;
                teamWrapper.label =team.Name ;
                teamWrapper.expanded = false ;
                teamWrapper.isCountry = false;
                teamWrapper.items = new List<TeamWrapper>();
                teamsList.add(teamWrapper);
            }
            countryWrapper.items = teamsList;
            countryWrappersList.add(countryWrapper);            
        }
       return countryWrappersList;
    }    


    public Class TeamWrapper{
        @AuraEnabled
        public String name{get;set;}
        @AuraEnabled
        public String label{get;set;}
        @AuraEnabled
        public Boolean expanded{get;set;}
        @AuraEnabled
        public Boolean isCountry{get;set;}
        @AuraEnabled
        public List<TeamWrapper> items{get;set;}        
    }
}

В js коде JSON получится таким:

[ 
   { 
      "expanded":false,
      "isCountry":true,
      "items":[ 
         { 
            "expanded":false,
            "isCountry":false,
            "items":[ 

            ],
            "label":"Arsenal",
            "name":"Arsenal"
         },
         { 
            "expanded":false,
            "isCountry":false,
            "items":[ 

            ],
            "label":"Liverpool",
            "name":"Liverpool"
         },
         { 
            "expanded":false,
            "isCountry":false,
            "items":[ 

            ],
            "label":"Manchester United",
            "name":"Manchester United"
         },
         { 
            "expanded":false,
            "isCountry":false,
            "items":[ 

            ],
            "label":"Manchester City",
            "name":"Manchester City"
         },
         { 
            "expanded":false,
            "isCountry":false,
            "items":[ 

            ],
            "label":"Chelsea",
            "name":"Chelsea"
         }
      ],
      "label":"England",
      "name":"England"
   },
   { 
      "expanded":false,
      "isCountry":true,
      "items":[ 
         { 
            "expanded":false,
            "isCountry":false,
            "items":[ 

            ],
            "label":"Real Madrid",
            "name":"Real Madrid"
         },
         { 
            "expanded":false,
            "isCountry":false,
            "items":[ 

            ],
            "label":"Barcelona",
            "name":"Barcelona"
         },
         { 
            "expanded":false,
            "isCountry":false,
            "items":[ 

            ],
            "label":"Sevilla",
            "name":"Sevilla"
         }
      ],
      "label":"Spain",
      "name":"Spain"
   },
   { 
      "expanded":false,
      "isCountry":true,
      "items":[ 
         { 
            "expanded":false,
            "isCountry":false,
            "items":[ 

            ],
            "label":"Inter",
            "name":"Inter"
         },
         { 
            "expanded":false,
            "isCountry":false,
            "items":[ 

            ],
            "label":"Napoli",
            "name":"Napoli"
         },
         { 
            "expanded":false,
            "isCountry":false,
            "items":[ 

            ],
            "label":"Juventus",
            "name":"Juventus"
         }
      ],
      "label":"Italy",
      "name":"Italy"
   }
]

Javascript контроллер будет выглядеть как-то так:

import { LightningElement, track, wire } from 'lwc';
import getTeamsDataForTree from '@salesforce/apex/FootballService.getTeamsDataForTree';

export default class FavoriteTeam extends LightningElement {

    @track selectedTeamValue;
    @track teams;

    @wire(getTeamsDataForTree)
    wireTeamData({error, data}){
        if(data){
            this.teams = data;
        }
        else{
            this.error = error;
        }
    }    

    handleOnselect(event) {
        const team = this.findNested(this.teams, 'name', event.detail.name);
        if(team){
            this.selectedTeamValue = team.label;
        }
    }
    
    findNested(obj, key, value) {
        if (obj[key] === value && !obj.isCountry) {
            return obj;
        }
        
        const objKeys = Object.keys(obj);
        for (const k of objKeys) {
            if (typeof obj[k] === 'object' || Array.isArray(obj[k])) {
                const found = this.findNested(obj[k], key, value);
                if (found) {
                    return found;
                }
            }
        }

        return null;
    }
}

И сама разметка:

<template>
    <lightning-card title="Click on your favorite team to choose it.">
        <div class="slds-m-top_medium slds-m-bottom_x-large">
          <template if:true={teams}>
            <div class="slds-p-around_medium lgc-bg">
                <lightning-tree items={teams} header="Teams" expanded=true onselect={handleOnselect}></lightning-tree>
            </div>
            <div class="slds-m-vertical_medium">
                <p>Your favorite team is: <span class="slds-text-heading_small">{selectedTeamValue}</span></p>
            </div>
          </template>
        </div>
      </lightning-card>
</template>

И в файле .js-meta.xml я указал lightning__HomePage, чтобы вынести наш компонент на домашнюю страницу.

Все страны, и, тем более, все клубы я добавлять само собой не стал, так как это заняло бы неоправданно длительное время. Но в текущей имплементации для добавления элемента достаточно создать запись кастомного объекта, а FootballService подберёт их самостоятельно.

Вот, что получилось в итоге:
image

4 Likes

Да хороший компонент :slight_smile:

Уже все открыто,спасибо,что обратили внимание)