organisation chart using d3 in angular

Discussão em 'Angular' iniciado por Ashkaran, Março 6, 2025.

  1. Ashkaran

    Ashkaran Guest

    D3 version 7.9.0, D3-org-chart version 3.1.1, Angular version 16... I made an organizational chart, however I would want it to be horizontal, from left to right.

    When I attempt to use direction (.direction('left-to-right')) and orientation (.orientation('horizontal')), I receive the following error:


    Property 'direction' does not exist on type 'OrgChart'
    ​

    HTML code

    <div id="orgchart" class="chart-container"></div>

    SCSS code

    #orgchart {



    .node .label {
    fill: green !important;
    }

    .node .title {
    fill: red !important;
    }

    .node rect {
    fill: lightblue !important;
    }

    svg {
    background-color: rgb(213, 219, 220);
    }

    ::ng-deep.node-button-div {
    width: 134% !important;
    }


    ::ng-deep svg.svg-chart-container {
    height: 200vh !important;
    }

    }


    TS code

    import {
    OnChanges,
    Component,
    OnInit,
    Input,
    ViewChild,
    ElementRef,
    } from '@angular/core';
    import * as d3 from 'd3';
    import { HierarchyNode } from 'd3';
    import { OrgChart, State } from 'd3-org-chart';



    @Component({
    selector: 'app-org-chart',
    templateUrl: './org-chart.component.html',
    styleUrls: ['./org-chart.component.scss']
    })
    export class OrgChartComponent implements OnInit, OnChanges {
    @ViewChild("chartContainer") chartContainer: ElementRef | undefined;
    @Input() data: any[] | undefined;
    chart: any;
    datas: any[] = [];
    constructor() { }

    ngOnInit() {
    const res = {
    data: [
    {
    id: 1,
    pos_name: 'KITTING PROCESS',
    size: '',
    parentId: 0,
    member: 'Mani.N (027)',
    imgname: null,
    desig_id: 1,
    desig_name: 'Managing Director',
    file_path: null,
    // status: 'inprogress',
    imageUrl:
    'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
    },
    {
    id: 2,
    pos_name: 'ASSEMBLY PROCESS',
    size: '',
    parentId: 0,
    // member: 'Soumen Deb (142)',
    imgname: null,
    desig_id: 2,
    // desig_name: 'Test27_desig',
    file_path: null,
    // status: 'pending',
    imageUrl:
    'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
    },
    {
    id: 3,
    pos_name: 'TESTING PROCESS',
    size: '',
    parentId: 0,
    // member: 'Tanmoy Ghosh (341)',
    imgname: null,
    desig_id: 3,
    // desig_name: 'Manager-Laser Customer Relations',
    file_path: null,
    // status: 'completed',
    imageUrl:
    'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
    },
    {
    id: 4,
    pos_name: 'Dry Process',
    size: '',
    parentId: 1,
    // member: 'Tanmoy Ghosh (341)',
    imgname: null,
    desig_id: 4,
    // desig_name: 'Manager-Laser Customer Relations',
    file_path: null,
    // status: 'completed',
    imageUrl:
    'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
    },
    {
    id: 5,
    pos_name: 'Wet Process',
    size: '',
    parentId: 1,
    // member: 'Tanmoy Ghosh (341)',
    imgname: null,
    desig_id: 4,
    // desig_name: 'Manager-Laser Customer Relations',
    file_path: null,
    // status: 'pending',
    imageUrl:
    'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
    },

    {
    id: 6,
    pos_name: 'Radix Lab to Fab Products',
    size: '',
    parentId: 1,
    // member: 'Tanmoy Ghosh (341)',
    imgname: null,
    desig_id: 6,
    // desig_name: 'Manager-Laser Customer Relations',
    file_path: null,
    // status: 'inprogress',
    imageUrl:
    'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
    },

    {
    id: 7,
    pos_name: "Strategic Technology Platforms",
    size: "",
    parentId: 1,
    member: "Sanjay.G (476)",
    imgname: null,
    desig_id: 73,
    desig_name: "Senior Officer - Costing",
    file_path: null,
    // status: 'completed',
    imageUrl: "http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png"
    },
    {
    id: 8,
    pos_name: 'Dry Process',
    size: '',
    parentId: 2,
    // member: 'Tanmoy Ghosh (341)',
    imgname: null,
    desig_id: 4,
    // desig_name: 'Manager-Laser Customer Relations',
    file_path: null,
    // status: 'completed',
    imageUrl:
    'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
    },
    {
    id: 9,
    pos_name: 'Wet Process',
    size: '',
    parentId: 2,
    // member: 'Tanmoy Ghosh (341)',
    imgname: null,
    desig_id: 4,
    // desig_name: 'Manager-Laser Customer Relations',
    file_path: null,
    // status: 'pending',
    imageUrl:
    'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
    },

    {
    id: 10,
    pos_name: 'Radix Lab to Fab Products',
    size: '',
    parentId: 2,
    // member: 'Tanmoy Ghosh (341)',
    imgname: null,
    desig_id: 6,
    // desig_name: 'Manager-Laser Customer Relations',
    file_path: null,
    // status: 'inprogress',
    imageUrl:
    'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
    },

    {
    id: 11,
    pos_name: "Strategic Technology Platforms",
    size: "",
    parentId: 2,
    member: "Sanjay.G (476)",
    imgname: null,
    desig_id: 73,
    desig_name: "Senior Officer - Costing",
    file_path: null,
    // status: 'completed',
    imageUrl: "http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png"
    },
    {
    id: 12,
    pos_name: 'Dry Process',
    size: '',
    parentId: 3,
    // member: 'Tanmoy Ghosh (341)',
    imgname: null,
    desig_id: 4,
    // desig_name: 'Manager-Laser Customer Relations',
    file_path: null,
    // status: 'completed',
    imageUrl:
    'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
    },
    {
    id: 13,
    pos_name: 'Wet Process',
    size: '',
    parentId: 3,
    // member: 'Tanmoy Ghosh (341)',
    imgname: null,
    desig_id: 4,
    // desig_name: 'Manager-Laser Customer Relations',
    file_path: null,
    // status: 'pending',
    imageUrl:
    'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
    },

    {
    id: 14,
    pos_name: 'Radix Lab to Fab Products',
    size: '',
    parentId: 3,
    // member: 'Tanmoy Ghosh (341)',
    imgname: null,
    desig_id: 6,
    // desig_name: 'Manager-Laser Customer Relations',
    file_path: null,
    // status: 'inprogress',
    imageUrl:
    'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
    },

    {
    id: 15,
    pos_name: "Strategic Technology Platforms",
    size: "",
    parentId: 3,
    member: "Sanjay.G (476)",
    imgname: null,
    desig_id: 73,
    desig_name: "Senior Officer - Costing",
    file_path: null,
    // status: 'completed',
    imageUrl: "http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png"
    },
    {
    id: 16,
    pos_name: "VeroTherm FAR XP",
    size: "",
    parentId: 4,
    // member: "Sanjay.G (476)",
    imgname: null,
    desig_id: 73,
    // desig_name: "Senior Officer - Costing",
    file_path: null,
    status: 'inprogress',
    imageUrl: "http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png"
    },
    {
    id: 17,
    pos_name: "VertaCure XP G2",
    size: "",
    parentId: 4,
    // member: "Sanjay.G (476)",
    imgname: null,
    desig_id: 73,
    // desig_name: "Senior Officer - Costing",
    file_path: null,
    status: 'pending',
    imageUrl: "http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png"
    },
    {
    id: 18,
    pos_name: "VertaBond",
    size: "",
    parentId: 4,
    // member: "Sanjay.G (476)",
    imgname: null,
    desig_id: 73,
    // desig_name: "Senior Officer - Costing",
    file_path: null,
    status: 'completed',
    imageUrl: "http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png"
    },
    {
    id: 19,
    pos_name: "UltraCoat",
    size: "",
    parentId: 4,
    // member: "Sanjay.G (476)",
    imgname: null,
    desig_id: 73,
    // desig_name: "Senior Officer - Costing",
    file_path: null,
    status: 'completed',
    imageUrl: "http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png"
    },
    {
    id: 20,
    pos_name: "Chemical Delivery Units",
    size: "",
    parentId: 5,
    // member: "Sanjay.G (476)",
    imgname: null,
    desig_id: 73,
    // desig_name: "Senior Officer - Costing",
    file_path: null,
    status: 'pending',
    imageUrl: "http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png"
    },
    {
    id: 21,
    pos_name: "SURE Wet Processing Tool",
    size: "",
    parentId: 5,
    // member: "Sanjay.G (476)",
    imgname: null,
    desig_id: 73,
    // desig_name: "Senior Officer - Costing",
    file_path: null,
    status: 'completed',
    imageUrl: "http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png"
    },
    {
    id: 22,
    pos_name: "TersOnus Platform",
    size: "",
    parentId: 5,
    // member: "Sanjay.G (476)",
    imgname: null,
    desig_id: 73,
    // desig_name: "Senior Officer - Costing",
    file_path: null,
    status: 'inprogress',
    imageUrl: "http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png"
    },
    {
    id: 23,
    pos_name: "TersOra Edge Zone Removal",
    size: "",
    parentId: 5,
    // member: "Sanjay.G (476)",
    imgname: null,
    desig_id: 73,
    // desig_name: "Senior Officer - Costing",
    file_path: null,
    status: 'completed',
    imageUrl: "http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png"
    },

    {
    id: 24,
    pos_name: "G Series",
    size: "",
    parentId: 6,
    // member: "Sanjay.G (476)",
    imgname: null,
    desig_id: 73,
    // desig_name: "Senior Officer - Costing",
    file_path: null,
    status: 'completed',
    imageUrl: "http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png"
    },

    {
    id: 25,
    pos_name: "EcoClean",
    size: "",
    parentId: 6,
    // member: "Sanjay.G (476)",
    imgname: null,
    desig_id: 73,
    // desig_name: "Senior Officer - Costing",
    file_path: null,
    status: 'completed',
    imageUrl: "http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png"
    }
    }



    console.log('Org Chart Data:', res.data);
    console.log('ID type:', typeof res.data[0].id, 'ParentID type:', typeof res.data[0].parentId);
    console.log('IDs:', res.data.map(item => item.id));
    res.data.forEach(item => {
    if (item.id === null || item.id === undefined || item.parentId === null || item.parentId === undefined) {
    console.log('Null or undefined found:', item);
    }
    });

    if (res) {
    const chartContainer = d3.select('.chart-container');
    chartContainer.append('defs')
    .append('clipPath')
    .attr('id', 'clip')
    .append('rect')
    .attr('width', '100%')
    .attr('height', '100%');
    this.chart = new OrgChart()
    .container('.chart-container')
    .data(res.data)

    .direction('left-to-right')
    .orientation('horizontal')
    .nodeWidth((d: any) => 300)
    .initialZoom(0.8)
    .nodeHeight((d: any) => 175)

    .buttonContent(
    ({
    node,
    state,
    }: {
    node: HierarchyNode<any>;
    state: State<any>;
    }) => {
    return `<div style="color:#fff; border-radius: 5px;
    padding: 3px 9px 3px 8px;font-size:10px;margin:auto auto;background-color:#007672;border: 1px solid #007672">
    <span style="font-size:14px">
    ${node.data._directSubordinates}
    ${node.children
    ? `<i class="fa fa-angle-up"></i>`
    : `<i class="fa fa-angle-down"></i>`
    }
    </span>

    </div>`;
    }
    )
    .linkUpdate((d: any, i: any, arr: any) => {
    const selection = d3.select('.chart-container');
    const strokeColor = '#007672';
    const strokeWidth = d.data._upToTheRootHighlighted ? 15 : 1;
    selection.style('stroke', strokeColor);
    selection.attr('stroke-width', strokeWidth);


    if (d.data._upToTheRootHighlighted) {
    selection.raise();
    }
    })
    .childrenMargin((d: any) => 50)
    .compactMarginBetween((d: any) => 35)
    .compactMarginPair((d: any) => 90)
    .nodeContent(function (
    d: HierarchyNode<any>,
    i: number,
    arr: HierarchyNode<any>[],
    state: State<any>
    ) {
    const nodeWithWidth = d as HierarchyNode<any> & { width: number };
    const imageUrl = d.data.imageUrl ? d.data.imageUrl : 'assets/masters/user.jpg';

    let borderColor = 'lightgray'; // Default border color

    if (d.data.status === 'pending') {
    borderColor = 'orange';
    } else if (d.data.status === 'inprogress') {
    borderColor = 'blue';
    } else if (d.data.status === 'completed') {
    borderColor = 'green';
    }
    return `



    <div style="padding-top:0px;background-color:none; border-radius: 20px; margin-left:1px;height:${d.height}px;border-radius:2px;overflow:visible">
    <div style="height:inherit;padding-top:0px;background-color:white;border:5px solid ${borderColor}; border-radius: 20px;">
    <div
    class="col-xs-12 col-sm-12 col-md-12 col-lg-12 row m-0 p-0"
    style="
    background: #ffffff 0% 0% no-repeat padding-box;
    box-shadow: 0px 3px 6px #00000029;
    height: inherit;
    width: inherit;
    border-radius: 20px;
    "
    >

    <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 row m-0 p-3">
    <div
    class="col-xs-12 col-sm-12 col-md-12 col-lg-12 row m-0 d-flex align-items-center p-0"
    >
    <div class="col-xs-3 col-sm-3 col-md-3 col-lg-3 p-0">
    <img src="assets/card/profile1.png" style="width: 70px; height: 70px;border-radius: 50%;" />
    </div>
    <div class="col-xs-9 col-sm-9 col-md-9 col-lg-9 p-0">
    <div
    class="col-xs-12 col-sm-12 col-md-12 col-lg-12 d-flex flex-column ms-4"
    >
    <div
    class="col-xs-12 col-sm-12 col-md-12 col-lg-12 pt-2 d-flex justify-content-start fw-bolder"
    >
    ${d.data.pos_name}
    </div>

    <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 py-1 fw-light " style="color: #007672;">
    ${d.data.desig_name}
    </div>

    <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 py-1 fw-light " style="color: #868585;">
    ${d.data.member}
    </div>

    </div>
    </div>
    </div>

    </div>


    `;
    })

    const zoom: any = d3
    .zoom<SVGSVGElement, any>()
    .scaleExtent([0.7, 2])
    .on('zoom', (event: any) => {
    chartContainer.attr('transform', event.transform);
    });


    chartContainer.call(zoom);



    this.chart.render();
    this.chart.expandAll();
    (this.chart as any).direction('left-to-right')



    }

    }








    ngAfterViewInit() {
    if (!this.chart) {

    }
    this.updateChart();
    }



    ngOnChanges() {
    this.updateChart();
    }
    updateChart() {
    if (!this.data) {
    return;
    }
    if (!this.chart) {
    return;
    }
    this.chart
    .container('.chart-container')
    .data(this.data)
    .svgWidth(500)

    .onNodeClick((d: any) => console.log(d + ' node clicked'))
    .render();
    }

    }

    Continue reading...

Compartilhe esta Página