Быстрые обновления:

Со времени последнего редактирования этой статьи я сделал некоторые обновления. Самая важная новая функция - это панель управления на домашней странице, которая позволяет пользователю работать с несколькими диаграммами.

В реализации создается новый маршрут chart и новый компонент ChartComponent для отображения отдельной диаграммы, а вместо этого HomeComponent будет отображать панель мониторинга. Как и следовало ожидать, база данных в localStorage на данный момент представляет собой массив диаграмм.

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

Я сделал. И почти все сделали.

Теперь искры в вашем уме, тогда придумайте способ управлять ими.

СПИСОК ЗАДАЧ !!!

Список задач? Эм ... это нормально, но что, если в большом списке есть несколько хороших идей? Может быть, вложенный список дел?

Как насчет хода и расписания? Что ж, у меня был плохой опыт продвижения приложения в первые два дня, но я закончил его через несколько месяцев. Если вы столкнулись с аналогичной проблемой, как и я, диаграмма Ганта может стать для вас решением.

Диаграмма Ганта

Что такое диаграмма Ганта? TL; DR. Диаграмма Ганта - это тип гистограммы, иллюстрирующий график проекта, названный в честь своего изобретателя Генри Ганта. Диаграмма Ганта - это тип столбчатой ​​диаграммы, которая иллюстрирует график проекта. На этой диаграмме по вертикальной оси перечислены задачи, которые необходимо выполнить, а по горизонтальной оси - временные интервалы. - Википедия

Я пробовал некоторые существующие решения для диаграмм Ганта, такие как шаблоны из Excel и Google Sheets. Но отслеживать вложенные элементы по-прежнему сложно.

Недавно я изучаю древовидную структуру данных и рекурсию, и использование Angular для ее создания - отличный способ реализовать эти знания.

Я смотрю на некоторые решения на GitHub, например angular-gantt и angular2-gantt-demo. Эти проекты классные, но они созданы либо с помощью AngularJS, либо с помощью Angular 2. Теперь я собираюсь использовать Angular 6 для создания моей собственной диаграммы Ганта.

Создайте приложение

Я закончил это приложение за неделю и управлял процессом сборки, используя его, как и было сказано в заголовке: Создать приложение для создания самого себя. Вот ссылки на репозиторий GitHub и демонстрацию на Heroku.

Представление и настройка проекта

Перед созданием приложения нам нужно настроить среду. После использования последней версии Angular-CLI для инициализации проекта нам также необходимо установить самые популярные UI-фреймворки в Angular: Angular Material и Angular Flex-Layout для макет.

ng new angular-gantt-chart --style=scss
cd angular-gantt-chart
npm install @angular/cdk @angular/material hammerjs @angular/flex-layout --save

В приложении мы будем активно использовать Angular Material для компонентов, особенно Компонент дерева, который предоставляет интерфейс для рендеринга вложенных элементов. Кроме того, приложение будет использовать много вложенных гибких элементов для реализации пользовательского интерфейса.

Вот необходимые зависимости для проектов. При необходимости мы установим другие пакеты в ходе дальнейшего обсуждения.

Для глобального style.scss мы заранее определяем некоторые классы для макета и цветов.

Затем мы создадим единственный компонент в приложении: ng g component home.

Хотя в этом проекте есть только один компонент - HomeComponent, мы все же настроили маршрутизатор, создав папку app-routing и настроив модуль маршрутизатора.

Как вы видите в предварительном просмотре проекта, вы можете подумать, что приложение содержит четыре компонента: названия шагов, выбор даты, прогресс и календарь. Но приложение тесно связано, потому что четыре компонента используют один и тот же источник данных, и мы не повторно визуализируем дерево после изменения источника данных.

Так же, как и маршрутизатор, мы также настраиваем модуль материалов: app-material/material.module.ts.

Затем в app.module.ts мы импортируем все упомянутые выше модули.

Постройте древовидную структуру

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

  1. Модель данных

Перед созданием приложения я хотел бы поговорить о модели данных для дерева материалов Angular, которая поможет понять коды.

Другими словами, база данных Core Tree DB является корневым экземпляром класса Step. Для класса Step name - это имя шага; progress - процент прогресса шага, диапазон от 0 до 100; dates - дата начала и дата окончания Step; progressDates записывает даты, охваченные текущим progress между dates; steps являются потомками текущего Step; expanded - статус структуры родительского узла, показывать ли дочерние узлы.

В базе данных дерева материалов StepFlatNode - это класс, используемый Angular Material для построения визуализации дерева. Он очень похож на класс Step, но включает некоторые другие свойства: expandable для обнаружения любых дочерних элементов внутри; level для уровня дерева, а корень - уровень 0. И вы заметите, что дочерних узлов нет, потому что Материал сгладит дерево.

Для потоков данных база данных Core Tree DB инициализирует BehaviorSubject из Step, затем Angular внедрит базу данных Core Tree DB в HomeComponent, что позволяет ему вызывать функции базы данных Core Tree DB.

2. Построить дерево (только для чтения)

В этом разделе мы собираемся использовать Плоское дерево из Angular Material, чтобы реализовать наши шаги вложенным способом.

Перед построением дерева мы вводим момент и момент-диапазон. Обе прекрасные библиотеки для обработки данных о дате, которые мы будем активно использовать в приложении. Кроме того, мы будем использовать angular-resizable-element, чтобы изменить размер боковой панели.

npm install moment moment-range angular-resizable-element --save

В этой части мы реализуем односторонний поток данных.

Позвольте мне прояснить основные части кодов. Основная часть HTML и TypeScript основана на примерах из документации, но они пересматриваются в зависимости от проекта.

В файле SCSS я определяю высоту узла, и это помогает, чтобы высота заголовка была такой же, как высота узлов дерева. Я также должен рассчитать ширину и высоту границы, чтобы они выровнялись.

На этом шаге так много кодов, а результат выглядит тривиальным.

Но это краеугольный камень для создания остальных частей приложения.

2. Постройте дерево (доступно для редактирования)

В этой части мы попытаемся реализовать возможности CRUD для дерева. Для этого нам нужно добавить внутренние функции как для баз данных, так и для новых элементов пользовательского интерфейса.

В этой реализации мы используем структуры Map в flatNodeMap и nestedNodeMap. Эти две карты помогают построить отражение между Step и StepFlatNode. Всякий раз, когда в дереве происходит изменение, treeControl будет использовать карты для поиска узла. Как только узел будет обнаружен, он передаст узел и действие в основную базу данных.

Как вы можете видеть в предварительном просмотре приложения, рядом с корневым шагом (уровень 0) нет кнопки удаления и нет кнопок добавления для шагов на уровне 3. Это предотвращает случайное удаление пользователем всего проекта или зайти слишком далеко.

Затем в обратном распространении субъект dataChange вызовет функцию next, чтобы передать данные и повторно сгенерировать дерево.

Но есть исключение. Если пользователь только обновляет свойства, он не требует и даже не должен повторно создавать дерево. В приведенном ниже примере повторная визуализация дерева прерывает ввод данных пользователем.

Отдельно стоит упомянуть функцию getParentStep. Если задан StepFlatNode, он обнаружит родительский узел, который требуется, когда пользователь хочет удалить шаг.

getParentStep(node: StepFlatNode) {    
  const { treeControl } = this;
  const currentLevel = treeControl.getLevel(node);
  // if root, ignore    
  if (currentLevel < 1) {      
    return null;    
  }    
  const startIndex = treeControl.dataNodes.indexOf(node) - 1;    
  // loop back to find the nearest upper node    
  for (let i = startIndex; i >= 0; i--) {      
    const currentNode = treeControl.dataNodes[i];      
    if (treeControl.getLevel(currentNode) < currentLevel) {    
      return currentNode;      
    }    
  }  
}

Сначала я подумываю об использовании рекурсии для поиска родительского узла. Но Материал предоставляет интерфейс под названием dataNode для класса TreeControl для хранения узлов в режиме DFS (поиск в глубину) в случае, все, что нам нужно сделать, это вернуться к массиву, пока мы не найдем родителя.

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

3. Создание календаря

Календарь - еще одна важная особенность диаграммы Ганта. Он покажет прогресс каждого шага и предоставит пользователю хорошее понимание того, как все работает.

Вы можете заметить, что все ячейки в календаре серые из-за отсутствия прогресса на каждом шаге. Если мы вручную изменим localStorage, это будет выглядеть более значимым.

В последнем превью было три типа ячеек. Синие - текущий прогресс; серые означают оставшийся прогресс, а белые исключаются из шага.

В этой части используются две новые переменные. Один - dates для заголовка календаря, другой - today, который используется для захвата столбца сегодняшнего дня в календаре и его выделения.

Функция buildCalendar генерирует свойство progressDates для каждого узла. В шаблоне HTML для каждой строки календаря он будет зацикливать каждую дату, чтобы определить, какому статусу (текущему, оставшемуся, исключительному) он принадлежит, и назначить ему соответствующий класс.

В файле SCSS вы можете заметить, что некоторые роли начинаются с ::ng-deep. Таким образом, мы можем применить стили к элементам внутри <material-tree-node>.

Стоит отметить, что столбец текущей даты будет иметь выделенную рамку.

4. Выбор даты и индикатор выполнения

Прежде чем мы создадим эти два компонента, необходим пакет под названием @ angular / material-moment-adapter. Этот пакет использует момент внутри выбора даты материала и помогает преобразовать формат даты YYYY-MM-DD в точное местное время.

npm install @angular/material-moment-adapter --save

В этой части SCSS сложно. Нам нужно взломать стандартный компонент Материал и настроить его. Цель состоит в том, чтобы выровнять нижнюю строку средства выбора даты и ползунка с календарем.

Для TypeScript стоит упомянуть частичное обновление дерева. Поскольку мы не меняем структуру узлов, все, что нам нужно сделать, это сохранить изменения, а затем перестроить календарь на основе дат и прогресса.

Внутри календаря, чтобы пользователь не ввел недопустимую дату, мы ограничиваем min dates.start и max dates.end родительского шага. Для этого мы бы повторно использовали ранее упомянутую функцию getParentStep для получения дат.

5. Заключение

Молодцы, ребята! Спасибо за то, что приняли это длинное руководство, и я надеюсь, что вы успешно построили свою собственную диаграмму Ганта.

Пожалуйста себя !!!

С технической точки зрения, после создания этого приложения вы, возможно, получите базовое представление о древовидной структуре и лучше познакомитесь с Angular Material, Angular Flex-layout и Moment.

Вот список некоторых функций или улучшений, которые могут сделать это приложение лучше.

  • ✅ A̶ ̶d̶a̶s̶h̶b̶o̶a̶r̶d̶ ̶p̶a̶g̶e̶ ̶a̶n̶d̶ ̶s̶u̶p̶p̶o̶r̶t̶ ̶m̶u̶l̶t̶i̶p̶l̶e̶ ̶p̶r̶o̶j̶e̶c̶t̶s
  • Фиксированный заголовок позволяет просматривать календарь при прокрутке вниз
  • Изменить порядок шага
  • Перетащить и отпустить
  • Подходит для мобильных устройств (может быть ???)

Если что-то не получается, не стесняйтесь оставлять свои комментарии и идеи, а также заглядывать в мой репозиторий GitHub.

Жду ваших ценных отзывов. 😝