Как скрыть события в fullCalendar?

Используя последнюю версию fullCalendar (5.3.2), я хочу скрыть некоторые события, относящиеся к ресурсам, которые я не хочу показывать сейчас в данном представлении. Стандартный способ сделать это — использовать функцию eventClassNames для проверки и добавления «скрытого» класса. Что-то вроде этого:

eventClassNames: function(arg) {
   my_class = "";
   if (arg.view.type != 'resourceTimeGridDay') {
      if (arg.event.extendedProps.real_rc != "1") {
         my_class = 'hidden';
      }
   }
   return my_class;
}

используя простой CSS:

.fc-event.hidden {
  display: none;
}

Это прекрасно работает, но возникает проблема, когда скрытое событие перекрывается с отображаемым. Например, в этом случае:

events: [
  {
    title: 'Resource 1',
    real_rc: '1',
    start: '2020-12-22 16:00',
    end: '2020-12-22 17:00'
  },
  {
    title: 'Resource 2',
    real_rc: '2',
    start: '2020-12-22 15:00',
    end: '2020-12-22 17:00'
  }
]

Должно отображаться только событие с real_rc == 1, и на самом деле это правильно, но пространство, используемое скрытым событием, зарезервировано, как вы можете видеть на этом изображении:

Если событие с real_rc: 2 опущено в списке event, результат будет ожидаемым:

Я использовал Chrome DevTools, чтобы попытаться выяснить, что происходит, и я думаю, что проблема в том, что «скрытый» класс установлен не на «самом внешнем элементе события», как fullCalendar указывает, а на внутренний:

(первый DIV — это первое событие, и, как вы можете видеть, класс hidden установлен, но не для DIV, а для тега a)

Вот codepen для проверки.

ИМХО это полный баг календаря, но сейчас у меня проблема и мне нужно решение. Мои варианты:

  1. Используйте селектор CSS для родительского <-- НЕВОЗМОЖНО: он не существует (пока)
  2. Сделать (1) с помощью jquery <-- Я НЕ ЗНАЮ КАК? Я знаю, что мне нужно выполнить что-то вроде $(".fc-event.hidden").parent().remove(), когда события загружаются и отображаются, но , поскольку v3 ничего подобного не существует UPDATE, и даже если он существует, с текущей версией v5 удаление элемента DOM не изменяет размер других полей событий.
  3. Попробуйте восстановить код библиотеки <-- ПРОБЛЕМА: я не хочу беспокоиться об исправлении, если следующая версия библиотеки выйдет без решения
  4. Фильтровать события при загрузке <-- МЕДЛЕННО: я использую функцию обратного вызова для загрузки событий через Ajax, и я могу выполнить там быструю фильтрацию, но в этом случае я потеряю производительность, так как мне придется обновлять события каждый раз, когда мне нужно чтобы показать события с real_rc != 1
  5. Пользовательские представления <-- Я НЕ ХОЧУ ИЗОБРЕТАТЬ КОЛЕСО. Как было предложено @saqibkafeel в комментарии, настраиваемые представления можно использовать для создания нового представления, но мне нравятся текущие представления, и мне не нужен новый, просто представления по умолчанию работают как положено.

Есть ли способ обойти эту проблему, не создавая новую? (Я чувствую, что самый простой вариант - найти крючок, который позволит мне сделать вариант номер 2, но я потратил весь день и ничего не нашел).

Ничто не мешает вам использовать jQuery, если вы этого хотите. jQuery по-прежнему можно использовать для управления элементами HTML в календаре. Тот факт, что fullCalendar перестал использовать его для внутренних целей, не мешает вам добавить его на свою страницу.

ADyson 23.12.2020 09:36

@ADyson проблема с jQuery заключается в том, что, насколько мне известно, нет перехватчиков или обратных вызовов, куда я могу добавить код и гарантировать, что события загружаются и отображаются. В v3 был обратный вызов, но его убрали.

Ivan 23.12.2020 09:44

О каких именно крючках вы говорите? Некоторые вещи изменились между версиями, но, насколько я знаю, очень мало, если вообще что-то, было удалено. Например, все еще есть хуки рендеринга событий.

ADyson 23.12.2020 10:52

@ADyson Да, для рендеринга существуют отдельные события, но не глобальные. В v3 существовал eventAfterAllRender ( fullcalendar.io/docs/v3/eventAfterAllRender). С этим я могу получить надежную точку, где я могу очистить нежелательные события, но в версии 5 единственным местом является eventDidMount, но это вызывается только один раз, поэтому, если движок снова отрендерит (т.е. щелкнет по изменению представления), события будут отображаться. снова и eventDidMount не будет вызываться.

Ivan 23.12.2020 11:50

Это интересно. Мне нужно рассмотреть его поближе, но сейчас нет времени... может быть, через несколько дней, когда Рождество закончится. Прокомментируйте здесь, если вы сделаете какой-либо прогресс до этого, то я не трачу время впустую :-).

ADyson 24.12.2020 11:07

Конечно, @ADyson, и счастливого Рождества!

Ivan 24.12.2020 11:09

почему вы не используете правильное пользовательское представление для событий с правильными отступами и полями?

saqib kafeel 26.12.2020 17:32
fullcalendar.io/docs/custom-view-with-js
saqib kafeel 26.12.2020 17:33

Спасибо @saqibkafeel, это еще один вариант. Я не использую пользовательские представления, потому что мне просто нужны классические представления, но они работают как положено. Я буду исследовать, может ли это решить проблему.

Ivan 26.12.2020 17:42

да, вы можете создать свой собственный вид как классический интерфейс просмотра

saqib kafeel 26.12.2020 17:44

@saqibkafeel Я пытался, но безуспешно. Может быть, вы можете дать ответ с данным codepen.

Ivan 27.12.2020 18:56
ibb.co/jT4sCNr @Ivan проверьте ссылку на это изображение, это результат вашего желания или вы хотите что-то еще?
saqib kafeel 27.12.2020 19:51

@saqibkafeel Нет, результат должен быть как на втором изображении, которое я разместил: i.stack.imgur.com/blrRH.png Т.е. когда событие не отображается, ему не нужно резервировать место для себя

Ivan 28.12.2020 18:54

@Ivan Я уже исправил проблему с помощью eventDidmout, если вы видите в моем ответе, в противном случае предоставьте мне правильный код codePed с проблемой производительности загрузки событий, я также отлажу его, спасибо, и я думаю, что баллы за вознаграждение должны увеличиться

saqib kafeel 29.12.2020 05:31
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
7
14
3 676
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Как вы уже заметили, CSS и JS здесь не помогут, потому что события размещаются с помощью position:absolute, поэтому удаление одного события (даже полное из DOM) не повлияет на отображение других. Единственный способ - удалить событие из календаря перед рендерингом.

Поэтому удалите событие вместо добавления класса:

eventClassNames: function(arg) { /* you can also use "eventDidMount" */
       if (arg.view.type != 'resourceTimeGridDay') {
          if (arg.event.extendedProps.real_rc != "1") {
             arg.event.remove(); /* HERE */
          }
       }
    },

Полный код:

document.addEventListener('DOMContentLoaded', function() {
  var calendarEl = document.getElementById('calendar');

  var calendar = new FullCalendar.Calendar(calendarEl, {
    initialView: 'timeGridWeek',
    initialDate: '2020-12-22',
    headerToolbar: {
      left: 'prev,next today',
      center: 'title',
      right: 'dayGridMonth,timeGridWeek,timeGridDay'
    },
    height: 'auto',

    eventClassNames: function(arg) {
       if (arg.view.type != 'resourceTimeGridDay') {
          if (arg.event.extendedProps.real_rc != "1") {
             arg.event.remove();
          }
       }
    },

    events: [
      {
        title: 'Test Resource 1',
        real_rc: '1',
        start: '2020-12-22 13:00',
        end: '2020-12-22 14:00'
      },
      {
        title: 'Also resource 1',
        real_rc: '1',
        start: '2020-12-22 13:30',
        end: '2020-12-22 14:30'
      },
      {
        title: 'Resource 1',
        real_rc: '1',
        start: '2020-12-22 16:00',
        end: '2020-12-22 17:00'
      },
      {
        title: 'Resource 2',
        real_rc: '2',
        start: '2020-12-22 15:00',
        end: '2020-12-22 17:00'
      }
    ]
  });

  calendar.render();
});
html, body {
  margin: 0;
  padding: 0;
  font-family: Arial, Helvetica Neue, Helvetica, sans-serif;
  font-size: 14px;
}

#calendar {
  max-width: 1100px;
  margin: 40px auto;
}
<link rel = "stylesheet" href = "https://unpkg.com/[email protected]/main.min.css">
<script src = "https://unpkg.com/[email protected]/main.min.js"></script>
<div id='calendar'></div>

Другая идея состоит в том, чтобы контролировать display события, как показано ниже:

document.addEventListener('DOMContentLoaded', function() {
  var calendarEl = document.getElementById('calendar');

  var calendar = new FullCalendar.Calendar(calendarEl, {
    initialView: 'timeGridWeek',
    initialDate: '2020-12-22',
    headerToolbar: {
      left: 'prev,next today',
      center: 'title',
      right: 'dayGridMonth,timeGridWeek,timeGridDay'
    },
    height: 'auto',

    eventDidMount: function(arg) {
       if (arg.view.type != 'resourceTimeGridDay') {
          if (arg.event.extendedProps.real_rc != "1") {
             arg.event.setProp( 'display', 'none' );
          }
       }
    },

    events: [
      {
        title: 'Test Resource 1',
        real_rc: '1',
        start: '2020-12-22 13:00',
        end: '2020-12-22 14:00'
      },
      {
        title: 'Also resource 1',
        real_rc: '1',
        start: '2020-12-22 13:30',
        end: '2020-12-22 14:30'
      },
      {
        title: 'Resource 1',
        real_rc: '1',
        start: '2020-12-22 16:00',
        end: '2020-12-22 17:00'
      },
      {
        title: 'Resource 2',
        real_rc: '2',
        start: '2020-12-22 15:00',
        end: '2020-12-22 17:00'
      }
    ]
  });

  calendar.render();
});
html, body {
  margin: 0;
  padding: 0;
  font-family: Arial, Helvetica Neue, Helvetica, sans-serif;
  font-size: 14px;
}

#calendar {
  max-width: 1100px;
  margin: 40px auto;
}
<link rel = "stylesheet" href = "https://unpkg.com/[email protected]/main.min.css">
<script src = "https://unpkg.com/[email protected]/main.min.js"></script>
<div id='calendar'></div>

Интерактивная демонстрация, в которой вы можете переключать отображение:

var rc = "1";

document.addEventListener('DOMContentLoaded', function() {
  var calendarEl = document.getElementById('calendar');

  var calendar = new FullCalendar.Calendar(calendarEl, {
    initialView: 'timeGridWeek',
    initialDate: '2020-12-22',
    headerToolbar: {
      left: 'prev,next today',
      center: 'title',
      right: 'dayGridMonth,timeGridWeek,timeGridDay'
    },
    height: 'auto',

    eventDidMount: function(arg) {
       if (arg.view.type != 'resourceTimeGridDay') {
          if (arg.event.extendedProps.real_rc != rc) {
             arg.event.setProp( 'display', 'none' );
          } 
       }
    },
    viewDidMount: function(arg) {
       var es = calendar.getEvents();
       for(var i=0;i<es.length;i++)
        es[i].setProp( 'display', 'auto' )
    },

    events: [
      {
        title: 'Test Resource 1',
        real_rc: '1',
        start: '2020-12-22 13:00',
        end: '2020-12-22 14:00'
      },
      {
        title: 'Also resource 1',
        real_rc: '1',
        start: '2020-12-22 13:30',
        end: '2020-12-22 14:30'
      },
      {
        title: 'Resource 1',
        real_rc: '1',
        start: '2020-12-22 16:00',
        end: '2020-12-22 17:00'
      },
      {
        title: 'Resource 2',
        real_rc: '2',
        start: '2020-12-22 15:00',
        end: '2020-12-22 17:00'
      }
    ]
  });

  calendar.render();
  document.querySelector("#toggle").addEventListener('click',function() {
    if (rc= = "1") rc = "2"; else rc = "1";
    /* trigger view change */
    calendar.changeView('dayGridMonth');
    calendar.changeView('timeGridWeek');
  });
});
html, body {
  margin: 0;
  padding: 0;
  font-family: Arial, Helvetica Neue, Helvetica, sans-serif;
  font-size: 14px;
}

#calendar {
  max-width: 1100px;
  margin: 40px auto;
}
<link rel = "stylesheet" href = "https://unpkg.com/[email protected]/main.min.css">
<script src = "https://unpkg.com/[email protected]/main.min.js"></script>
<button id = "toggle">toggle</button>
<div id='calendar'></div>

Спасибо, но у этого подхода есть проблема, как я упоминал в пункте № 4: вы удаляете события, поэтому вам нужно перезагрузить их, если есть представление об изменении. Это проблематично по двум причинам: во-первых, потому что AFAIK v5 не имеет хука для changeView, а во-вторых (что более важно), потому что вам каждый раз нужен дополнительный вызов AJAX (и это занимает много времени, потому что я получаю тысячи событий).

Ivan 28.12.2020 19:42

Извините, я нашел хук для изменения представления: viewDidMount. С этим крючком ваше решение осуществимо. Мне нужно сделать вызов AJAX, но, по крайней мере, это сработает.

Ivan 28.12.2020 19:49

@Ivan первый фрагмент удаляется, а второй только прячется. С отображением событие все еще существует, и вы все равно можете обновить его позже, я думаю (попробую обновить на примере)

Temani Afif 28.12.2020 20:09

Да, второй только скрывает, но eventDidMount вызывается только один раз при загрузке событий, поэтому, если resourceTimeGridDay - это вид при загрузке событий, то они не скрыты, а если нет, то они скрыты всегда. Ожидаемое поведение похоже на то, когда вы используете eventClassNames, который вызывается с каждым рендерингом события, поэтому с каждым изменением представления событие получает тот или иной класс.

Ivan 29.12.2020 09:48

@Ivan Я добавил еще один пример с переключением отображения при нажатии

Temani Afif 29.12.2020 10:55

Спасибо! Этот подход путем перечисления событий и изменения их свойств является новым для меня, и он хорошо работает в качестве обходного пути, пока эта ошибка полного календаря не будет исправлена.

Ivan 29.12.2020 12:59

Вы можете решить эту проблему, используя селектор CSS.

Я решил это в ответ, используя функцию рендеринга, которая возвращает компонент события с именем класса, например «my-event hidden», и добавил стиль, подобный следующему:

a.fc-event:has(.my-event.hidden) {
  position: absolute !important;
  top: -9999px !important;
  left: -9999px !important;
}

Другие вопросы по теме