Начало работы с угловыми сигналами

1. Прежде чем начать

черный угловой логотип

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

Что вы построите

  • Вы узнаете о трех реактивных примитивах, представленных в Angular Signals: signal() , computed() и effect() .
  • Используйте сигналы Angular для создания игры-шифра на Angular. Шифры — это системы для шифрования и расшифровки данных. В этой игре пользователи могут расшифровать секретное сообщение, перетаскивая подсказки, чтобы решить шифр, настроить сообщение и поделиться URL-адресом, чтобы отправлять секретные сообщения друзьям.

Игра на Angular Cypher в стиле винтажной зеленой игровой консоли, со скрытым сообщением на экране: 'Anqnxaa Lpcnaxl aaf pn jfafxyofa aofapfm pn a16 wyjak!'

Предварительные требования

2. Получите код

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

Откройте Stackblitz и запустите приложение.

Для начала откройте ссылку Stackblitz в вашем любимом веб-браузере:

  1. Откройте новую вкладку браузера и перейдите по ссылке https://stackblitz.com/edit/io-signals-codelab-starter?file=src%2Fcipher%2Fservice.cipher.ts,src%2Fsecret-message%2Fservice.message.ts&service.massage.ts
  2. Создайте форк Stackblitz, чтобы получить собственное редактируемое рабочее пространство. Stackblitz автоматически запустит приложение, и вы готовы к работе!

Альтернативный вариант: клонируйте репозиторий и запустите приложение.

В качестве альтернативного метода выполнения этого практического задания можно использовать VSCode или локальную IDE:

  1. Откройте новую вкладку браузера и перейдите по ссылке https://github.com/angular/codelabs/tree/signals-get-started .
  2. Создайте форк и клонируйте репозиторий, а затем используйте команду cd codelabs/ для перехода в репозиторий.
  3. Переключитесь на ветку с исходным кодом с помощью команды git checkout signals-get-started .
  4. Откройте код в VSCode или в вашей любимой IDE.
  5. Для установки зависимостей, необходимых для работы сервера, используйте команду npm install .
  6. Для запуска сервера используйте команду ng serve .
  7. Откройте вкладку браузера по адресу http://localhost:4200 .

3. Установите исходный уровень.

Ваша отправная точка — игра на Angular Cipher, но она пока не работает. Функционал игры будет обеспечиваться с помощью Angular Signals.

Игра на Angular Cypher в стиле винтажной зеленой игровой консоли, со скрытым сообщением на экране: 'Anqnxaa Lpcnaxl aaf pn jfafxyofa aofapfm pn a16 wyjak!'

Для начала, ознакомьтесь с готовой версией того, что вы будете создавать: Angular Signals Cypher .

  1. Просмотрите закодированное сообщение на экране.
  2. Перетащите кнопку с буквой на клавиатуре, чтобы приблизиться к разгадке шифра и расшифровке секретного сообщения.
  3. В случае успеха, следите за тем, как сообщение обновляется, чтобы расшифровать большую часть секретного текста.
  4. Нажмите «Настроить» , чтобы изменить отправителя и сообщение, а затем нажмите «Создать и скопировать URL» , чтобы увидеть значения на экране и изменения в URL.
  5. Бонус: Скопируйте и вставьте URL-адрес в новую вкладку или поделитесь им с другом и посмотрите, как отправитель и сообщение хранятся в URL-адресе.

GIF-анимация игры Angular Cypher, на экране которой расшифровывается скрытое сообщение, образующее надпись «Сегодня в версии 16 доступны предварительные версии Angular Signals для разработчиков!»

4. Определите свой первый сигнал().

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

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

Преобразовать superSecretMessage в signal()

superSecretMessage — это значение в MessageService , определяющее секретное сообщение, которое расшифровывает плеер. В настоящее время это значение не уведомляет приложение об изменениях, поэтому кнопка «Настроить» не работает. Это можно решить с помощью сигнала.

Превратив superSecretMessage в сигнал, вы можете уведомлять части приложения, которые зависят от информации об изменении сообщения. При настройке сообщения в диалоговом окне вы устанавливаете сигнал для обновления остальной части приложения новым сообщением.

Чтобы определить свой первый сигнал, выполните следующие шаги, указанные в TODO(1): Define your first signal() в каждом файле:

  1. В файле service.message.ts используйте библиотеку Signals, чтобы сделать superSecretMessage реактивным:

