diff --git a/adev-es/src/content/tutorials/signals/intro/README.en.md b/adev-es/src/content/tutorials/signals/intro/README.en.md new file mode 100644 index 0000000..b9d8df8 --- /dev/null +++ b/adev-es/src/content/tutorials/signals/intro/README.en.md @@ -0,0 +1,13 @@ +# Learn Angular signals + +This interactive tutorial will teach you the fundamentals of Angular signals and how to use them to build reactive applications. + +## How to use this tutorial + +This tutorial assumes you understand Angular's core concepts. If you're new to Angular, read our [essentials guide](/essentials). + +Each step represents a concept in Angular signals. You can do one, or all of them. + +If you get stuck, click "Reveal answer" at the top. + +Alright, let's [get started](/tutorials/signals/1-creating-your-first-signal)! diff --git a/adev-es/src/content/tutorials/signals/intro/README.md b/adev-es/src/content/tutorials/signals/intro/README.md index b9d8df8..dc33dec 100644 --- a/adev-es/src/content/tutorials/signals/intro/README.md +++ b/adev-es/src/content/tutorials/signals/intro/README.md @@ -1,13 +1,13 @@ -# Learn Angular signals +# Aprende sobre signals en Angular -This interactive tutorial will teach you the fundamentals of Angular signals and how to use them to build reactive applications. +Este tutorial interactivo te enseñará los fundamentos de los signals de Angular y cómo usarlos para construir aplicaciones reactivas. -## How to use this tutorial +## Cómo usar este tutorial -This tutorial assumes you understand Angular's core concepts. If you're new to Angular, read our [essentials guide](/essentials). +Este tutorial asume que entiendes los conceptos principales de Angular. Si eres nuevo en Angular, lee nuestra [guía esencial](/essentials). -Each step represents a concept in Angular signals. You can do one, or all of them. +Cada paso representa un concepto en los signals de Angular. Puedes hacer uno, o todos ellos. -If you get stuck, click "Reveal answer" at the top. +Si te quedas atascado, haz clic en "Reveal answer" (Mostrar respuesta) en la parte superior. -Alright, let's [get started](/tutorials/signals/1-creating-your-first-signal)! +Muy bien, ¡[comencemos](/tutorials/signals/1-creating-your-first-signal)! diff --git a/adev-es/src/content/tutorials/signals/steps/1-creating-your-first-signal/README.en.md b/adev-es/src/content/tutorials/signals/steps/1-creating-your-first-signal/README.en.md new file mode 100644 index 0000000..cd2cf93 --- /dev/null +++ b/adev-es/src/content/tutorials/signals/steps/1-creating-your-first-signal/README.en.md @@ -0,0 +1,130 @@ +# Creating and updating your first signal + +Welcome to the Angular signals tutorial! [Signals](/essentials/signals) are Angular's reactive primitive that provide a way to manage state and automatically update your UI when that state changes. + +In this activity, you'll learn how to: + +- Create your first signal using the `signal()` function +- Display its value in a template +- Update the signal value using `set()` and `update()` methods + +Let's build an interactive user status system with signals! + +
+ + + + +Import the `signal` function from `@angular/core` at the top of your component file. + +```ts +import {Component, signal, ChangeDetectionStrategy} from '@angular/core'; +``` + + + + +Add a `userStatus` signal to your component class that is initialized with a value of `'offline'`. + +```ts +@Component({ + /* Config omitted */ +}) +export class App { + userStatus = signal<'online' | 'offline'>('offline'); +} +``` + + + + +Update the status indicator to display the current user status by: +1. Binding the signal to the class attribute with `[class]="userStatus()"` +2. Displaying the status text by replacing `???` with `{{ userStatus() }}` + +```html + +
+ + Status: ??? +
+ + +
+ + Status: {{ userStatus() }} +
+``` + +Notice how we call the signal `userStatus()` with parentheses to read its value. +
+ + +Add methods to your component that change the user status using the `set()` method. + +```ts +goOnline() { + this.userStatus.set('online'); +} + +goOffline() { + this.userStatus.set('offline'); +} +``` + +The `set()` method replaces the signal's value entirely with a new value. + + + + +The buttons are already in the template. Now connect them to your methods by adding: +1. Click handlers with `(click)` +2. Disabled states with `[disabled]` when already in that status + +```html + + + +``` + + + + +Add a `toggleStatus()` method that switches between online and offline using the `update()` method. + +```ts +toggleStatus() { + this.userStatus.update(current => current === 'online' ? 'offline' : 'online'); +} +``` + +The `update()` method takes a function that receives the current value and returns the new value. This is useful when you need to modify the existing value based on its current state. + + + + +The toggle button is already in the template. Connect it to your `toggleStatus()` method: + +```html + +``` + + + +
+ +Congratulations! You've created your first signal and learned how to update it using both `set()` and `update()` methods. The `signal()` function creates a reactive value that Angular tracks, and when you update it, your UI automatically reflects the changes. + +Next, you'll learn [how to derive state from signals using computed](/tutorials/signals/2-deriving-state-with-computed-signals)! + + + +You might notice `ChangeDetectionStrategy.OnPush` in the component decorator throughout this tutorial. This is a performance optimization for Angular components that use signals. For now, you can safely ignore it—just know it helps your app run faster when using signals! You can learn more in the [change detection strategies API docs](/api/core/ChangeDetectionStrategy). + + diff --git a/adev-es/src/content/tutorials/signals/steps/1-creating-your-first-signal/README.md b/adev-es/src/content/tutorials/signals/steps/1-creating-your-first-signal/README.md index cd2cf93..3136754 100644 --- a/adev-es/src/content/tutorials/signals/steps/1-creating-your-first-signal/README.md +++ b/adev-es/src/content/tutorials/signals/steps/1-creating-your-first-signal/README.md @@ -1,21 +1,21 @@ -# Creating and updating your first signal +# Creando y actualizando tu primer signal -Welcome to the Angular signals tutorial! [Signals](/essentials/signals) are Angular's reactive primitive that provide a way to manage state and automatically update your UI when that state changes. +¡Bienvenido al tutorial de signals de Angular! Los [signals](/essentials/signals) son la primitiva reactiva de Angular que proporciona una forma de gestionar estado y actualizar automáticamente tu UI cuando ese estado cambia. -In this activity, you'll learn how to: +En esta actividad, aprenderás cómo: -- Create your first signal using the `signal()` function -- Display its value in a template -- Update the signal value using `set()` and `update()` methods +- Crear tu primer signal usando la función `signal()` +- Mostrar su valor en una plantilla +- Actualizar el valor del signal usando los métodos `set()` y `update()` -Let's build an interactive user status system with signals! +¡Construyamos un sistema interactivo de estado de usuario con signals!
- -Import the `signal` function from `@angular/core` at the top of your component file. + +Importa la función `signal` desde `@angular/core` al inicio de tu archivo de componente. ```ts import {Component, signal, ChangeDetectionStrategy} from '@angular/core'; @@ -23,12 +23,12 @@ import {Component, signal, ChangeDetectionStrategy} from '@angular/core'; - -Add a `userStatus` signal to your component class that is initialized with a value of `'offline'`. + +Agrega un signal `userStatus` a tu clase de componente que se inicialice con un valor de `'offline'`. ```ts @Component({ - /* Config omitted */ + /* Config omitida */ }) export class App { userStatus = signal<'online' | 'offline'>('offline'); @@ -37,30 +37,30 @@ export class App { - -Update the status indicator to display the current user status by: -1. Binding the signal to the class attribute with `[class]="userStatus()"` -2. Displaying the status text by replacing `???` with `{{ userStatus() }}` + +Actualiza el indicador de estado para mostrar el estado actual del usuario: +1. Vinculando el signal al atributo class con `[class]="userStatus()"` +2. Mostrando el texto de estado reemplazando `???` con `{{ userStatus() }}` ```html - +
Status: ???
- +
Status: {{ userStatus() }}
``` -Notice how we call the signal `userStatus()` with parentheses to read its value. +Observa cómo llamamos al signal `userStatus()` con paréntesis para leer su valor.
- -Add methods to your component that change the user status using the `set()` method. + +Agrega métodos a tu componente que cambien el estado del usuario usando el método `set()`. ```ts goOnline() { @@ -72,17 +72,17 @@ goOffline() { } ``` -The `set()` method replaces the signal's value entirely with a new value. +El método `set()` reemplaza el valor del signal completamente con un nuevo valor. - -The buttons are already in the template. Now connect them to your methods by adding: -1. Click handlers with `(click)` -2. Disabled states with `[disabled]` when already in that status + +Los botones ya están en la plantilla. Ahora conéctalos a tus métodos agregando: +1. Manejadores de clic con `(click)` +2. Estados deshabilitados con `[disabled]` cuando ya están en ese estado ```html - + @@ -93,8 +93,8 @@ The buttons are already in the template. Now connect them to your methods by add - -Add a `toggleStatus()` method that switches between online and offline using the `update()` method. + +Agrega un método `toggleStatus()` que cambie entre online y offline usando el método `update()`. ```ts toggleStatus() { @@ -102,12 +102,12 @@ toggleStatus() { } ``` -The `update()` method takes a function that receives the current value and returns the new value. This is useful when you need to modify the existing value based on its current state. +El método `update()` toma una función que recibe el valor actual y devuelve el nuevo valor. Esto es útil cuando necesitas modificar el valor existente basado en su estado actual. - -The toggle button is already in the template. Connect it to your `toggleStatus()` method: + +El botón toggle ya está en la plantilla. Conéctalo a tu método `toggleStatus()`: ```html + + + +``` + + + + +Now test the behavior: + +1. Change the user status - notice how `notificationsEnabled` updates automatically +2. Manually toggle notifications - it overrides the computed value +3. Change status again - the linked signal re-syncs with its computation + +This demonstrates that linked signals maintain their reactive connection even after being manually set! + + +
+ +Excellent! You've learned the key differences between computed and linked signals: + +- **Computed signals**: Read-only, always derived from other signals +- **Linked signals**: Writable, can be both derived AND manually updated +- **Use computed when**: The value should always be calculated +- **Use linkedSignal when**: You need a default computation that can be overridden + +In the next lesson, you'll learn [how to manage async data with signals](/tutorials/signals/4-managing-async-data-with-signals)! diff --git a/adev-es/src/content/tutorials/signals/steps/3-deriving-state-with-linked-signals/README.md b/adev-es/src/content/tutorials/signals/steps/3-deriving-state-with-linked-signals/README.md index f1980a4..3fdd061 100644 --- a/adev-es/src/content/tutorials/signals/steps/3-deriving-state-with-linked-signals/README.md +++ b/adev-es/src/content/tutorials/signals/steps/3-deriving-state-with-linked-signals/README.md @@ -1,54 +1,54 @@ -# Deriving state with linked signals +# Derivando estado con linked signals -Now that you've learned [how to derive state with computed signals](/tutorials/signals/2-deriving-state-with-computed-signals), you created a computed signal for `notificationsEnabled` that automatically followed your user status. But what if users want to manually disable notifications even when they're online? That's where linked signals come in. +Ahora que has aprendido [cómo derivar estado con computed signals](/tutorials/signals/2-deriving-state-with-computed-signals), creaste un computed signal para `notificationsEnabled` que seguía automáticamente tu estado de usuario. Pero ¿qué pasa si los usuarios quieren deshabilitar manualmente las notificaciones incluso cuando están en línea? Ahí es donde entran los linked signals. -Linked signals are writable signals that maintain a reactive connection to their source signals. They're perfect for creating state that normally follows a computation but can be overridden when needed. +Los linked signals son signals editables que mantienen una conexión reactiva con sus signals fuente. Son perfectos para crear estado que normalmente sigue un cómputo pero puede ser anulado cuando sea necesario. -In this activity, you'll learn how `linkedSignal()` differs from `computed()` by enhancing the previous user status system's computed `notificationsEnabled` to a writable linked signal. +En esta actividad, aprenderás cómo `linkedSignal()` se diferencia de `computed()` mejorando el computed `notificationsEnabled` del sistema de estado de usuario anterior a un linked signal editable.
- -Add `linkedSignal` to your existing imports. + +Agrega `linkedSignal` a tus importaciones existentes. ```ts -// Add linkedSignal to existing imports +// Agregar linkedSignal a las importaciones existentes import {Component, signal, computed, linkedSignal, ChangeDetectionStrategy} from '@angular/core'; ``` - -Replace the computed `notificationsEnabled` with a linkedSignal using the exact same expression: + +Reemplaza el computed `notificationsEnabled` con un linkedSignal usando exactamente la misma expresión: ```ts -// Previously (from lesson 2): +// Anteriormente (de la lección 2): // notificationsEnabled = computed(() => this.userStatus() === 'online'); -// Now with linkedSignal - same expression, but writable: +// Ahora con linkedSignal - misma expresión, pero editable: notificationsEnabled = linkedSignal(() => this.userStatus() === 'online'); ``` -The expression is identical, but linkedSignal creates a writable signal. It will still automatically update when `userStatus` changes, but you can also set it manually. +La expresión es idéntica, pero linkedSignal crea un signal editable. Se seguirá actualizando automáticamente cuando `userStatus` cambie, pero también puedes establecerlo manualmente. - -Add a method to demonstrate that linked signals can be written to directly: + +Agrega un método para demostrar que los linked signals pueden escribirse directamente: ```ts toggleNotifications() { - // This works with linkedSignal but would error with computed! + // Esto funciona con linkedSignal pero daría error con computed! this.notificationsEnabled.set(!this.notificationsEnabled()); } ``` -This is the key difference: computed signals are read-only, but linked signals can be updated manually while still maintaining their reactive connection. +Esta es la diferencia clave: los computed signals son de solo lectura, pero los linked signals pueden actualizarse manualmente mientras mantienen su conexión reactiva. - -Update your template to add a toggle button for notifications: + +Actualiza tu plantilla para agregar un botón de alternancia para notificaciones: ```angular-html
@@ -67,29 +67,29 @@ Update your template to add a toggle button for notifications: }
- + ```
- -Now test the behavior: + +Ahora prueba el comportamiento: -1. Change the user status - notice how `notificationsEnabled` updates automatically -2. Manually toggle notifications - it overrides the computed value -3. Change status again - the linked signal re-syncs with its computation +1. Cambia el estado del usuario - observa cómo `notificationsEnabled` se actualiza automáticamente +2. Alterna manualmente las notificaciones - anula el valor computed +3. Cambia el estado nuevamente - el linked signal se re-sincroniza con su cómputo -This demonstrates that linked signals maintain their reactive connection even after being manually set! +¡Esto demuestra que los linked signals mantienen su conexión reactiva incluso después de ser establecidos manualmente!
-Excellent! You've learned the key differences between computed and linked signals: +¡Excelente! Has aprendido las diferencias clave entre computed y linked signals: -- **Computed signals**: Read-only, always derived from other signals -- **Linked signals**: Writable, can be both derived AND manually updated -- **Use computed when**: The value should always be calculated -- **Use linkedSignal when**: You need a default computation that can be overridden +- **Computed signals**: Solo lectura, siempre derivados de otros signals +- **Linked signals**: Editables, pueden ser tanto derivados como actualizados manualmente +- **Usa computed cuando**: El valor siempre debe ser calculado +- **Usa linkedSignal cuando**: Necesitas un cómputo predeterminado que pueda ser anulado -In the next lesson, you'll learn [how to manage async data with signals](/tutorials/signals/4-managing-async-data-with-signals)! +En la próxima lección, aprenderás [cómo gestionar datos asíncronos con signals](/tutorials/signals/4-managing-async-data-with-signals)! diff --git a/adev-es/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/README.en.md b/adev-es/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/README.en.md new file mode 100644 index 0000000..da3a520 --- /dev/null +++ b/adev-es/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/README.en.md @@ -0,0 +1,110 @@ +# Managing async data with signals using the Resources API + +Now that you've learned [how to derive state with linked signals](/tutorials/signals/3-deriving-state-with-linked-signals), let's explore how to handle asynchronous data with the Resource API. The Resource API provides a powerful way to manage async operations using signals, with built-in loading states, error handling, and request management. + +In this activity, you'll learn how to use the `resource()` function to load data asynchronously and how to handle different states of async operations by building a user profile loader that demonstrates the Resource API in action. + +
+ + + + +Add `resource` to your existing imports and import the fake API function. + +```ts +// Add resource to existing imports +import {Component, signal, computed, resource, ChangeDetectionStrategy} from '@angular/core'; +// Import mock API function +import {loadUser} from './user-api'; +``` + + + + +Add a property in the component class that creates a resource to load user data based on a user ID signal. + +```ts +userId = signal(1); + +userResource = resource({ + params: () => ({ id: this.userId() }), + loader: (params) => loadUser(params.params.id) +}); +``` + + + + +Add methods to change the user ID and reload the resource. + +```ts +loadUser(id: number) { + this.userId.set(id); +} + +reloadUser() { + this.userResource.reload(); +} +``` + +Changing the params signal automatically triggers a reload, or you can manually reload with `reload()`. + + + +Add computed signals to access different states of the resource. + +```ts +isLoading = computed(() => this.userResource.status() === 'loading'); +hasError = computed(() => this.userResource.status() === 'error'); +``` + +Resources provide a `status()` signal that can be 'loading', 'success', or 'error', a `value()` signal for the loaded data, and a `hasValue()` method that safely checks if data is available. + + + +The template structure is already provided. Now connect everything: + +Part 1. **Add click handlers to the buttons:** + +```html + + + + +``` + +Part 2. **Replace the placeholder with resource state handling:** + +```angular-html +@if (isLoading()) { +

Loading user...

+} @else if (hasError()) { +

Error: {{ userResource.error()?.message }}

+} @else if (userResource.hasValue()) { + +} +``` + +The resource provides different methods to check its state: + +- `isLoading()` - true when fetching data +- `hasError()` - true when an error occurred +- `userResource.hasValue()` - true when data is available +- `userResource.value()` - access the loaded data +- `userResource.error()` - access error information + +
+ +
+ +Excellent! You've now learned how to use the Resource API with signals. Key concepts to remember: + +- **Resources are reactive**: They automatically reload when params change +- **Built-in state management**: Resources provide `status()`, `value()`, and `error()` signals +- **Automatic cleanup**: Resources handle request cancellation and cleanup automatically +- **Manual control**: You can manually reload or abort requests when needed + +In the next lesson, you'll learn [how to pass data to components with input signals](/tutorials/signals/5-component-communication-with-signals)! diff --git a/adev-es/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/README.md b/adev-es/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/README.md index da3a520..7743786 100644 --- a/adev-es/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/README.md +++ b/adev-es/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/README.md @@ -1,27 +1,27 @@ -# Managing async data with signals using the Resources API +# Gestionando datos asíncronos con signals usando la API de Resources -Now that you've learned [how to derive state with linked signals](/tutorials/signals/3-deriving-state-with-linked-signals), let's explore how to handle asynchronous data with the Resource API. The Resource API provides a powerful way to manage async operations using signals, with built-in loading states, error handling, and request management. +Ahora que has aprendido [cómo derivar estado con linked signals](/tutorials/signals/3-deriving-state-with-linked-signals), exploremos cómo manejar datos asíncronos con la API de Resource. La API de Resource proporciona una forma potente de gestionar operaciones asíncronas usando signals, con estados de carga integrados, manejo de errores y gestión de solicitudes. -In this activity, you'll learn how to use the `resource()` function to load data asynchronously and how to handle different states of async operations by building a user profile loader that demonstrates the Resource API in action. +En esta actividad, aprenderás cómo usar la función `resource()` para cargar datos de forma asíncrona y cómo manejar diferentes estados de operaciones asíncronas construyendo un cargador de perfil de usuario que demuestra la API de Resource en acción.
- -Add `resource` to your existing imports and import the fake API function. + +Agrega `resource` a tus importaciones existentes e importa la función de API falsa. ```ts -// Add resource to existing imports +// Agregar resource a las importaciones existentes import {Component, signal, computed, resource, ChangeDetectionStrategy} from '@angular/core'; -// Import mock API function +// Importar función de API mock import {loadUser} from './user-api'; ``` - -Add a property in the component class that creates a resource to load user data based on a user ID signal. + +Agrega una propiedad en la clase del componente que cree un resource para cargar datos de usuario basados en un signal de ID de usuario. ```ts userId = signal(1); @@ -34,8 +34,8 @@ userResource = resource({ - -Add methods to change the user ID and reload the resource. + +Agrega métodos para cambiar el ID de usuario y recargar el resource. ```ts loadUser(id: number) { @@ -47,24 +47,24 @@ reloadUser() { } ``` -Changing the params signal automatically triggers a reload, or you can manually reload with `reload()`. +Cambiar el signal de params automáticamente dispara una recarga, o puedes recargar manualmente con `reload()`. - -Add computed signals to access different states of the resource. + +Agrega computed signals para acceder a diferentes estados del resource. ```ts isLoading = computed(() => this.userResource.status() === 'loading'); hasError = computed(() => this.userResource.status() === 'error'); ``` -Resources provide a `status()` signal that can be 'loading', 'success', or 'error', a `value()` signal for the loaded data, and a `hasValue()` method that safely checks if data is available. +Los resources proporcionan un signal `status()` que puede ser 'loading', 'success' o 'error', un signal `value()` para los datos cargados y un método `hasValue()` que verifica de forma segura si los datos están disponibles. - -The template structure is already provided. Now connect everything: + +La estructura de la plantilla ya está proporcionada. Ahora conecta todo: -Part 1. **Add click handlers to the buttons:** +Parte 1. **Agrega manejadores de clic a los botones:** ```html @@ -73,7 +73,7 @@ Part 1. **Add click handlers to the buttons:** ``` -Part 2. **Replace the placeholder with resource state handling:** +Parte 2. **Reemplaza el placeholder con el manejo de estado del resource:** ```angular-html @if (isLoading()) { @@ -88,23 +88,23 @@ Part 2. **Replace the placeholder with resource state handling:** } ``` -The resource provides different methods to check its state: +El resource proporciona diferentes métodos para verificar su estado: -- `isLoading()` - true when fetching data -- `hasError()` - true when an error occurred -- `userResource.hasValue()` - true when data is available -- `userResource.value()` - access the loaded data -- `userResource.error()` - access error information +- `isLoading()` - verdadero cuando está obteniendo datos +- `hasError()` - verdadero cuando ocurrió un error +- `userResource.hasValue()` - verdadero cuando los datos están disponibles +- `userResource.value()` - accede a los datos cargados +- `userResource.error()` - accede a la información del error -Excellent! You've now learned how to use the Resource API with signals. Key concepts to remember: +¡Excelente! Ahora has aprendido cómo usar la API de Resource con signals. Conceptos clave para recordar: -- **Resources are reactive**: They automatically reload when params change -- **Built-in state management**: Resources provide `status()`, `value()`, and `error()` signals -- **Automatic cleanup**: Resources handle request cancellation and cleanup automatically -- **Manual control**: You can manually reload or abort requests when needed +- **Los resources son reactivos**: Se recargan automáticamente cuando los params cambian +- **Gestión de estado integrada**: Los resources proporcionan signals `status()`, `value()` y `error()` +- **Limpieza automática**: Los resources manejan la cancelación y limpieza de solicitudes automáticamente +- **Control manual**: Puedes recargar o abortar solicitudes manualmente cuando sea necesario -In the next lesson, you'll learn [how to pass data to components with input signals](/tutorials/signals/5-component-communication-with-signals)! +En la próxima lección, aprenderás [cómo pasar datos a componentes con input signals](/tutorials/signals/5-component-communication-with-signals)! diff --git a/adev-es/src/content/tutorials/signals/steps/5-component-communication-with-signals/README.en.md b/adev-es/src/content/tutorials/signals/steps/5-component-communication-with-signals/README.en.md new file mode 100644 index 0000000..636cac4 --- /dev/null +++ b/adev-es/src/content/tutorials/signals/steps/5-component-communication-with-signals/README.en.md @@ -0,0 +1,105 @@ +# Passing data to components with input signals + +Now that you've learned [managing async data with signals](/tutorials/signals/4-managing-async-data-with-signals), let's explore Angular's signal-based `input()` API for passing data from parent to child components, making component data flow more reactive and efficient. If you're familiar with component props from other frameworks, inputs are the same idea. + +In this activity, you'll add signal inputs to a product card component and see how parent data flows down reactively. + +
+ + + + +Add signal `input()` functions to receive data in the `product-card` component. + +```ts +// Add imports for signal inputs +import {Component, input, ChangeDetectionStrategy} from '@angular/core'; + +// Add these signal inputs +name = input.required(); +price = input.required(); +available = input(true); +``` + +Notice how `input.required()` creates an input that must be provided, while `input()` with a default value is optional. + + + +Update the template in `product-card` to display the signal input values. + +```angular-html +
+

{{ name() }}

+

\${{ price() }}

+

Status: + @if (available()) { + Available + } @else { + Out of Stock + } +

+
+``` + +Input signals work just like regular signals in templates - call them as functions to access their values. +
+ + +Update the `product-card` usage in `app.ts` to pass dynamic signal values instead of static ones. + +```html + + + + + +``` + +The square brackets `[]` create property bindings that pass the current signal values to the child. + + + +Add methods in `app.ts` to update the parent signals and see how the child component reacts automatically. + +```ts +updateProduct() { + this.productName.set('Updated Product'); + this.productPrice.set(149); +} + +toggleAvailability() { + this.productAvailable.set(!this.productAvailable()); +} +``` + +```html + +
+ + +
+``` + +When parent signals change, the child component automatically receives and displays the new values! +
+ +
+ +Excellent! You've learned how signal inputs work: + +- **Signal inputs** - Use `input()` and `input.required()` to receive data from parent components +- **Reactive updates** - Child components automatically update when parent signal values change +- **Type safety** - Signal inputs provide full TypeScript type checking +- **Default values** - Optional inputs can have default values while required inputs must be provided + +Signal inputs make component communication more reactive and eliminate the need for `OnChanges` lifecycle hooks in many cases. + +In the next lesson, you'll learn about [two-way binding with model signals](/tutorials/signals/6-two-way-binding-with-model-signals)! diff --git a/adev-es/src/content/tutorials/signals/steps/5-component-communication-with-signals/README.md b/adev-es/src/content/tutorials/signals/steps/5-component-communication-with-signals/README.md index 636cac4..0fc2956 100644 --- a/adev-es/src/content/tutorials/signals/steps/5-component-communication-with-signals/README.md +++ b/adev-es/src/content/tutorials/signals/steps/5-component-communication-with-signals/README.md @@ -1,31 +1,31 @@ -# Passing data to components with input signals +# Pasando datos a componentes con input signals -Now that you've learned [managing async data with signals](/tutorials/signals/4-managing-async-data-with-signals), let's explore Angular's signal-based `input()` API for passing data from parent to child components, making component data flow more reactive and efficient. If you're familiar with component props from other frameworks, inputs are the same idea. +Ahora que has aprendido [cómo gestionar datos asíncronos con signals](/tutorials/signals/4-managing-async-data-with-signals), exploremos la API `input()` basada en signals de Angular para pasar datos de componentes padre a hijo, haciendo que el flujo de datos del componente sea más reactivo y eficiente. Si estás familiarizado con las props de componentes de otros frameworks, los inputs son la misma idea. -In this activity, you'll add signal inputs to a product card component and see how parent data flows down reactively. +En esta actividad, agregarás signal inputs a un componente de tarjeta de producto y verás cómo los datos del padre fluyen hacia abajo de forma reactiva.
- -Add signal `input()` functions to receive data in the `product-card` component. + +Agrega funciones `input()` de signal para recibir datos en el componente `product-card`. ```ts -// Add imports for signal inputs +// Agregar importaciones para signal inputs import {Component, input, ChangeDetectionStrategy} from '@angular/core'; -// Add these signal inputs +// Agregar estos signal inputs name = input.required(); price = input.required(); available = input(true); ``` -Notice how `input.required()` creates an input that must be provided, while `input()` with a default value is optional. +Observa cómo `input.required()` crea un input que debe ser proporcionado, mientras que `input()` con un valor predeterminado es opcional. - -Update the template in `product-card` to display the signal input values. + +Actualiza la plantilla en `product-card` para mostrar los valores de los signal inputs. ```angular-html
@@ -41,21 +41,21 @@ Update the template in `product-card` to display the signal input values.
``` -Input signals work just like regular signals in templates - call them as functions to access their values. +Los input signals funcionan igual que los signals regulares en las plantillas — llámalos como funciones para acceder a sus valores.
- -Update the `product-card` usage in `app.ts` to pass dynamic signal values instead of static ones. + +Actualiza el uso de `product-card` en `app.ts` para pasar valores dinámicos de signals en lugar de valores estáticos. ```html - + - + ``` -The square brackets `[]` create property bindings that pass the current signal values to the child. +Los corchetes `[]` crean enlaces de propiedad que pasan los valores actuales de los signals al hijo. - -Add methods in `app.ts` to update the parent signals and see how the child component reacts automatically. + +Agrega métodos en `app.ts` para actualizar los signals del padre y ver cómo el componente hijo reacciona automáticamente. ```ts updateProduct() { @@ -81,25 +81,25 @@ toggleAvailability() { ``` ```html - +
``` -When parent signals change, the child component automatically receives and displays the new values! +¡Cuando los signals del padre cambian, el componente hijo recibe y muestra automáticamente los nuevos valores!
-Excellent! You've learned how signal inputs work: +¡Excelente! Has aprendido cómo funcionan los signal inputs: -- **Signal inputs** - Use `input()` and `input.required()` to receive data from parent components -- **Reactive updates** - Child components automatically update when parent signal values change -- **Type safety** - Signal inputs provide full TypeScript type checking -- **Default values** - Optional inputs can have default values while required inputs must be provided +- **Signal inputs** - Usa `input()` y `input.required()` para recibir datos de componentes padre +- **Actualizaciones reactivas** - Los componentes hijo se actualizan automáticamente cuando los valores de los signals del padre cambian +- **Seguridad de tipos** - Los signal inputs proporcionan verificación de tipos completa de TypeScript +- **Valores predeterminados** - Los inputs opcionales pueden tener valores predeterminados mientras que los inputs requeridos deben ser proporcionados -Signal inputs make component communication more reactive and eliminate the need for `OnChanges` lifecycle hooks in many cases. +Los signal inputs hacen que la comunicación entre componentes sea más reactiva y eliminan la necesidad de los hooks de ciclo de vida `OnChanges` en muchos casos. -In the next lesson, you'll learn about [two-way binding with model signals](/tutorials/signals/6-two-way-binding-with-model-signals)! +En la próxima lección, aprenderás sobre [el enlace bidireccional con model signals](/tutorials/signals/6-two-way-binding-with-model-signals)! diff --git a/adev-es/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/README.en.md b/adev-es/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/README.en.md new file mode 100644 index 0000000..f451bc0 --- /dev/null +++ b/adev-es/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/README.en.md @@ -0,0 +1,134 @@ +# Two-way binding with model signals + +Now that you've learned [passing data to components with input signals](/tutorials/signals/5-component-communication-with-signals), let's explore Angular's `model()` API for two-way binding. Model signals are perfect for UI components like checkboxes, sliders, or custom form controls where the component needs to both receive a value AND update it. + +In this activity, you'll create a custom checkbox component that manages its own state while keeping the parent synchronized. + +
+ + + + +Create a model signal in the `custom-checkbox` component that can both receive and update the parent's value. + +```ts +// Add imports for model signals +import {Component, model, input, ChangeDetectionStrategy} from '@angular/core'; + +// Model signal for two-way binding +checked = model.required(); + +// Optional input for label +label = input(''); +``` + +Unlike `input()` signals which are read-only, `model()` signals can be both read and written to. + + + +Build the checkbox template that responds to clicks and updates its own model. + +```html + +``` + +The component reads from its model signal and has a method to update it. + + + +Implement the toggle method that updates the model signal when the checkbox is clicked. + +```ts +toggle() { + // This updates BOTH the component's state AND the parent's model! + this.checked.set(!this.checked()); +} +``` + +When the child component calls `this.checked.set()`, it automatically propagates the change back to the parent. This is the key difference from `input()` signals. + + + +First, uncomment the model signal properties and methods in `app.ts`: + +```ts +// Parent signal models +agreedToTerms = model(false); +enableNotifications = model(true); + +// Methods to test two-way binding +toggleTermsFromParent() { + this.agreedToTerms.set(!this.agreedToTerms()); +} + +resetAll() { + this.agreedToTerms.set(false); + this.enableNotifications.set(false); +} +``` + +Then update the template: + +Part 1. **Uncomment the checkboxes and add two-way binding:** + +- Replace `___ADD_TWO_WAY_BINDING___` with `[(checked)]="agreedToTerms"` for the first checkbox +- Replace `___ADD_TWO_WAY_BINDING___` with `[(checked)]="enableNotifications"` for the second + +Part 2. **Replace the `???` placeholders with @if blocks:** + +```angular-html +@if (agreedToTerms()) { + Yes +} @else { + No +} + +@if (enableNotifications()) { + Yes +} @else { + No +} +``` + +Part 3. **Add click handlers to the buttons:** + +```html + + +``` + +The `[(checked)]` syntax creates two-way binding - data flows down to the component AND changes flow back up to the parent by emitting an event that references the signal itself and does _not_ call the signal getter directly. + + + +Interact with your app to see two-way binding in action: + +1. **Click checkboxes** - Component updates its own state and notifies parent +2. **Click "Toggle Terms from Parent"** - Parent updates propagate down to component +3. **Click "Reset All"** - Parent resets both models and components update automatically + +Both the parent and child can update the shared state, and both stay in sync automatically! + + + + +Perfect! You've learned how model signals enable two-way binding: + +- **Model signals** - Use `model()` and `model.required()` for values that can be both read and written +- **Two-way binding** - Use `[(property)]` syntax to bind parent signals to child models +- **Perfect for UI components** - Checkboxes, form controls, and widgets that need to manage their own state +- **Automatic synchronization** - Parent and child stay in sync without manual event handling + +**When to use `model()` vs `input()`:** + +- Use `input()` for data that only flows down (display data, configuration) +- Use `model()` for UI components that need to update their own value (form controls, toggles) + +In the next lesson, you'll learn about [using signals with services](/tutorials/signals/7-using-signals-with-services)! diff --git a/adev-es/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/README.md b/adev-es/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/README.md index f451bc0..d402823 100644 --- a/adev-es/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/README.md +++ b/adev-es/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/README.md @@ -1,32 +1,32 @@ -# Two-way binding with model signals +# Enlace bidireccional con model signals -Now that you've learned [passing data to components with input signals](/tutorials/signals/5-component-communication-with-signals), let's explore Angular's `model()` API for two-way binding. Model signals are perfect for UI components like checkboxes, sliders, or custom form controls where the component needs to both receive a value AND update it. +Ahora que has aprendido [cómo pasar datos a componentes con input signals](/tutorials/signals/5-component-communication-with-signals), exploremos la API `model()` de Angular para el enlace bidireccional. Los model signals son perfectos para componentes de UI como checkboxes, sliders o controles de formulario personalizados donde el componente necesita tanto recibir un valor como actualizarlo. -In this activity, you'll create a custom checkbox component that manages its own state while keeping the parent synchronized. +En esta actividad, crearás un componente de checkbox personalizado que gestiona su propio estado mientras mantiene sincronizado al padre.
- -Create a model signal in the `custom-checkbox` component that can both receive and update the parent's value. + +Crea un model signal en el componente `custom-checkbox` que pueda tanto recibir como actualizar el valor del padre. ```ts -// Add imports for model signals +// Agregar importaciones para model signals import {Component, model, input, ChangeDetectionStrategy} from '@angular/core'; -// Model signal for two-way binding +// Model signal para enlace bidireccional checked = model.required(); -// Optional input for label +// Input opcional para la etiqueta label = input(''); ``` -Unlike `input()` signals which are read-only, `model()` signals can be both read and written to. +A diferencia de los signals `input()` que son de solo lectura, los signals `model()` pueden ser tanto leídos como escritos. - -Build the checkbox template that responds to clicks and updates its own model. + +Construye la plantilla del checkbox que responde a clics y actualiza su propio model. ```html ``` -The component reads from its model signal and has a method to update it. +El componente lee de su model signal y tiene un método para actualizarlo. - -Implement the toggle method that updates the model signal when the checkbox is clicked. + +Implementa el método toggle que actualiza el model signal cuando se hace clic en el checkbox. ```ts toggle() { - // This updates BOTH the component's state AND the parent's model! + // ¡Esto actualiza TANTO el estado del componente COMO el model del padre! this.checked.set(!this.checked()); } ``` -When the child component calls `this.checked.set()`, it automatically propagates the change back to the parent. This is the key difference from `input()` signals. +Cuando el componente hijo llama a `this.checked.set()`, automáticamente propaga el cambio de vuelta al padre. Esta es la diferencia clave con los signals `input()`. - -First, uncomment the model signal properties and methods in `app.ts`: + +Primero, descomenta las propiedades y métodos de model signal en `app.ts`: ```ts -// Parent signal models +// Model signals del padre agreedToTerms = model(false); enableNotifications = model(true); -// Methods to test two-way binding +// Métodos para probar el enlace bidireccional toggleTermsFromParent() { this.agreedToTerms.set(!this.agreedToTerms()); } @@ -74,14 +74,14 @@ resetAll() { } ``` -Then update the template: +Luego actualiza la plantilla: -Part 1. **Uncomment the checkboxes and add two-way binding:** +Parte 1. **Descomenta los checkboxes y agrega enlace bidireccional:** -- Replace `___ADD_TWO_WAY_BINDING___` with `[(checked)]="agreedToTerms"` for the first checkbox -- Replace `___ADD_TWO_WAY_BINDING___` with `[(checked)]="enableNotifications"` for the second +- Reemplaza `___ADD_TWO_WAY_BINDING___` con `[(checked)]="agreedToTerms"` para el primer checkbox +- Reemplaza `___ADD_TWO_WAY_BINDING___` con `[(checked)]="enableNotifications"` para el segundo -Part 2. **Replace the `???` placeholders with @if blocks:** +Parte 2. **Reemplaza los placeholders `???` con bloques @if:** ```angular-html @if (agreedToTerms()) { @@ -97,38 +97,38 @@ Part 2. **Replace the `???` placeholders with @if blocks:** } ``` -Part 3. **Add click handlers to the buttons:** +Parte 3. **Agrega manejadores de clic a los botones:** ```html ``` -The `[(checked)]` syntax creates two-way binding - data flows down to the component AND changes flow back up to the parent by emitting an event that references the signal itself and does _not_ call the signal getter directly. +La sintaxis `[(checked)]` crea un enlace bidireccional — los datos fluyen hacia el componente Y los cambios fluyen de vuelta al padre emitiendo un evento que referencia el signal mismo y _no_ llama al getter del signal directamente. - -Interact with your app to see two-way binding in action: + +Interactúa con tu aplicación para ver el enlace bidireccional en acción: -1. **Click checkboxes** - Component updates its own state and notifies parent -2. **Click "Toggle Terms from Parent"** - Parent updates propagate down to component -3. **Click "Reset All"** - Parent resets both models and components update automatically +1. **Haz clic en los checkboxes** - El componente actualiza su propio estado y notifica al padre +2. **Haz clic en "Toggle Terms from Parent"** - Las actualizaciones del padre se propagan al componente +3. **Haz clic en "Reset All"** - El padre reinicia ambos models y los componentes se actualizan automáticamente -Both the parent and child can update the shared state, and both stay in sync automatically! +¡Tanto el padre como el hijo pueden actualizar el estado compartido, y ambos se mantienen sincronizados automáticamente! -Perfect! You've learned how model signals enable two-way binding: +¡Perfecto! Has aprendido cómo los model signals permiten el enlace bidireccional: -- **Model signals** - Use `model()` and `model.required()` for values that can be both read and written -- **Two-way binding** - Use `[(property)]` syntax to bind parent signals to child models -- **Perfect for UI components** - Checkboxes, form controls, and widgets that need to manage their own state -- **Automatic synchronization** - Parent and child stay in sync without manual event handling +- **Model signals** - Usa `model()` y `model.required()` para valores que pueden ser tanto leídos como escritos +- **Enlace bidireccional** - Usa la sintaxis `[(property)]` para enlazar signals del padre a models del hijo +- **Perfecto para componentes de UI** - Checkboxes, controles de formulario y widgets que necesitan gestionar su propio estado +- **Sincronización automática** - Padre e hijo se mantienen sincronizados sin manejo manual de eventos -**When to use `model()` vs `input()`:** +**Cuándo usar `model()` vs `input()`:** -- Use `input()` for data that only flows down (display data, configuration) -- Use `model()` for UI components that need to update their own value (form controls, toggles) +- Usa `input()` para datos que solo fluyen hacia abajo (datos de visualización, configuración) +- Usa `model()` para componentes de UI que necesitan actualizar su propio valor (controles de formulario, toggles) -In the next lesson, you'll learn about [using signals with services](/tutorials/signals/7-using-signals-with-services)! +En la próxima lección, aprenderás sobre [cómo usar signals con servicios](/tutorials/signals/7-using-signals-with-services)! diff --git a/adev-es/src/content/tutorials/signals/steps/7-using-signals-with-services/README.en.md b/adev-es/src/content/tutorials/signals/steps/7-using-signals-with-services/README.en.md new file mode 100644 index 0000000..15cc226 --- /dev/null +++ b/adev-es/src/content/tutorials/signals/steps/7-using-signals-with-services/README.en.md @@ -0,0 +1,103 @@ +# Using signals with services + +Now that you've learned [two-way binding with model signals](/tutorials/signals/6-two-way-binding-with-model-signals), let's explore how to use signals with Angular services. Services are perfect for sharing reactive state across multiple components, and signals make this even more powerful by providing automatic change detection and clean reactive patterns. + +In this activity, you'll learn how to create a cart store with signals that allow the cart display component to react to state changes automatically. + +
+ + + + +Add readonly and computed signals to make the cart state reactive in `cart-store.ts`. + +```ts +// Add the computed import +import {Injectable, signal, computed} from '@angular/core'; + +// Then add these signals to the class: + +// Readonly signals +readonly cartItems = this.items.asReadonly(); + +// Computed signals +readonly totalQuantity = computed(() => { + return this.items().reduce((sum, item) => sum + item.quantity, 0); +}); + +readonly totalPrice = computed(() => { + return this.items().reduce((sum, item) => sum + item.price * item.quantity, 0); +}); +``` + +These signals allow components to reactively access cart data and computed totals. The `asReadonly()` method prevents external code from modifying the cart items directly, while `computed()` creates derived state that automatically updates when the source signal changes. + + + +The cart display component in `cart-display.ts` already uses the cart store signals in its template. Complete the quantity update methods to modify cart items: + +```ts +increaseQuantity(id: string) { + const items = this.cartStore.cartItems(); + const currentItem = items.find((item) => item.id === id); + if (currentItem) { + this.cartStore.updateQuantity(id, currentItem.quantity + 1); + } +} + +decreaseQuantity(id: string) { + const items = this.cartStore.cartItems(); + const currentItem = items.find((item) => item.id === id); + if (currentItem && currentItem.quantity > 1) { + this.cartStore.updateQuantity(id, currentItem.quantity - 1); + } +} +``` + +These methods read the current cart state using `cartItems()` and update quantities through the store's methods. The UI automatically updates when the signals change! + + + +Update the main app component in `app.ts` to use the cart service and display the cart component. + +```angular-ts +import {Component, inject} from '@angular/core'; +import {CartStore} from './cart-store'; +import {CartDisplay} from './cart-display'; + +@Component({ + selector: 'app-root', + imports: [CartDisplay], + template: ` +
+
+

Signals with Services Demo

+
+ Cart: {{ cartStore.totalQuantity() }} items (\${{ cartStore.totalPrice() }}) +
+
+ +
+ +
+
+ `, + styleUrl: './app.css', +}) +export class App { + cartStore = inject(CartStore); +} +``` + +
+ +
+ +Excellent! You've now learned how to use signals with services. Key concepts to remember: + +- **Service-level signals**: Services can use signals to manage reactive state +- **Dependency injection**: Use `inject()` to access services with signals in components +- **Computed signals in services**: Create derived state that updates automatically +- **Readonly signals**: Expose read-only versions of signals to prevent external mutations + +In the next lesson, you'll learn about [how to use signals with directives](/tutorials/signals/8-using-signals-with-directives)! diff --git a/adev-es/src/content/tutorials/signals/steps/7-using-signals-with-services/README.md b/adev-es/src/content/tutorials/signals/steps/7-using-signals-with-services/README.md index 15cc226..f341f93 100644 --- a/adev-es/src/content/tutorials/signals/steps/7-using-signals-with-services/README.md +++ b/adev-es/src/content/tutorials/signals/steps/7-using-signals-with-services/README.md @@ -1,23 +1,23 @@ -# Using signals with services +# Usando signals con servicios -Now that you've learned [two-way binding with model signals](/tutorials/signals/6-two-way-binding-with-model-signals), let's explore how to use signals with Angular services. Services are perfect for sharing reactive state across multiple components, and signals make this even more powerful by providing automatic change detection and clean reactive patterns. +Ahora que has aprendido [cómo usar el enlace bidireccional con model signals](/tutorials/signals/6-two-way-binding-with-model-signals), exploremos cómo usar signals con servicios de Angular. Los servicios son perfectos para compartir estado reactivo entre múltiples componentes, y los signals hacen esto aún más poderoso al proporcionar detección de cambios automática y patrones reactivos limpios. -In this activity, you'll learn how to create a cart store with signals that allow the cart display component to react to state changes automatically. +En esta actividad, aprenderás cómo crear un carrito de compras (cart store) con signals que permita que el componente de visualización del carrito reaccione a los cambios de estado automáticamente.
- -Add readonly and computed signals to make the cart state reactive in `cart-store.ts`. + +Agrega signals de solo lectura y computed para hacer reactivo el estado del carrito en `cart-store.ts`. ```ts -// Add the computed import +// Agregar la importación de computed import {Injectable, signal, computed} from '@angular/core'; -// Then add these signals to the class: +// Luego agrega estos signals a la clase: -// Readonly signals +// Signals de solo lectura readonly cartItems = this.items.asReadonly(); // Computed signals @@ -30,11 +30,11 @@ readonly totalPrice = computed(() => { }); ``` -These signals allow components to reactively access cart data and computed totals. The `asReadonly()` method prevents external code from modifying the cart items directly, while `computed()` creates derived state that automatically updates when the source signal changes. +Estos signals permiten a los componentes acceder reactivamente a los datos del carrito y los totales calculados. El método `asReadonly()` evita que código externo modifique los items del carrito directamente, mientras que `computed()` crea estado derivado que se actualiza automáticamente cuando el signal fuente cambia. - -The cart display component in `cart-display.ts` already uses the cart store signals in its template. Complete the quantity update methods to modify cart items: + +El componente de visualización del carrito en `cart-display.ts` ya usa los signals del cart store en su plantilla. Completa los métodos de actualización de cantidad para modificar los items del carrito: ```ts increaseQuantity(id: string) { @@ -54,11 +54,11 @@ decreaseQuantity(id: string) { } ``` -These methods read the current cart state using `cartItems()` and update quantities through the store's methods. The UI automatically updates when the signals change! +Estos métodos leen el estado actual del carrito usando `cartItems()` y actualizan las cantidades a través de los métodos del store. ¡La UI se actualiza automáticamente cuando los signals cambian! - -Update the main app component in `app.ts` to use the cart service and display the cart component. + +Actualiza el componente principal de la app en `app.ts` para usar el servicio del carrito y mostrar el componente del carrito. ```angular-ts import {Component, inject} from '@angular/core'; @@ -93,11 +93,11 @@ export class App { -Excellent! You've now learned how to use signals with services. Key concepts to remember: +¡Excelente! Ahora has aprendido cómo usar signals con servicios. Conceptos clave para recordar: -- **Service-level signals**: Services can use signals to manage reactive state -- **Dependency injection**: Use `inject()` to access services with signals in components -- **Computed signals in services**: Create derived state that updates automatically -- **Readonly signals**: Expose read-only versions of signals to prevent external mutations +- **Signals a nivel de servicio**: Los servicios pueden usar signals para gestionar estado reactivo +- **Inyección de dependencias**: Usa `inject()` para acceder a servicios con signals en componentes +- **Computed signals en servicios**: Crea estado derivado que se actualiza automáticamente +- **Signals de solo lectura**: Expone versiones de solo lectura de los signals para prevenir mutaciones externas -In the next lesson, you'll learn about [how to use signals with directives](/tutorials/signals/8-using-signals-with-directives)! +En la próxima lección, aprenderás sobre [cómo usar signals con directivas](/tutorials/signals/8-using-signals-with-directives)! diff --git a/adev-es/src/content/tutorials/signals/steps/8-using-signals-with-directives/README.en.md b/adev-es/src/content/tutorials/signals/steps/8-using-signals-with-directives/README.en.md new file mode 100644 index 0000000..5f6dba3 --- /dev/null +++ b/adev-es/src/content/tutorials/signals/steps/8-using-signals-with-directives/README.en.md @@ -0,0 +1,110 @@ +# Using signals with directives + +Now that you've learned [using signals with services](/tutorials/signals/7-using-signals-with-services), let's explore how directives use signals. **The great news: signals work exactly the same in directives as they do in components!** The main difference is that since directives don't have templates, you'll primarily use signals in host bindings to reactively update the host element. + +In this activity, you'll build a highlight directive that demonstrates how signals create reactive behavior in directives. + +
+ + + + +Import the signal functions and create your reactive state. This works exactly the same as in components: + +```ts +import {Directive, input, signal, computed} from '@angular/core'; + +@Directive({ + selector: '[highlight]', +}) +export class HighlightDirective { + // Signal inputs - same as components! + color = input('yellow'); + intensity = input(0.3); + + // Internal state - same as components! + private isHovered = signal(false); + + // Computed signals - same as components! + backgroundStyle = computed(() => { + const baseColor = this.color(); + const alpha = this.isHovered() ? this.intensity() : this.intensity() * 0.5; + + const colorMap: Record = { + 'yellow': `rgba(255, 255, 0, ${alpha})`, + 'blue': `rgba(0, 100, 255, ${alpha})`, + 'green': `rgba(0, 200, 0, ${alpha})`, + 'red': `rgba(255, 0, 0, ${alpha})`, + }; + + return colorMap[baseColor] || colorMap['yellow']; + }); +} +``` + +Notice how this is identical to component patterns - the only difference is we're in a `@Directive` instead of `@Component`. + + + +Since directives don't have templates, you'll use signals in **host bindings** to reactively update the host element. Add the `host` configuration and event handlers: + +```ts +@Directive({ + selector: '[highlight]', + host: { + '[style.backgroundColor]': 'backgroundStyle()', + '(mouseenter)': 'onMouseEnter()', + '(mouseleave)': 'onMouseLeave()', + }, +}) +export class HighlightDirective { + // ... signals from previous step ... + + onMouseEnter() { + this.isHovered.set(true); + } + + onMouseLeave() { + this.isHovered.set(false); + } +} +``` + +The host bindings automatically re-evaluate when the signals change - just like template bindings in components! When `isHovered` changes, the `backgroundStyle` computed signal recalculates, and the host binding updates the element's style. + + + +Update the app template to demonstrate the reactive directive: + +```angular-ts +template: ` +
+

Directive with Signals

+ +
+ Hover me - Yellow highlight +
+ +
+ Hover me - Blue highlight +
+ +
+ Hover me - Green highlight +
+
+`, +``` + +The directive automatically applies reactive highlighting based on the signal inputs! +
+ +
+ +Perfect! You've now seen how signals work with directives. Some key takeaways from this lesson are: + +- **Signals are universal** - All signal APIs (`input()`, `signal()`, `computed()`, `effect()`) work the same in both directives and components +- **Host bindings are the primary use case** - Since directives don't have templates, you use signals in host bindings to reactively modify the host element +- **Same reactive patterns** - Signal updates trigger automatic re-evaluation of computed signals and host bindings, just like in component templates + +In the next lesson, you'll [learn how to query child elements with signal queries](/tutorials/signals/9-query-child-elements-with-signal-queries)! diff --git a/adev-es/src/content/tutorials/signals/steps/8-using-signals-with-directives/README.md b/adev-es/src/content/tutorials/signals/steps/8-using-signals-with-directives/README.md index 5f6dba3..b9a0f0f 100644 --- a/adev-es/src/content/tutorials/signals/steps/8-using-signals-with-directives/README.md +++ b/adev-es/src/content/tutorials/signals/steps/8-using-signals-with-directives/README.md @@ -1,15 +1,15 @@ -# Using signals with directives +# Usando signals con directivas -Now that you've learned [using signals with services](/tutorials/signals/7-using-signals-with-services), let's explore how directives use signals. **The great news: signals work exactly the same in directives as they do in components!** The main difference is that since directives don't have templates, you'll primarily use signals in host bindings to reactively update the host element. +Ahora que has aprendido [cómo usar signals con servicios](/tutorials/signals/7-using-signals-with-services), exploremos cómo las directivas usan signals. **La buena noticia: los signals funcionan exactamente igual en directivas que en componentes.** La diferencia principal es que, como las directivas no tienen plantillas, usarás principalmente signals en host bindings para actualizar reactivamente el elemento host. -In this activity, you'll build a highlight directive that demonstrates how signals create reactive behavior in directives. +En esta actividad, construirás una directiva de resaltado que demuestra cómo los signals crean comportamiento reactivo en directivas.
- -Import the signal functions and create your reactive state. This works exactly the same as in components: + +Importa las funciones de signal y crea tu estado reactivo. Esto funciona exactamente igual que en componentes: ```ts import {Directive, input, signal, computed} from '@angular/core'; @@ -18,14 +18,14 @@ import {Directive, input, signal, computed} from '@angular/core'; selector: '[highlight]', }) export class HighlightDirective { - // Signal inputs - same as components! + // Signal inputs - ¡igual que en componentes! color = input('yellow'); intensity = input(0.3); - // Internal state - same as components! + // Estado interno - ¡igual que en componentes! private isHovered = signal(false); - // Computed signals - same as components! + // Computed signals - ¡igual que en componentes! backgroundStyle = computed(() => { const baseColor = this.color(); const alpha = this.isHovered() ? this.intensity() : this.intensity() * 0.5; @@ -42,11 +42,11 @@ export class HighlightDirective { } ``` -Notice how this is identical to component patterns - the only difference is we're in a `@Directive` instead of `@Component`. +Observa cómo esto es idéntico a los patrones de componentes — la única diferencia es que estamos en una `@Directive` en lugar de `@Component`. - -Since directives don't have templates, you'll use signals in **host bindings** to reactively update the host element. Add the `host` configuration and event handlers: + +Como las directivas no tienen plantillas, usarás signals en **host bindings** para actualizar reactivamente el elemento host. Agrega la configuración `host` y los manejadores de eventos: ```ts @Directive({ @@ -58,7 +58,7 @@ Since directives don't have templates, you'll use signals in **host bindings** t }, }) export class HighlightDirective { - // ... signals from previous step ... + // ... signals del paso anterior ... onMouseEnter() { this.isHovered.set(true); @@ -70,11 +70,11 @@ export class HighlightDirective { } ``` -The host bindings automatically re-evaluate when the signals change - just like template bindings in components! When `isHovered` changes, the `backgroundStyle` computed signal recalculates, and the host binding updates the element's style. +Los host bindings se re-evalúan automáticamente cuando los signals cambian — ¡igual que los enlaces de plantilla en componentes! Cuando `isHovered` cambia, el computed signal `backgroundStyle` se recalcula y el host binding actualiza el estilo del elemento. - -Update the app template to demonstrate the reactive directive: + +Actualiza la plantilla de la app para demostrar la directiva reactiva: ```angular-ts template: ` @@ -96,15 +96,15 @@ template: ` `, ``` -The directive automatically applies reactive highlighting based on the signal inputs! +¡La directiva aplica automáticamente el resaltado reactivo basado en los signal inputs! -Perfect! You've now seen how signals work with directives. Some key takeaways from this lesson are: +¡Perfecto! Ahora has visto cómo funcionan los signals con directivas. Algunos puntos clave de esta lección: -- **Signals are universal** - All signal APIs (`input()`, `signal()`, `computed()`, `effect()`) work the same in both directives and components -- **Host bindings are the primary use case** - Since directives don't have templates, you use signals in host bindings to reactively modify the host element -- **Same reactive patterns** - Signal updates trigger automatic re-evaluation of computed signals and host bindings, just like in component templates +- **Los signals son universales** - Todas las APIs de signal (`input()`, `signal()`, `computed()`, `effect()`) funcionan igual en directivas y componentes +- **Los host bindings son el caso de uso principal** - Como las directivas no tienen plantillas, usas signals en host bindings para modificar reactivamente el elemento host +- **Mismos patrones reactivos** - Las actualizaciones de signals disparan la re-evaluación automática de computed signals y host bindings, igual que en las plantillas de componentes -In the next lesson, you'll [learn how to query child elements with signal queries](/tutorials/signals/9-query-child-elements-with-signal-queries)! +En la próxima lección, [aprenderás cómo consultar elementos hijos con signal queries](/tutorials/signals/9-query-child-elements-with-signal-queries)! diff --git a/adev-es/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/README.en.md b/adev-es/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/README.en.md new file mode 100644 index 0000000..8354746 --- /dev/null +++ b/adev-es/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/README.en.md @@ -0,0 +1,66 @@ +# Query child elements with signal queries + +Now that you've learned [how to use signals with directives](/tutorials/signals/8-using-signals-with-directives), let's explore signal-based query APIs. These provide a reactive way to access and interact with child components and directives. Both components and directives can perform queries while also being queried themselves. Unlike the traditional ViewChild, signal queries automatically update and provide type-safe access to child components and directives. + +In this activity, you'll add viewChild queries to interact with child components programmatically. + +
+ + + + +First, add the `viewChild` import to access child components in `app.ts`. + +```ts +import {Component, signal, computed, viewChild, ChangeDetectionStrategy} from '@angular/core'; +``` + + + + +Add viewChild queries to the App component to access child components. + +```ts +// Query APIs to access child components +firstProduct = viewChild(ProductCard); +cartSummary = viewChild(CartSummary); +``` + +These queries create signals that reference child component instances. + + + +Use the viewChild queries to call methods on child components in `app.ts`: + +```ts +showFirstProductDetails() { + const product = this.firstProduct(); + if (product) { + product.highlight(); + } +} + +initiateCheckout() { + const summary = this.cartSummary(); + if (summary) { + summary.initiateCheckout(); + } +} +``` + + + + +The control buttons should now work: + +- **"Show First Product Details"** - Calls `highlight()` on the ProductCard +- **"Initiate Checkout"** - Calls `initiateCheckout()` on the CartSummary + +Click the buttons to see how viewChild queries enable parent components to control child behavior. + + + + +Perfect! You've learned how to use signal-based query APIs for child component interaction: + +In the next lesson, you'll learn about [how to react to signal changes with effect](/tutorials/signals/10-reacting-to-signal-changes-with-effect)! diff --git a/adev-es/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/README.md b/adev-es/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/README.md index 8354746..00fee5c 100644 --- a/adev-es/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/README.md +++ b/adev-es/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/README.md @@ -1,15 +1,15 @@ -# Query child elements with signal queries +# Consulta elementos hijos con signal queries -Now that you've learned [how to use signals with directives](/tutorials/signals/8-using-signals-with-directives), let's explore signal-based query APIs. These provide a reactive way to access and interact with child components and directives. Both components and directives can perform queries while also being queried themselves. Unlike the traditional ViewChild, signal queries automatically update and provide type-safe access to child components and directives. +Ahora que has aprendido [cómo usar signals con directivas](/tutorials/signals/8-using-signals-with-directives), exploremos las APIs de consulta basadas en signals. Estas proporcionan una forma reactiva de acceder e interactuar con componentes y directivas hijos. Tanto los componentes como las directivas pueden realizar consultas mientras también pueden ser consultados ellos mismos. A diferencia del tradicional ViewChild, las signal queries se actualizan automáticamente y proporcionan acceso seguro con tipos a componentes y directivas hijos. -In this activity, you'll add viewChild queries to interact with child components programmatically. +En esta actividad, agregarás consultas viewChild para interactuar con componentes hijos programáticamente.
- -First, add the `viewChild` import to access child components in `app.ts`. + +Primero, agrega la importación de `viewChild` para acceder a componentes hijos en `app.ts`. ```ts import {Component, signal, computed, viewChild, ChangeDetectionStrategy} from '@angular/core'; @@ -17,20 +17,20 @@ import {Component, signal, computed, viewChild, ChangeDetectionStrategy} from '@ - -Add viewChild queries to the App component to access child components. + +Agrega consultas viewChild al componente App para acceder a componentes hijos. ```ts -// Query APIs to access child components +// APIs de consulta para acceder a componentes hijos firstProduct = viewChild(ProductCard); cartSummary = viewChild(CartSummary); ``` -These queries create signals that reference child component instances. +Estas consultas crean signals que referencian instancias de componentes hijos. - -Use the viewChild queries to call methods on child components in `app.ts`: + +Usa las consultas viewChild para llamar métodos en componentes hijos en `app.ts`: ```ts showFirstProductDetails() { @@ -50,17 +50,17 @@ initiateCheckout() { - -The control buttons should now work: + +Los botones de control ahora deberían funcionar: -- **"Show First Product Details"** - Calls `highlight()` on the ProductCard -- **"Initiate Checkout"** - Calls `initiateCheckout()` on the CartSummary +- **"Show First Product Details"** - Llama a `highlight()` en el ProductCard +- **"Initiate Checkout"** - Llama a `initiateCheckout()` en el CartSummary -Click the buttons to see how viewChild queries enable parent components to control child behavior. +Haz clic en los botones para ver cómo las consultas viewChild permiten que los componentes padre controlen el comportamiento de los hijos. -Perfect! You've learned how to use signal-based query APIs for child component interaction: +¡Perfecto! Has aprendido cómo usar las APIs de consulta basadas en signals para la interacción con componentes hijos. -In the next lesson, you'll learn about [how to react to signal changes with effect](/tutorials/signals/10-reacting-to-signal-changes-with-effect)! +En la próxima lección, aprenderás sobre [cómo reaccionar a cambios de signal con effect](/tutorials/signals/10-reacting-to-signal-changes-with-effect)!