меню

Я начал изучать Сигналы, так как это кажется горячей темой сегодняшнего дня, и я хотел написать пост о том, что открыл, а также создать демонстрационное приложение, которое (надеюсь) позволит легко понять Angular Signals.

Версия Angular 16 была выпущена в начале мая, и эта версия полна новых функций. Но одной из особенностей, о которой все говорят, является новая возможность - Сигналы. Это настоящий поворотный момент, который находится в стадии предварительного просмотра разработчиками и будет выпущен позже в этом году, в версии 17.

Что такое Angular Signals?

Сигналы используются для уведомления заинтересованных сторон о изменении значения. Сигнал может содержать любой тип значения, начиная от простых примитивов (строка, число, булево значение) до сложных структур данных (например, объекта или массива). Чтобы отслеживать, где используется сигнал, Angular всегда обращается к значению сигнала через функцию-геттер. Сигналы могут быть только для чтения или иметь возможность записи. Для обновления значений в записываемых сигналах используется API. Чтобы создать записываемый сигнал, вызовите функцию signal, передавая начальное значение.

Сигналы (Signals)

Сигналы имеют следующие методы:

  • Set — используется для установки нового значения.
  • Update — используется для обновления ранее установленного значения.
  • Mutate — используется для обновления сложных объектов.
  • Effect — этот необязательный метод вызывается при изменении значения (подробнее о эффектах см. ниже).

Объявление сигнала — необходимо задать начальное значение для сигнала.

 
//инициализация сигнала
const elementType = signal('animate');
 

Для установки нового значения мы можем использовать метод set.

  
//set-им новое значение 
const elementType.set(value.elementType);
 

Для обновления сигнала с предыдущим значением мы используем метод update.

 
//update-им значение
const elementType.update(value => value.elementType);
 

Метод Mutate используется, когда сигнал содержит объект. Вместо создания и добавления нового объекта (что тоже возможно), вы можете обновить этот объект напрямую.

  
//mutate - им значение
const element = signal([{elementTitle:'animation', elementType:'Function'}]);
element.mutate(value => {value[0].elementType = 'Class'});
 

Вычисляемый сигнал получает свое значение из других сигналов и вычисляется лениво, а его значение кэшируется. Это означает, что вычисляемый сигнал не будет вычислен, пока он не будет прочитан; после чтения его значение кэшируется. В приведенном ниже примере значение fullname не будет вычислено до тех пор, пока оно не будет прочитано. После этого результат кэшируется, и все последующие чтения будут использовать кэшированный результат. Если изменятся значение firstname или lastname, то fullname будет пересчитано.

 
//compute-им значение
const firstname = signal('firstname');
const lastname = signal('lastname');
const fullname = computed(() => `${firstname()} ${lastname()}`);
console.log(fullname());

//Вывод --> firstname lastname
 

Когда значение сигнала изменяется, вызывается эффект. Эффект будет запущен как минимум один раз и будет отслеживать чтения сигнала; когда значение изменяется, эффект снова запускается. Эффекты могут быть полезны в некоторых случаях, например:

  • Запись данных в консоль для отладки.
  • Синхронизация с локальным/сеансовым хранилищем.
  • Пользовательское поведение DOM.
  • Пользовательская отрисовка (диаграммы, холст или другие компоненты пользовательского интерфейса).
  
effect(()=> {
  console.log(`Fullname: ${this.fullname()}`);
}
  

Давайте создадим наше приложение и приступим к работе с некоторыми из них.

Приложение, которое мы построим (или по крайней мере частично построим в этом посте), будет основано на таблице Менделеева. Вместо использования элементов, мы будем использовать API Angular.

Поскольку мы используем Angular 16, мы теперь можем создать новое автономное приложение из командной строки (также нововведение в версии 16). В этом посте мы не будем описывать все шаги для настройки этого проекта, в конце будет ссылка на репозиторий GitHub. Чтобы создать новое автономное приложение из командной строки, введите:

 
ng new <project-name> --standalone
 

Мы создадим файл TypeScript - mockApiData и заполним его массивом объектов. Я приведу здесь только часть кода, чтобы дать вам представление.

  
export const mockApiData = [
  {
    elementType: 'F',
    title: 'animate',
    description:
      'A multi-provider token that represents the array of registered HttpInterceptor objects.',
    color: '#4caf50',
    group: 'animations',
    type: 'Function',
  },
  {
    elementType: 'I',
    title: 'AnimateChildOptions',
    description:
      'Adds duration options to control animation styling and timing for a child animation.',
    color: '#009688',
    group: 'animations',
    type: 'Interface',
  },
  {
    elementType: 'C',
    title: 'AnimationDriver',
    description: '',
    color: '#2196F3',
    group: 'animations/browser',
    type: 'Class',
  },
];
  

В этом компоненте есть две части. Первая часть представляет собой раздел, который соответствует элементу из таблицы Менделеева, но мы заполним его данными mockApiData, указанными выше.

  
<ng-container *ngFor="let api of mockData">
  <div
    class="shape panel element-type"
    [style.background-color]="api.color"
    (click)="loadDetails(url())"
    (mouseenter)="mouseEnter(api)"
  >
    <div>
      <div class="element-type">{{ api.elementType }}</div>
    </div>
    <div>
      <div class="description">{{ api.title }}</div>
    </div>
  </div>
</ng-container>
  

Главный момент, на который нужно обратить внимание в этом коде, это событие клика. Здесь мы используем сигналы для передачи URL выбранного элемента в обработчик событий в файле кода. URL устанавливается изначально при загрузке страницы (см. код ts файла ниже). Когда мы наводим курсор на плитку, значение elementType изменяется. Затем событие клика считывает это новое значение и передает его обработчику событий.

Давайте посмотрим на файл кода, чтобы понять, что происходит.

  
export class PodComponent {
  // Заполняем данные из mockApiData.
  mockData = apiData;

  // Настраиваем начальные сигналы, получая первый элемент в массиве.
  elementType = signal(this.mockData[0].elementType);

  title = signal(this.mockData[0].title);

  description = signal(this.mockData[0].description);

  color = signal(this.mockData[0].color);

  group = signal(this.mockData[0].group);

  // Здесь мы вычисляем URL, считываем сигналы группы и заголовка.
  url = computed(() => `${API_URL}/${this.group()}/${this.title()}`);
}
 

Как упоминалось в начале этого поста, для сигналов требуется значение по умолчанию. Для этого мы создаем переменную, чтобы сохранить значение первого elementType в массиве данных в качестве сигнала, и мы делаем то же самое для заголовка, описания, цвета и группы. Последняя переменная (url) немного отличается: здесь нам нужно собрать URL, который указывает на документацию Angular API для выбранного элемента. Это отличное использование вычисляемой функции, так как мы можем получать данные из других сигналов, изменять их и возвращать новое значение. Вычисляемый сигнал не может быть записываемым, вы должны предоставить один или несколько сигналов и/или другое значение.

Мы можем сделать что-то подобное в событии mouseEnter, на этот раз передавая наведенный элемент. Вместо использования метода set снова, на этот раз мы будем использовать метод update.

 
mouseEnter(value: {
      elementType: string;
      title: string;
      description: string;
      color: string;
      group: string;
    }): void {
      this.elementType.update(() => value.elementType);
      this.title.update(() => value.title);
      this.description.update(() => value.description);
      this.color.update(() => value.color);
      this.url = computed(() => `${API_URL}/${this.group()}/${this.title()}`);
  }
  

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

  
<div class="container">
  <div class="shape element-type" [style.background-color]="color()">
    <div>{{ elementType() }}</div>
  </div>

  <div>
    <div>{{ title() }}</div>
    <div>{{ description() }}</div>
    <div>{{ url() }}</div>
    <button mat-button (click)="loadDetails(url())">API Docs</button>
  </div>
</div>
  

В приведенном выше коде мы теперь можем считывать сигналы для elementType, title, description и URL. Мы также можем считывать сигнал и передавать его в событие клика loadDetails. Мы даже можем считывать значение сигнала для цвета и в этом примере устанавливать цвет фона плитки.

Angular App

Это всего лишь малая часть приложения, и я планирую расширить его в ближайшие недели и написать больше о данном приложении.

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

Полное приложение можно посмотреть здесь: Angular Signals.


Возможно, вам будет интересно

Компонент и модель

Angilar создание обычных и вложенных компонентов через CLI. Описание модели.

Route. Переключения и параметры

Работа с роутером и ссылками в Angular. Программное переключение роутера и параметры пути в Angular

От хорошего к великому: параметры входных данных, inputs в Angular

В Angular 16.0.0-next.4 была добавлена новая функция - возможность определения входных данных inputs, как обязательных для компонентов и директив. Другими словами, теперь мы можем указать, что компонент или директива требует определенных входных данных inputs для правильной работы.

Route. Защита роутера

Использование интерфейсов CanActivate и CanActivateChild для принятия решения об активации роутера

Оформление заявки

Документы на создание сайта

Изучите наше коммерческое предложение, заполните БРИФ и отправьте его на почту maxidebox@list.ru. Изучив все пожелания из БРИФ-а, обратным ответом оповестим Вас по стоимости разработке, ответим на вопросы.

КП на создание сайта Коммерческое предложение на созданеи сайта

Мы берем на себя ответственность за все стадии работы и полностью избавляем клиентов от забот и необходимости вникать в тонкости.

Скачать БРИФ (акета) на создание сайта Скачать БРИФ (акета) на создание сайта

Зополните у БРИФ-а все необходимые поля. Сделайте краткое описание к каждому из пунктов анкеты, привидите примеры в соответсвующий пунктах - это позволит лучше понять Ваши ожидания и требования к сайту