src/app/secret-message/service.message.ts

superSecretMessage = signal(
  'Angular Signals are in developer preview in v16 today!'
);

Это автоматически предложит вам импортировать signal из @angular/core . Если вы обновите страницу, вы, вероятно, столкнетесь с ошибками там, где ранее ссылались на superSecretMessage . Это связано с тем, что вы изменили тип ` superSecretMessage со string на SettableSignal<string> . Вы можете исправить это, изменив все ссылки на superSecretMessage на использование API сигналов. Везде, где вы считываете значение, вызывайте геттер ` superSecretMessage() из `Signal`. А везде, где вы записываете значение, используйте API .set в SettableSignal чтобы установить новое значение для сообщения.

  1. В файлах secret-message.ts и service.message.ts обновите все ссылки на superSecretMessage на superSecretMessage() :

src/app/secret-message/secret-message.ts

// Before
this.messages.superSecretMessage
this.messages.superSecretMessage = message;

// After
this.messages.superSecretMessage()
this.messages.superSecretMessage.set(message);

src/app/secret-message/service.message.ts

// Before
this.superSecretMessage

// After
this.superSecretMessage()

Изучите два других сигнала.

  • Обратите внимание, что в вашем приложении есть еще два сигнала:

src/app/cipher/service.cipher.ts

cipher = signal(this.createNewCipherKey());
decodedCipher = signal<CipherKey[]>([]);

CipherService определяет cipher сигнал — случайно сгенерированное сопоставление пар ключ-значение, где одна буква алфавита соответствует новой cipher букве. Этот сигнал используется для шифрования сообщения и определения, нашёл ли игрок успешное совпадение на клавиатуре.

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

Уникальная и мощная особенность библиотеки Signals в Angular заключается в том, что вы можете внедрять реактивность повсюду. Вы определяете сигналы один раз в сервисах приложения, и вы можете использовать их в шаблонах, компонентах, пайпах, других сервисах или везде, где вы пишете код приложения. Они не ограничены областью видимости компонента.

Проверить изменения

  • Вам осталось выполнить еще один шаг, прежде чем приложение заработает. А пока попробуйте добавить console.log() в разные части вашего приложения, чтобы посмотреть, как устанавливается ваше новое superSecretMessage .

На Stackblitz отображается сообщение в console.log(), показывающее, что superSecretMessage корректно выводит новое сообщение в консоль.

5. Определите свою первую вычисляемую функцию (Computed()).

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

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

Преобразовать solvedMessage в computed()

solvedMessage преобразует значение secretMessage из закодированного состояния в декодированное с помощью сигнала decodedCipher .

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

В настоящее время solvedMessage не обновляется при изменении secretMessage , decodedCipher или superSecretMessage . Поэтому вы не видите обновлений на экране, когда игрок расшифровывает шифр.

Сделав solvedMessage вычисляемым объектом, вы создаете реактивный контекст, так что при обновлении сообщения или расшифровке шифра вы можете получить обновление состояния из отслеживаемых зависимостей.

Чтобы преобразовать solvedMessage в computed() , выполните следующие шаги в TODO(2): Define your first computed() в каждом файле:

  1. В файле service.message.ts используйте библиотеку Signals, чтобы сделать solvedMessage реактивным:

src/app/secret-message/service.message.ts

solvedMessage = computed(() =>
  this.translateMessage(
    this.secretMessage(), 
    this.cipher.decodedCipher()
  )
);

