Marker doesn't apply

Hello,

I have an issue that I think is related to multiple gantt instance. On my vue project, I have a menu who can lead to create a gantt instance, with three different way to sort data. So my ganttComponent is render, and then if user select another way of sorting data, it is destroyed and then rerender. Everything work well since I use gantt.clearAll(), except my marker, who doesn’t apply the second time. Do I have to do a specific things on destroying the gantt instance ? I can see that the marker is well created, but I think it doesn’t attach to the active gantt instance.

Hello Alex,
The clearAll method removes the data and markers, but it doesn’t actually destroy the Gantt instance. You need to use the destructor method for that:
https://docs.dhtmlx.com/gantt/api__gantt_destructor.html

When you create a new Gantt instance, other configuration should not be relevant.
Here is an example in the snippet:
https://snippet.dhtmlx.com/r8el80c8

Though, I don’t see any issues when I use only the clearAll method:
https://snippet.dhtmlx.com/ludgc8nz

Probably, the issue is related to the Gantt configuration, but it is hard to suggest what might be wrong as I don’t see your code.
Please add your configuration to the following snippet and make sure that the issue is reproduced there:

Then, click on the Save button and send me the link.
Or send me a ready demo with all the necessary JavaScript and CSS files so that I can reproduce the issue locally.

actually this is quit complex to give you a demo because the problem occur because of the complexity of my project. The problem I meet is quite the same as this one : http://disq.us/p/2obwc4m with the difference that I use the standard version, so I don’t want to use “gantt.destructor()” or I will not be able to parse new data without reload. Also, the problem is specific with markers (and maybe tooltip), because data, dataProcessor etc goes with the right instance.

As I can’t give you a demo, here is my complete code (sorry for that). So this is my GanttComponent, who is called one time at beginning, and then when user try another way to sort data, this component is dismounted and remounted :

<template>
  <div ref="ganttContainer" style="height: 90vh; width: 99vw;"></div>
</template>
 
<script>
/* eslint-disable @typescript-eslint/camelcase */
import {gantt, Gantt} from 'dhtmlx-gantt';
import { getModule } from 'vuex-module-decorators';
import MainModule from '../../../../store/modules/MainModule';
import moment from 'moment-timezone';


const mainState = getModule(MainModule);

const dateToStr = gantt.date.date_to_str(gantt.config.task_date);

const dateEditor = {type: "date", map_to: "start_date"};
const durationEditor = {type: "number", map_to: "duration", min:0, max: 100};

const stateEditor = {type: "select", options: [
        {key: "Unassigned", label: "Unassigned"},
        {key: "Closed", label: "Closed"},
        {key: "InProgress", label: "In progress"},
        {key: "Late", label: "Late"},
        {key: "Unscheduled", label: "Unscheduled"}
    ], map_to: "state"};

const legend = document.createElement('div');
legend.className = 'gantt-legend';
legend.id = 'gantt-legend';

const header = document.createElement('header');
header.className = 'legend-head';

const h3 = document.createElement('h3');
h3.textContent = 'Legend';

header.appendChild(h3);
legend.appendChild(header);

const legendList = document.createElement('div');
legendList.className = 'legend-list';

const states = ['Unassigned', 'Closed', 'In progress', 'Late', 'Unscheduled', 'folder'];
const descriptions = ['Unassigned issues', 'Closed issues', 'In progress', 'Late issues', 'Unscheduled', 'Parent'];

for (let i = 0; i < states.length; i++) {
  const row = document.createElement('div');
  row.className = 'legend-row';

  const label = document.createElement('div');
  label.className = 'legend-label ' + states[i].toLowerCase().replace(' ', '-');

  const description = document.createElement('div');
  description.textContent = descriptions[i];

  row.appendChild(label);
  row.appendChild(description);
  legendList.appendChild(row);
}

legend.appendChild(legendList);

// Créer la modal et ses éléments
const modal = document.createElement('div');
const content = document.createElement('div');
const closeButton = document.createElement('span');
const text = document.createElement('p');
const okButton = document.createElement('button');
const cancelButton = document.createElement('button');

// Ajouter du texte aux éléments
closeButton.textContent = '×';
text.textContent = 'Send all changes to GitLab ?';
okButton.textContent = 'OK';
okButton.style.backgroundColor = '#4caf50';
cancelButton.textContent = 'Cancel';
cancelButton.style.backgroundColor = '#bbbbbb';

// Ajouter des classes aux éléments pour le style
modal.classList.add('modal');
content.classList.add('modal-content');
closeButton.classList.add('close-button');

// Ajouter des événements aux boutons
closeButton.addEventListener('click', () => modal.style.display = 'none');
cancelButton.addEventListener('click', () => modal.style.display = 'none');

// Ajouter les éléments à la modal
content.appendChild(closeButton);
content.appendChild(text);
content.appendChild(okButton);
content.appendChild(cancelButton);
modal.appendChild(content);

// Ajouter la modal au document
document.body.appendChild(modal);


const stateFilter = {
  Unassigned: true,
  Closed: true,
  InProgress: true,
  Late: true,
  Unscheduled: true
}; 

let standaloneFilter = true;

const daysStyle = function(date){
    const dateToStr = gantt.date.date_to_str("%D");
    if (dateToStr(date) == "Sun"||dateToStr(date) == "Sat")  return "weekend";
 
    return "";
};



export default {
  props: {
    tasks: {
      type: Object,
      default () {
        return {data: [], links: []}
      }
    },
    requestsQueue: {
      type: Array,
      required: true
    },
    users: {
      type: Array,
      required: true
    }
  },

  data() {
    return {
      selectedScale: "day",
      isHidden: false,
      selectedUsers: null,
      markerId: null
    }
  },
  
  watch: {
    requestsQueue: {
      handler: function (requestsQueue) {
        const uploadButtonDiv = document.querySelector(".upload-logo-container");
        if (requestsQueue.length > 0 && mainState.viewGateway.configuration.admin) {
          if (uploadButtonDiv) {
            uploadButtonDiv.style.display = 'block';
          }
        }
        else {
          if (uploadButtonDiv) {
            uploadButtonDiv.style.display = 'none';
          }
        }
      },
      deep: true
    },
    selectedScale: {
      handler: function (newVal, oldVal) {
        const selectScale = document.querySelector(".selectScale");
        selectScale.value = newVal;
        const event = new Event('change');
        selectScale.dispatchEvent(event);
      },
      deep: true
    }
  },

  methods: {
    $_initGanttEvents: function() {
      if (!gantt.$_eventsInitialized) {
        gantt.attachEvent('onTaskSelected', (id) => {
          const task = gantt.getTask(id);
          this.$emit('task-selected', task);
        });
        gantt.attachEvent('onTaskIdChange', (id, new_id) => {
          if (gantt.getSelectedId() == new_id) {
            const task = gantt.getTask(new_id);
            this.$emit('task-selected', task);
          }
        });
        gantt.$_eventsInitialized = true;
      }
    },
    $_initDataProcessor: function() {
      if (!gantt.$_dataProcessorInitialized) {
        this.dp = gantt.createDataProcessor((entity, action, data, id) => {
          this.$emit(`${entity}-updated`, id, action, data);
        });
        this.dp.attachEvent("onBeforeUpdate", function (id, status, data) {
            if (!data.name) {
                gantt.message("The task's name can't be empty!");
                return false;
            }
            if (!data.start_date || !data.end_date || data.start_date > data.end_date) {
                gantt.message("Task's dates are invalid!");
                return false;
            }
            return true;
        });
        gantt.$_dataProcessorInitialized = true;
      }
    },
    addAssignUserToTask: function(id) {
      const task = gantt.getTask(id);
      task.users.push('');
    }
  },
 
  mounted: function () {

    this.$_initGanttEvents();

    const userArray = this.users.map(user => user.username);

    let isThereStandaloneTasks = false;
    for (const task of this.$props.tasks.data) {
      if (task.level === 1 && task.parent === 0) {
        isThereStandaloneTasks = true;
        break;
      }
    }

    gantt.plugins({ 
        marker: true,
        multiselect: true ,
        tooltip: true ,
        undo: true
    }); 

    gantt.config.undo = true;
    gantt.config.redo = true;

    gantt.config.date_format = "%Y-%m-%d";

    gantt.config.layout = {
    css: "gantt_container",
      rows:[
            {
                  html: `
                  <div class="gantt-controls">
                      <button class="gantt-undo" onclick="gantt.undo()">Undo</button>
                      <button class="gantt-redo" onclick="gantt.redo()">Redo</button>
                    <div class="state-filter">
                      <div class="state-filter-unassigned"><input type="checkbox" id="Unassigned" class="state-checkbox" checked=${stateFilter["Unassigned"]} onChange="stateCheckboxOnChange('Unassigned')"><label for="Unassigned">Unassigned</label></div>
                      <div class="state-filter-closed" style="display : ${mainState.viewGateway.configuration.addClosedIssue ? ``:`none`}"><input type="checkbox" id="Closed" class="state-checkbox" checked=${stateFilter["Closed"]} onChange="stateCheckboxOnChange('Closed')"><label for="Closed">Closed</label></div>
                      <div class="state-filter-inprogress"><input type="checkbox" id="InProgress" class="state-checkbox" checked=${stateFilter["InProgress"]} onChange="stateCheckboxOnChange('InProgress')"><label for="InProgress">InProgress</label></div>
                      <div class="state-filter-late"><input type="checkbox" id="Late" class="state-checkbox" checked=${stateFilter["Late"]} onChange="stateCheckboxOnChange('Late')"><label for="Late">Late</label></div>
                      <div class="state-filter-unscheduled"><input type="checkbox" id="Unscheduled" class="state-checkbox" checked=${stateFilter["Unscheduled"]} onChange="stateCheckboxOnChange('Unscheduled')"><label for="Unscheduled">Unscheduled</label></div>
                    </div>
                    <div class='searchEl'><label for="searchFilter">Search task :</label><input id='searchFilter' style='width: 120px;' type='text' placeholder='Search tasks...'></div>
                    ${isThereStandaloneTasks ? `<div class='standaloneFilter'><label for="standaloneFilter">Standalone tasks :</label><input id='standaloneFilter' type='checkbox' checked=${standaloneFilter}></div>` : ''}
                    <select v-model="${this.selectedScale}" class="selectScale">
                      <option value="day">Day</option>
                      <option value="2days">2 Days</option>
                      <option value="week">Week</option>
                      <option value="month">Month</option>
                    </select>
                  
                    <div class="custom-select-container">
                      <div class="custom-select" id="userSelect" ">
                        Select users
                        <div class="custom-select-arrow"></div>
                      </div>
                      <div class="custom-select-dropdown" id="userDropdown">
                        ${this.users.map(user => `
                          <label class="custom-select-option">
                            <input type="checkbox" value="${user.username}">
                            ${user.username}
                          </label>
                        `).join('')}
                      </div>
                    </div>

                    <div class="upload-logo-container" title="push all changes to GitLab" style="display: none">
                       <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
                        <!--
                          <!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.>
                        -->
                       <path fill="#28bf2d" d="M246.6 9.4c-12.5-12.5-32.8-12.5-45.3 0l-128 128c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 109.3 192 320c0 17.7 14.3 32 32 32s32-14.3 32-32l0-210.7 73.4 73.4c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-128-128zM64 352c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 64c0 53 43 96 96 96l256 0c53 0 96-43 96-96l0-64c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 64c0 17.7-14.3 32-32 32L96 448c-17.7 0-32-14.3-32-32l0-64z"/>
                       </svg>
                    </div>
                    </div>`
                  
                  , css:"gantt-controls", height: 40
                },
                { resizer: true, width: 1 },
              {
            cols: [
              {
                // the default grid view  
                view: "grid",  
                scrollX:"scrollHor", 
                scrollY:"scrollVer"
              },
              { resizer: true, width: 1 },
              {
                // the default timeline view
                view: "timeline", 
                scrollX:"scrollHor", 
                scrollY:"scrollVer"
              },
              {
                view: "scrollbar", 
                id:"scrollVer"
              }
          ]},
          {
              view: "scrollbar", 
              id:"scrollHor"
          }
      ]
    };
    
    gantt.config.fit_tasks = true;

    gantt.config.columns = [
      {name: "name", label: "Task name", tree: true, width: 200, resize: true },
      {name: "start_date", label: "Start time", align: "center", width: 150 , resize: true, editor: dateEditor},
      {name: "duration", label: "Duration", align: "center", width: 60, editor: durationEditor},
      {name: "users", label: "User", align: "center", width: 100, template:function(obj){
                                return obj.users ? obj.users.map(user => user.username).join(', ') : "";}},
      {name: "state", label: "State", align: "center", width: 100, editor: stateEditor}
    ];

    gantt.config.lightbox.sections = [
      {name: "name", label: "Name", height:30, map_to:"name", type:"textarea", focus:true},
      {name:"description", label: "Description", height:120, map_to:"description", type:"textarea"},
      {name:"state",    height:22, map_to:"state", type:"select", options: stateEditor.options},
      {name:"template", height:150, type:"template", map_to:"my_template"}, 
      {name: "time", height:72, map_to:"auto", type:"duration"}
    ];

    gantt.config.lightbox.project_sections = [
      {name: "name", label: "Name", height:30, map_to:"name", type:"textarea", focus:true},
      {name:"description", label: "Description", height:72, map_to:"description", type:"textarea"},
      {name:"template", height:150, type:"template", map_to:"my_template"}, 
      {name: "time", height:72, map_to:"auto", type:"duration"}
    ];

    gantt.locale.labels.section_name = "Title";
    gantt.locale.labels.section_state = "State";
    gantt.locale.labels.section_template = "Details";

    gantt.config.lightbox.project_sections.allow_root = false;

    gantt.attachEvent("onBeforeLightbox", (id) =>  {
      const task = gantt.getTask(id);

      if (task.level === 0 ) {
        gantt.config.lightbox.sections = gantt.config.lightbox.project_sections;
        task.my_template = `<div class='lightbox_labels'>${task.labels?.map(label => `<span style="padding: 3px;color: white;background-color:${label.color};border-radius:5px">${label.name}</span>`).join('')}</div>`;

        return true;
      }
    
      //<!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
      const addButton = `<div class='addUserAssign' ><svg xmlns="http:www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M256 80c0-17.7-14.3-32-32-32s-32 14.3-32 32V224H48c-17.7 0-32 14.3-32 32s14.3 32 32 32H192V432c0 17.7 14.3 32 32 32s32-14.3 32-32V288H400c17.7 0 32-14.3 32-32s-14.3-32-32-32H256V80z"/></svg></div>`;

      task.my_template = "<span id='lightbox_users_title'>Assign to: </span><div class='lightbox_user'>" 
      + `${task.users?.map((taskUser, index) => {
        return `<select id="userSelect${index}" class='userSelect'> <option value='none'> </option> ${this.$props.users.map(user =>{
          return `<option class="userOption" value='${JSON.stringify({ username: user.username, id: user.id })}' ${taskUser.username === user.username ? 'selected' : ''}>${user.username}</option>`
        }).join('')
        })} </select>`
      }).join('')}`
      + addButton + "</div>"
      + "<br>  <span id='lightbox_progress'>Progress: </span>"+ task.progress*100 +" %"
      + `<br>  <div class='lightbox_labels'>${task.labels?.map(label => `<span style="padding: 3px;color: white;background-color:${label.color};border-radius:5px">${label.name}</span>`).join('')}</div>`;


      this.$nextTick(() => {
        const container = document.querySelector('.lightbox_user');
        const addUserAssign = document.querySelector('.addUserAssign');
        let selects = container.querySelectorAll('select');
        selects.forEach(select => {
          select.onchange = function(e) {
            if (e.target.value === 'none') {
              task.users[parseInt(select.id.split('userSelect')[1])] = '';
            }
            else {
              task.users[parseInt(select.id.split('userSelect')[1])] = JSON.parse(e.target.value);
            }
          };
        });
        if (addUserAssign) {
          addUserAssign.addEventListener('click', () => {
            this.addAssignUserToTask(id);
            const newUser = task.users[task.users.length - 1];
            const newUserSelect = document.createElement('select');
            newUserSelect.onchange = function(e) {
              if (e.target.value === 'none') {
                task.users[parseInt(newUserSelect.id.split('userSelect')[1])] = '';
              }
              else {
                task.users[parseInt(newUserSelect.id.split('userSelect')[1])] = JSON.parse(e.target.value);
              }
            };
            newUserSelect.id = `userSelect${task.users.length - 1}`;
            newUserSelect.className = 'userSelect';
            newUserSelect.innerHTML = `<option value='none'> </option>` + this.$props.users.map(user => `<option value='${JSON.stringify({ username: user.username, id: user.id })}'>${user.username}</option>`).join('');
            const lastChild = container.lastElementChild;
            container.insertBefore(newUserSelect, lastChild);

            // Mettre à jour la référence à selects
            selects = container.querySelectorAll('select');
          });
        }
      });
      return true;
    });


    gantt.config.lightbox.allow_root = false;

    gantt.templates.task_text = function(start, end, task) {
      return "<b>Name:</b> " + task.name ;
    };
    gantt.templates.tooltip_text = function(start, end, task) {
      // Personnalisez ici le texte du tooltip pour chaque tâche (hover)
      return "<b>" + task.name + "</b><br/>" 
      + "Start: " + gantt.templates.tooltip_date_format(start) + "<br/>" 
      + "End: " + gantt.templates.tooltip_date_format(end) + "<br/>" 
      + "Duration: " + task.duration + " days" + "<br/>"
      + (task.state ? "<br/>State: " + task.state : "")
      + (task.users ? "<br/>" + `${task.users.map(user => user.username).join(', ')}` : "");
    };

    gantt.templates.task_class = function(start, end, task){
      let css = "";

      switch(task.state){
          case "Unassigned":
              css = "unassigned";
              break;
          case "Closed":
              css = "closed";
              break;
          case "InProgress":
              css = "in-progress";
              break;
          case "Late":
              css = "late";
              break;
          case "Unscheduled":
              css = "unscheduled";
              break;
          case gantt.hasChild(task.id):
              css = "folder";
              break;
          default:
              css = "";
      }
      return css;
    };

    gantt.templates.grid_row_class = function(start, end, task){
        return task.level === 0 ? "gantt_row_project" : "";
    };  


    gantt.config.columns_resizable = true;
    gantt.config.columns_autoresize = true;

    const today = new Date();

    const todayMarker = gantt.addMarker({
      start_date: today, //a Date object that sets the marker's date
      css: "today", //a CSS class applied to the marker
      text: "Now", //the marker title
      title: dateToStr(today) // the marker's tooltip
    });

    this.markerId = gantt.getMarker(todayMarker).id;

    const closestTask = this.$props.tasks.data.reduce((closest, current) => {
      const currentDate = new Date(current.start_date);
      const closestDate = new Date(closest.start_date);
      return Math.abs(today - currentDate) < Math.abs(today - closestDate) ? current : closest;
    });

    

    const topLevelTasks = this.$props.tasks.data.filter(task => task.level === 0);
    if (topLevelTasks.length < 10) {
      gantt.config.open_tree_initially = true; // if less than 10 top level tasks, epics will be opened by default
    }
    
    
    gantt.init(this.$refs.ganttContainer);
    gantt.parse(this.$props.tasks);

    const start = performance.now();

    gantt.sort((a, b) => {     
      return new Date(a.end_date) - new Date(b.end_date);
    }, false, 0, false);

    const end = performance.now();
    const executionTime = end - start;
    console.log(`Temps d'exécution tri : ${executionTime} millisecondes.`);

    
    gantt.showDate(gantt.getMarker(todayMarker).start_date);
    gantt.showTask(closestTask.id);                             // this will scroll the timeline to the task, horizontally and vertically

    const firstElement = this.$props.tasks.data[0];
    const lastElement = this.$props.tasks.data[this.$props.tasks.data.length - 1];

    const startDate = new Date(firstElement.start_date);
    const endDate = new Date(lastElement.end_date);

    const diffInDays = moment(startDate).diff(moment(endDate), 'days');
   
      if (diffInDays <= 7) {
        this.selectedScale = "day";

      } else if (diffInDays <= 30) {
        this.selectedScale = "2days";

      } else if (diffInDays <= 200) {
        this.selectedScale = "week";
      } else {
        this.selectedScale = "month";

      }
    

    if (isThereStandaloneTasks) {
      document.getElementById('standaloneFilter').addEventListener('change', function(e) {
        standaloneFilter = e.target.checked;
        gantt.refreshData();
      });
    }

    
    
    document.querySelectorAll(".state-checkbox").forEach(function(checkbox) {
        checkbox.onchange = function(e) {
          stateFilter[e.target.id] = !stateFilter[e.target.id];
          gantt.refreshData();
        };
    });

   
    document.querySelector(".selectScale").addEventListener('change', function(e) {
      this.selectedScale = e.target.value;
  
      switch (this.selectedScale) {
        case 'day':
          gantt.config.scales = [
            {unit: "month", step: 1, format: "%F, %Y"},
            {unit: "day", step: 1, format: "%j, %D", css: daysStyle}
          ];
          break;
        case '2days':
          gantt.config.scales = [
            {unit: "month", step: 1, format: "%F, %Y"},
            {unit: "day", step: 2, format: "%j, %D", css: daysStyle}
          ];
          break;
        case 'week':
          gantt.config.scales = [
            {unit: "month", step: 1, format: "%F, %Y"},
            {unit: "week", step: 1, format: "%j, %D", css: daysStyle}
          ];
          break;
        case 'month':
          gantt.config.scales = [
            {unit: "month", step: 1, format: "%F, %Y"},
          ];
          break;
      }
      gantt.render(); // re-rendre le diagramme de Gantt avec la nouvelle configuration
    });

    document.querySelector(".upload-logo-container").addEventListener('click', () => {
      modal.style.display = 'block';
    });

    okButton.addEventListener('click', () => {
      this.$emit('upload-tasks');
      modal.style.display = 'none';
    });

    
    const userSelectBtn = document.getElementById('userSelect');
    userSelectBtn.addEventListener('click', function() {
      const dropdown = document.getElementById('userDropdown');
      dropdown.style.display = dropdown.style.display === 'none' ? 'block' : 'none';
    });
    
    
    const selectUsers = document.querySelector('.custom-select-dropdown');
    selectUsers.onchange = (event) => {
      const selectedOptions = document.querySelectorAll('.custom-select-option input:checked');
      this.selectedUsers = Array.from(selectedOptions).map(option => option.value);
      // Mettre à jour l'état de selectedUsers dans votre application
      gantt.refreshData();
    };

    document.addEventListener('click', function(event) {
      const select = document.querySelector('.custom-select-container');
      const dropdown = document.getElementById('userDropdown');
      if (!select.contains(event.target)) {
        dropdown.style.display = 'none';
      }
    });

    let filterValue = "";

    gantt.attachEvent("onDataRender", function () {
    const filterEl = document.querySelector("#searchFilter")
    filterEl.addEventListener('input', function (e) {
        filterValue = filterEl.value;
        gantt.refreshData();
    });
    });

    const filterLogic = (task, match)  => {
      match = match || false;
      // check children
      gantt.eachTask(function (child) {
          if (filterLogic(child)) {
              match = true;
          }
      }, task.id);

      // check task
      if (task.name.toLowerCase().indexOf(filterValue.toLowerCase()) > -1) {
          match = true;
      }

      // check users
      if (this.selectedUsers.length > 0) {
        if (task.level === 0) {
          // Pour les tâches de niveau 0, vérifiez si elles ont au moins un enfant qui correspond au filtre
          let hasMatchingChild = false;
          gantt.eachTask((child) => {
            if (child.users?.some(user => this.selectedUsers.includes(user.username))) {
              hasMatchingChild = true;
            }
          }, task.id);
          if (!hasMatchingChild) {
            match = false;
          }
        } else if (task.level === 1 && !task.users?.some(user => this.selectedUsers.includes(user.username))) {
          // Pour les tâches de niveau 1, vérifiez simplement si elles correspondent au filtre
          match = false;
        }
      }

      // check state
      if (stateFilter[task.state] === false) {
          match = false;
      }

      // check standalone
      if (!standaloneFilter && !gantt.hasChild(task.id) && task.level === 1 && task.parent == 0 ) {
        match = false;
      }

      return match;
    }

    gantt.attachEvent("onBeforeTaskDisplay", (id, task) => {
      let thereIsAtLeastOneFilterToApply = false;
      for (const state in stateFilter) {
        if (!stateFilter[state]) {
          thereIsAtLeastOneFilterToApply = true;
          break;
        }
      }
      let isThereUsersFilter = false;
      if (this.selectedUsers && this.selectedUsers.length > 0) {
        isThereUsersFilter = true;
      }

      if (!filterValue && !thereIsAtLeastOneFilterToApply && standaloneFilter && !isThereUsersFilter) {
        return true;
      }
      
      return filterLogic(task);
    });

    gantt.attachEvent("onDataRender", () =>{
      if (gantt.getTaskByTime().length > 0) {
        const closestTask = this.$props.tasks.data.reduce((closest, current) => {
        const currentDate = new Date(current.start_date);
        const closestDate = new Date(closest.start_date);
        return Math.abs(today - currentDate) < Math.abs(today - closestDate) ? current : closest;
      });
      gantt.showDate(gantt.getMarker(todayMarker).start_date);
      gantt.showTask(closestTask.id);     
      }
    });

    gantt.attachEvent("onLightboxSave", function(id, item){
        if(!item.name){
            gantt.message({type:"error", text:"Enter task name!"});
            return false;
        }
        if (!item.start_date || !item.end_date || item.start_date > item.end_date) {
                gantt.message("Task's dates are invalid!");
                return false;
            }
        return true;
    });
  
    gantt.$root.appendChild(legend);
    this.$_initDataProcessor();
  },

  beforeDestroy: function() {
    console.log('destroyed');
    gantt.clearAll();
   // gantt.deleteMarker(this.markerId);
    gantt.detachAllEvents();
    this.dp.destructor();
    gantt.$dataProcessor = null;
    gantt.$_eventsInitialized = false;
    gantt.$_dataProcessorInitialized = false;  
  }

}
</script>
 
<style>

Hello Alex,
Thank you for the clarification.
Yes, the Gantt Instance approach is not available for the GPL version. So, you need to use the Gantt Instance Alternative approach:
https://docs.dhtmlx.com/gantt/desktop__gantt_instance.html

I have the following Vue demo:
https://files.dhtmlx.com/30d/c0a7cdde5ff4139e785733d9bfdbc9fb/vue3+gantt-gpl.zip

It seems that the issue in your case occurs because of the detachAllEvents command:
https://files.dhtmlx.com/30d/8059dec77425849f8d6588ba95a877aa/vokoscreen-2024-05-29_12-54-58.mp4

As you can read in the documentation, the method detaches not only the handlers you added, but also the internal ones:
detachAllEvents Gantt Docs.

So, there is no guarantee that Gantt will work correctly after that.

You need to save the attached event IDs somewhere, then detach them after switching to another tab:
https://docs.dhtmlx.com/gantt/desktop__gantt_instance.html#customevents

Thank you a lot, that worked !

thank YOU! I’ve been looking for a solution to solve this and now I found it :slight_smile: