Skip to content

Marisha-tech/TodoFlowers

Repository files navigation

TodoFlowers

TodoFlowers - список задач ухода за растениями

Проект создан с помощью Angular CLI версии 12

Данное приложение было создано для ведения учета ухода за растениями

Приложение построено на базе шаблона https://demos.creative-tim.com/material-dashboard/pages/dashboard.html?_ga=2.217451606.1122372560.1652763608-1150030646.1647338199

В приложении используются след библиотеки:

  • Bootstrap
  • AngularMaterial
  • Intro - это облегченная библиотека JavaScript для создания пошаговых ознакомительных туров для клиентов
  • ngx-color-picker - готовый компонент для выбора цвета в палитре

Архитектура приложения построена на базе паттернов проектирования:

  • DAO (Data Access Object) - слой между данными и бизнес-процессами. Используется для расширяемости. В интерфейсах - описание. В классах – реализация. Сервисы (@Injectable) -> DAO (Классы, Интерфейсы) -> Данные (любой источник)
  • Smart component и Dump component.

Smart компонент - управляет другими компонентами, предоставляет данные.

Dumb компонент - получает данные и отображает их, обрабатывает действия пользователей и отправляет их на обработку в Smart компонент.

Данные находятся в классе TestData в виде массивов.

В header размещены:

  • кнопка отображения/скрытия меню категорий («бургер»)
  • вывод наименования текущей выбранной категории
  • скрытие/показ статистики задач
  • иконка настройки приложения
  • краткая пошаговая инструкция приложения

В контентной части размещены:

  • Статистика задач
  • Поиск задач
  • Фильтр по статусам
  • Фильтр по приоритету
  • Кнопка «Добавить задачу»
  • Кнопка «Скрыть фильтр» - отображается после того, как была использована фильтрация
  • Список задач

В footer размещены:

  • информация о сайте
  • Сайт - ссылка на сайт
  • О приложении – диалоговое окно с информацией о приложении

Шаблон

https://www.creative-tim.com/product/material-dashboard/?partner=49926

Подключение bootstrap

https://github.com/fyockm/bootstrap-css-onl

npm install bootstrap-css-only

После чего в папке node_modules создана папка bootstrap-css-only

Установка библиотеки Angular Material

https://material.angular.io/guide/getting-started

ng add @angular/material

После чего появится папка node_modules/@angular/material

DI (Dependency Injection)

Dependency Injection - Готовый механизм 'из коробки' Angular

Внедрение зависимости - получение ссылки на экземпляр класса в любом месте, где это необходимо

CategoriesComponent получает доступ к DataHandlerService

Экземпляры компонентов и сервисов создаются автоматически при запуске приложения (либо при первом обращении к ним)

Directive

Directive - Директива, инструкция, указание

Инструкция движку Angular для обработки тега

Изменяет/дополняет поведение тега

Component Directives - подставляет содержимое компонент

Attribute Directives - изменяет поведение/отображение тега: [title]=”titleVar”

Structural Directives - изменяет структуру: *ngIf, *ngFor (все начинаются с символа *)

https://angular.io/guide/attribute-directives https://angular.io/guide/structural-directives

Компонент - это тоже директива

@Component - декоратор - добавляет метаданные к классу, который обрабатывает движок Angular

Когда добавляем тег компонента в HTML - Angular обрабатывает его и добавляет весь функционал компонента

Binding

Binding - связывание чего-либо из HTML (тег, действие, значение) с чем-либо из класса (метод, переменная)

Event binding - связывание событий

С помощью этого механизма обрабатываются почти все действия пользователя

(click) - событие нажатия мышкой, которое можно обработать в классе TS

Доступно множество готовых HTML событий (двойное нажатие, наведение и пр.)

Реактивное программирование (РП)

Reactive programming (от слова "реакция") - программирование с асинхронными потоками данных

Получаете данные, вызывается метод - всё это "потоки"

Поток - это контейнер для реализации принципов РП

Асинхронные вызовы (не зависает UI) - как только получаем результат - обновляем UI

Принципы "издатель-подписчик" (Observable - за кем наблюдают, observer - кто наблюдает) - нет жесткой связи между объектами, каждый сам выбирает какие данные "слушать" (пример "Excel" - в одном месте меняете значение, в других местах обновляется)

Принцип отопок ("всё - потоки") - можно комбинировать, вкладывать друг в друга и пр. - собирать итоговый вариант как конструктор

RxJS

https://rxjs.dev/

Реализация РП в JavaScript (TypeScript)

Angular изначально создавался под РП - частичное обновление страниц, быстрый отклик UI, автоматические Ajax запросы

Вручную не нужно выполнять Ajax запросы - все за вас делают Angular + RxJS

Объекты в RxJS

Объекты в RxJS - готовые решения (контейнеры) для асинхронных вызовов с различным поведением

Нужно "обернуть" данные в контейнер и любой может подписаться на их изменение

Observable - объект, за которым наблюдаем (базовый класс)

Subject - можно слушать изменения и реагировать на них (сам может подписывать на другие Observable)

BehaviorSubject - хранит последнее значение

RelaySubject - хранит N последних значений

Observer (подписчик) не работает напрямую с данными, а только "слушают" изменения и получают обновления

Pipe

Pipe - позволяет преобразовать значение в любой другой вид

Pipe - класс TS, который "прогоняет" через себя значение и на выходе дает измененный результат. Например, значение даты в нужный формат

Преобразования могут быть любыми (можно создавать свои классы pipe, если нужно специфическое преобразование)

Можно использовать сразу на странице HTML

Attribute directive

Можно динамически работать со стилями - включать или исключать классы для тега

Angular заменяет готовое решение, которое заменяет jQuery и другие JS-коды для динамической работы со стилями

class.active

checked

title

Если фигурных скобок нет - будет присваиваться текст, а не вычисленное значение

С помощью директивы атрибутов можно указывать динамическое значение из переменной. Значение указывается в двоынйх кавычках [title]="titleValue", где [title] - атрибут тега, "titleValue" - любое значение, переменная, true/false

Не путать

(click) - event binding - действие, событие, которое вызовет метод из класса

{{'значение из переменной'}} - интерполяция, применяется в любом месте на странице (но не для значения атрибута)

[title] = “titleValue” - attribute directive, считывание значения из переменной titleValue и запись ее в атрибут title.

Если не будет скобок [], то в атрибут title присвоится текст titleValue

One-way binding - односторонняя связь

[class.completed] = “task.completed” - включение или исключение для тега селектора .completed из CSS

Таблица

mat-table( mat - от слова material) - Специальный компонент для отображения списка объектов. Встроенные возможности для постраничности, сортировки. Будем отображать список задач (вместо таблицы, которая предоставляется в шаблоне)

https://material.angular.io/components/table/

Компонент находится внутри библиотеки Angular Material (ранее подключали через npm) https://material.angular.io/guide/getting-started

Импорт

Библиотека @angular/material в файле package.json (обычно добавляется автоматически при установке библиотеки через npm)

    Модули
  • MatTableModule
  • MatPaginatorModule
  • MatSortModule
  • BrowserAnimationsModule
    Модули должны быть импортированы в 2-х местах в файле app.module.ts (Иначе функционал просто не будет работать, хотя никаких ошибок может не отображаться):
  • import в начале файла
  • imports в @NgModule

Тема

Подключить тему, в которой уже прописаны многие стили компонентов Angular Material

Выбираем тему deeppurple

Можно выбирать любую другую: https://material.angular.io/guide/theming

Прописать импорт в styles.css: @import '~@angular/material/prebuilt-themes/deeppurple-amber.css';

DataSource

Mat-table использует datasource - контейнер с данными и доп. возможностями

Сортировка и пагинация

Будем использовать: [ngClass] - включение/исключение классов CSS*ngIf - условие для отображения тега

mat-table - готовые решения для сортировки и постраничности

Можно задавать свои правила сортировки (по какому полю объекта выполнять сортировку) с помощью SortingDataAccessor

Последовательность действий - применить attribute directives: matSort - для таблицы; mat-header-cell - для каждого столбца, по которому нужно сортировать

Если использовать @ViewChild, то с данными можно работать только после ngOnInit(){}, например в ngAfterViewInit(){}

Жизненный цикл компонента - LifeCycle

https://angular.io/guide/lifecycle-hooks

LifeCycle - набор методов, которые вызываются на разных этапах работы компонента (от создания до уничтожения)

Нужно знать, для правильной инициализации переменных (чтобы не было ошибки undefined)

Без полного знаний как работает Angular - нет смысла запоминать все методы. Только по мере их использования (понимания)

constructor
ngOnChanges
ngOnInit
ngDoCheck
ngAfterContentInit
ngAfterContentChecked
ngAfterViewInit
ngAfterViewChecked
ngOnDestroy

DAO (Data Access Object) - паттерн (шаблон)

Используется для того, чтобы код был расширяемым, правильно написанным

DAO - слой между данными и бизнес-процессами

В интерфейсах - описание. В классах - реализация

Доступ к данным не напрямую, а через “интерфейс”

Для чего: формализация, расширяемость, логичность. Часто используется на backend

Business Layer <-> DAO (CRUD: Create, Read, Update, Delete). Сам DAO для доступа к БД может использовать что угодно (JDBC API, Hibernate API) <-> DB

https://www.netguru.com/codestories/top-5-most-used-patterns-in-oop-with-typescript

Реализация DAO - Сервисы + DAO

Сервисы (@Injectable) -> DAO (Классы, Интерфейсы) -> Данные (любой источник)

!!!!!НЕЛЬЗЯ!!!!! Сервисы -> Данные

Сервисы напрямую не обращаются к данным - только с помощью DAO-реализации

Если необходимо добавить новые методы - добавляем в:

  1. Интерфейсы
  2. Реализации
  3. Сервисы

Принципы подхода:

  • Не зависим от источника данных - можем подключать любой источник (БД, удаленный сервер, который передает JSON и тд), главное - соблюдать интерфейсы
  • Виден весь возможный API из интерфейсов - можно оценить общий функционал (какие есть возможности и тд)
  • Единая точка взаимодействия с данными

Декоратор @Input

@Input - декоратор для передачи входящих параметров компонента

https://angular.io/guide/template-syntax#input-and-output�properties

Сервис передает нужные данные компоненту (после какого-либо события или при инициализации)

@Input - автоматически подписывается на изменение данных (как с Observable и subscribe) и заменяет по смыслу

Видны все входящие данные для компонента (все @Input параметры)

Компонент может иметь ряд @Input переменных, нужные для отображения данных

Компонент сам напрямую не запрашивает данные, ему их передает сервис (по необходимости)

Parent и Child компоненты

Parent - родитель - в нашем случае AppComponent

Child - дочерний - в нашем случае TasksComponent

AppComponent (parent)

  • Подписывается на все необходимые данные (subscribe) из DAO
  • “Раздает” данные всем дочерним элементам
  • Собирает итоговую страницу из компонентов как “конструктор”

TasksComponent (child)

  • Ждет входящие данные через @Input (как только данные изменятся, @Input это увидит и обновит переменную)
  • Отображает данные в своем view

Сохранить в закладку “Cheat sheet” (шпаргалка) https://angular.io/guide/cheatsheet

Прочитать только до раздела @Output https://angular.io/guide/template-syntax#input-and-output-properties

=======

![ac1b7e67c9](https://user-images.githubusercontent.com/51979512/161208176-e6985301-071f-45d2-8ef4-bb15139eccc8.png)

@Output

https://angular.io/guide/template-syntax#how-to-use-output

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

@Output - Способ передачи данных от дочернего компонента к родительскому

В одном компоненте могут быть и @Input и @Output

С помощью @Input и @Output:

  • реализовывается принцип слабой связанности компонентов
  • инкапсуляция компонентов (взаимодействуем с компонентом только с помощью @Input и @Output и не знаем всей “внутренней кухни”)

Диалоговые окна

https://material.angular.io/components/dialog/overview

MatDialog - готовое решения для работы с диалоговыми окнами из Angular Material

Гибкие настройки, передача данных, изменение внешнего вида

Создадим отдельный компонент EditTaskDialog для создания/редактирования задачи, который будет помещен в диалоговое окно

В файле app.module.ts:

  • Импорт MatDialogModule
  • Добавить в entryComponents

Void - Указывает, что метод ничего не возвращает. При попытке добавить return - будет ошибка компиляции

Для всех методов желательно указывать void или возвращаемый тип, чтобы облегчить работу компилятора и решать многие проблемы на этапе компиляции. Если тип метода не указан - можно возвращать или не возвращать значение

Two-way data binding

https://angular.io/guide/template-syntax#two-way-binding

Two-way data binding - Двустороннее связывание.

Считывает и при изменении в HTML (пользователь ввел данные) - записывает значение

Используется везде, где пользователь изменяет значения, в формах, input - компонентах

One-way data binding (read-only):
  • Interpolation {{}} - считать свойство и отобразить в HTML
  • Property Binding [ ] - считать свойство и атрибут
  • Event Binding ( ) - обработка действия пользователя (метод)
Two-way binding:
  • [()] - совмещает считывание свойства атрибута и обработка действия пользователя

Директива [(ngModel)] - Используется в тегах, где можно изменить/выбрать данные (текстовое поле, списки и пр.). Считывает значение в элемент и при изменении - записывает в переменную

Выбор даты - компонент MatDatepicker

https://v12.material.angular.io/components/datepicker/overview

MatDatepicker - готовый выпадающий календарь. Связываем с текстовым полем input

В app.module.ts импортировать: MatNativeDateModule и MatDatepickerModule

Pipes

https://angular.io/guide/pipes

Pipe - Готовый набор различных “пайпов”. Можно создавать свои “форматтеры”

mat-checkbox

https://material.angular.io/components/checkbox/overview MatCheckboxModule - добавить в add.module.ts

Если на строке “висят” также и другие действия(редактирование задачи и пр.) - желательно сначала выполнять метод $event.stopPropagation()

$event.stopPropagation() - чтобы дальше не выполнялись никакие действия

Smart and Dumb components

Архитектура

Smart компонент - управляет другими компонентами, предоставляет данные

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

Одна из задач - правильно разбить приложение на Smart и Dumb компоненты

Избегаем “каши” из компонентов, которые взаимодействуют не формализовано

Четкое понимание API - как взаимодействуют компоненты

Один из способов решения: архитектура “Smart and Dumb components”:
  • Взаимодействие с компонентами преимущественно через декораторы @Input и @Output
  • @Input не должны изменяться внутри dumb компонента (это делает smart)
  • Разделить компоненты-представления (Presentational, Dumb, Pure) и компоненты�менеджеры (Smart, Container)

Структура

Сервисы - AppComponent (вся бизнес-логика) - TodosComponent (отображение) @Input @Output и CategoriesComponent (отображение) @Input @Output - Другие Dump компоненты

AppComponent - smart, container:
  • Собирает всю страницу из других компонентов
  • Управляет данными дочерних компонентов
  • Реагирует на действия дочерних компонентов

Минусы: при сложном дереве компонентов - трудно передавать параметры и отслеживать события (решение - использовать библиотеку для хранения состояний, например

TodosComponent, CategoriesComponent - dumb, presentational, pure
  • Получают данные через @Input и отображают
  • Не знаю откуда данные берутся, их задача - просто отобразить
  • При необходимости - отправляют действия пользователя с помощью @Output в Smart-компонент
  • Напрямую не обращаются к данным

Каждый компонент должен заниматься только своим делом

**Smart** компоненты нужно разделять логически, в зависимости от круга задач - в нашем случае достаточно одного: **AppComponent**

Содержит все бизнес-процессы, может запрашивать данные

**Dumb** - все компоненты для отображения

Можно изменять внутреннюю реализацию, но это не отразится на работу приложения (т.к. API из набора @Input и @Output не меняется)

Легче тестировать

Выбор цвета

https://www.npmjs.com/package/ngx-color-picker

ngx-color-picker - готовый компонент для выбора цвета в палитре

ColorPickerModule - добавить в app.module.ts

Intro

https://introjs.com/

intro.js - Популярная библиотека на JavaScript, у которой существует адаптированная версия под Angular

npm install intro.js --save - установка

Слайд-меню

https://www.npmjs.com/package/ng-sidebar

ng-sidebar

Боковое слайд-меню формируется с помощью дополнительных тегов. Позволяет реализовать меню с любой стороны и по различным настройкам

npm install --save ng-sidebar

Мобильная версия

Основная задача - отображать весь контент на мобильных устройствах

Верстка под небольшие экраны мобильных устройств

Используем Google Chrome для проверки (device toolbar)

Заменяем таблицу с задачами отдельным списком задач (карточки)

Боковое меню будет иметь свои настройки для мобильной версии

Большинство компонентов Angular Material уже адаптированы под моб. Устройства

Если пользователь зашел в приложение через мобильное устройство - используем мобильную верстку, иначе - используем обычную верстку

DeviceDetector - Позволяет определять тип устройства. Внедряется с помощью DI как сервис в любой компонент (https://www.npmjs.com/package/ngx-device-detector)

npm i ngx-device-detector --save

TTFB - Производительность приложения

TTFB - Time To First Byte (время до первого байта) - полезен для работы с клиентами или группой тестирования

Можно "отловить" медленную работы сервера (например, закончилась оперативная память и сервер "свопит" данные на жесткий диск и только потом "отдает")

Медленная работа жесткого диска (битые сектора и пр)

Медленная сеть на стороне сервера

Performance

Performance - производительность приложения в Angular

Как узнать на чьей стороне проблема?

    Базовые причины медленной работы приложения:
  • Лишняя отрисовка (обновление всего дерева компонентов, а не только то, что изменилось)
  • Синхронные запросы (неиспользование RxJS)
  • Нагрузка сети, жесткого диска, ОЗУ и пр. - как у клиента, так и у сервера
  • антивирусы, фаерволы

С помощью инструмента Google Chrome Developer Tools можно проверить производительность

Кэширование браузера - ускоряет работы приложения

HAR file (HTTP Archive)

HAR file - полезен для работы с клиентами или группой тестирования

Как только кто-то нашел баг в отображении - можно сформировать HAR файл и отправить для анализа

Файл могут формировать почти все современные браузеры

Heap snapshot

https://developer.chrome.com/docs/devtools/memory-problems/heap-snapshots/

https://developer.chrome.com/docs/devtools/memory-problems/memory-101/

https://www.yourkit.com/docs/java/help/sizes.jsp

https://slides.com/gruizdevilla/memory - Поиск и отладка утечек памяти в JavaScript с помощью Chrome DevTools

Heap snapshot - в консоли вкладка "Memory" - кнопка "Take heap snapshot"

Главная задача - избежать memory leak - объекты не удаляются из памяти инструментов Garbage Collector, а накапливаются и "забивают" память

GC удаляет те объекты, на которые никто не ссылается

Distance - расстояние от GC root до объекта

Heap snapshots distance

Подробно по значениям сети

memory leak

Компиляция в Angular

Типы компиляции:

JIT - Just-in-Time (JIT) - вовремя, по необходимости

  • По-умолчанию использовался, когда запускали приложение на тестовом сервере node.js
  • Компилируется в браузере
  • Занимает больше места, т.к. в итоговую папку добавляется компилятор
  • Подходит для локальной разработки, тестирования
  • ng build

AOT - Ahead-of-Time (AOT) - раньше времени, заранее

https://angular.io/guide/aot-compiler

  • Компилируется на машине, где запущена команда (работает быстрее)
  • Занимает меньше места, т.к. в итоговую папку НЕ добавляется компилятор
  • Более безопасный вариант (т.к. код и служебные данные лучше скрываются)
  • Подходит для production
    • ng build --prod (рекомендуется)
    • ng build --aot
  • Переменные класса не должны быть private

Development mode

В зависимости от режима - в коде можно добавлять различные условия (получать реальные данные, вместо тестовых, создавать различные значения переменных и пр.)

Готовая функция isDevMode() - прописывать в компоненте

Для нашего проекта неважно, т.к. это не используем