Это автоматически предложит вам импортировать computed из @angular/core . Если вы обновите страницу, вы, вероятно, столкнетесь с ошибками там, где ранее ссылались на solvedMessage . Это связано с тем, что вы изменили тип ` superSecretMessage со string на Signal<string> , функцию. Вы можете исправить это, заменив все ссылки на solvedMessage на solvedMessage() .

  1. В файле secret-message.ts обновите все ссылки на solvedMessage на solvedMessage() :

src/app/secret-message/secret-message.ts

// Before
<span *ngFor="let char of this.messages.solvedMessage.split(''); index as i;" [class.unsolved]="this.messages.solvedMessage[i] !== this.messages.superSecretMessage()[i]" >{{ char }}</span>

// After
<span *ngFor="let char of this.messages.solvedMessage().split(''); index as i;" [class.unsolved]="this.messages.solvedMessage()[i] !== this.messages.superSecretMessage()[i]" >{{ char }}</span>

Обратите внимание, что в отличие от superSecretMessage , solvedMessage не является SettableSignal — вы не можете изменить его значение напрямую. Вместо этого его значение обновляется всякий раз, когда обновляется любой из зависимых от него сигналов ( secretMessage и decodedCipher ).

Изучите две другие функции computed()

  • Обратите внимание, что в вашем приложении есть еще два вычисляемых значения:

src/app/secret-message/service.message.ts

secretMessage = computed(() => 
  this.translateMessage(
    this.superSecretMessage(),
    this.cipher.cipher()
  )
);

src/app/cipher/service.cipher.ts

unsolvedAlphabet = computed(() =>
  ALPHABET.filter(
    (letter) => !this.decodedCipher().find((guess) => guess.value === letter)
  )
);

В классе MessageService определяется вычисляемое secretMessage , представляющее собой superSecretMessage , закодированное с помощью cipher , который игроки пытаются расшифровать.

CipherService определяется вычисляемый параметр unsolvedAlphabet , представляющий собой список всех букв, которые игрок не смог расшифровать, и который формируется из списка расшифрованных ключей в decodedCipher .

Проверить изменения

Теперь, когда superSecretMessage — это сигнал, а solvedMessage — вычисляемое значение, приложение должно работать! Проверьте функциональность игры:

  1. Перетащите LetterGuessComponent в компонент LetterKeyComponent в вашем CipherComponent , чтобы поработать над расшифровкой шифра и декодированием секретного сообщения.
  2. Посмотрите, как обновляется SecretMessageComponent по мере расшифровки секретного сообщения.
  3. Нажмите «Настроить» , чтобы изменить отправителя и сообщение, а затем нажмите «Создать и скопировать URL» , чтобы увидеть значения на экране и изменения в URL.
  4. Бонус: Скопируйте и вставьте URL-адрес в новую вкладку или поделитесь им с другом и посмотрите, как отправитель и сообщение хранятся в URL-адресе.

GIF-анимация игры Angular Cypher, на экране которой расшифровывается скрытое сообщение, образующее надпись «Сегодня в версии 16 доступны предварительные версии Angular Signals для разработчиков!»

6. Добавьте свой первый эффект()

Иногда может потребоваться, чтобы что-то произошло при изменении значения сигнала. С помощью effect() можно запланировать и запустить функцию-обработчик в ответ на изменение сигнала.

Добавьте конфетти, когда шифр будет расшифрован.

Теперь, когда приложение работает, вы можете добавить немного веселья, добавив конфетти, когда шифр будет разгадан и секретное сообщение будет расшифровано.

Чтобы добавить конфетти, выполните следующие действия в TODO(3): Add your first effect() :

  1. В файле cipher.ts запланируйте эффект, который будет добавлять конфетти при расшифровке сообщения:

src/app/cipher/cipher.ts

import * as confetti from 'canvas-confetti';

ngOnInit(): void {
  ...

  effect(() => {
    if (this.messages.superSecretMessage() === this.messages.solvedMessage()) {
      var confettiCanvas = document.getElementById('confetti-canvas');
      confetti.create()(confettiCanvas, { particleCount: 100 });
    }
  });
}

Обратите внимание, как этот эффект зависит от сигнала и вычисленного значения: this.messages.superSecretMessage() и this.messages.solvedMessage() .

Функция Effect позволяет запланировать выполнение функции confetti в реактивном контексте, чтобы отслеживать и переоценивать изменения, возникающие при обновлении зависимостей.

Проверить изменения

  • Попробуйте разгадать шифр (подсказка: вы можете изменить сообщение на что-нибудь короткое, чтобы проверить быстрее!). Вас поздравит с первым effect() !

GIF-анимация игры Angular Cypher, где на экране расшифровывается скрытое сообщение, образующее слово «Время конфетти!», а при расшифровке сообщения взрываются хлопушки с конфетти.

7. Поздравляем!

Ваш Angular-шифр готов к расшифровке и передаче секретных сообщений! Хотите отправить сообщение команде Angular? Отметьте нас в социальных сетях @Angular, чтобы мы могли его расшифровать! 🎉

Игра Angular Cypher решена с помощью скрытого сообщения на экране: «Сегодня в версии 16 реализована предварительная версия Angular Signals для разработчиков!»

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

Узнать больше

Посмотрите эти практические занятия:

Ознакомьтесь с этими материалами: