Recipes App – Parte 2

Retomamos nuestra Recipes app donde la dejamos. Aquí tienes la parte #1 si te la perdiste. Si acabas de aterrizar aquí y no tienes ni idea de Angular, échale un vistazo a esta guía de iniciación. Y si ya estás a tope con los componentes y databinding pero quieres refrescar tus conocimientos, aquí tienes una guía completa sobre el asunto. Dicho esto, ¡vamos al lío!

En esta parte #2 vamos a pulir la navegación de nuestra app, mostrando distintas interfaces según qué link pulsemos en el menú de navegación. También haremos posible seleccionar una receta de nuestra lista de recetas y mostrar los detalles a un lado. 

Cómo añadir la navegación usando event binding ngIf

Lo que vamos a hacer a continuación es configurar la navegación para que nuestra app muestre, o bien las recipes, o bien la shopping list. Pero no ambas cosas a la vez, como está ahora. 

La solución que vamos a aplicar es temporal, ya que utilizaremos conceptos básicos de Angular, como la comunicación entre parent component child component. Más adelante aprenderemos a hacer esto con herramientas más avanzadas, ?‍♀️ pero por ahora vamos a usar custom property binding y la directiva ngIf.

Nuestros componentes Recipes y ShoppingList se encuentran dentro del app.component.html al mismo nivel. Es decir, son componentes hermanos. A su vez, estos componentes son child components del AppComponent siendo éste su parent component.

root (AppComponent)
header
shopping list
recipe list

Es dentro del AppComponent donde vamos a determinar cuándo mostrar un child component  u otro. Sin embargo, la navegación es algo que deberemos configurar en el HeaderComponentDesde el HeaderComponent le pasaremos la información al AppComponent del link sobre el que se ha hecho click, y el AppComponent se encargará entonces de mostrar las recipes o la shopping-list (utilizando un ngIf).

Para determinar qué child component debe mostrar el AppComponent, debemos crear y emitir nuestro propio evento desde el HeaderComponent.

1. En el header.component.htmlvamos a trabajar con los links a las recipes y a la shopping list. Es decir, con:

<li class="nav-item"> <a class="nav-link" href="#">Recipes</a> </li> <li class="nav-item"> <a class="nav-link" href="#">Shopping List</a> </li>
  • Nos creamos un click event por cada <a> y un método llamado onSelect.
  • A ese método le pasamos por parámetro un string según sean las recipes o la shopping list. A estas strings podemos llamarlas como queramos. Para ser consistentes, las llamaremos recipes shopping-list.
      <li class="nav-item">
        <a class="nav-link" href="#" (click)="onSelect('recipes')">Recipes</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="#" (click)="onSelect('shopping-list')">Shopping list</a>
      </li>

2. Implementamos el método onSelect en el HeaderComponent.

Al método le estamos pasando un string, así que debemos configurarlo para que reciba algún parámetro de tipo string. Ya que podríamos considerar nuestras secciones de recipes y shopping list como elementos de nuestro menú de navegación de nuestra app, a este parámetro lo llamamos navbarItemque será de tipo string. 

Ahora vamos a emitir nuestro custom event:

  • Nos creamos nuestro event emitter, al que llamaremos navbarItemSelected. No olvides importarlo desde @angular/core.
  • Le añadimos el Output decorator para que pueda ser escuchado desde fuera (desde el parent component, vaya). Lo importamos también desde @angular/core. 
  • Usamos nuestro event emitter dentro del método onSelect, y emitimos el parámetro navbarItem
  @Output() navbarItemSelected = new EventEmitter<string>();

  onSelect(navBarItem: string) {
    this.navbarItemSelected.emit(navBarItem);
  }

3. Volvemos al app.component.html, donde, dentro del selector app-header, vamos a vincular nuestro custom event (navbarItemSelected) a una función que gestionará qué hacer cuando se haga click en recipes o en shopping list. A esa función la llamamos onNavigate

Necesitaremos la información que nos llegue desde el evento navbarItemSelected. Para capturar dicha información, usaremos el $eventpasado por parámetro al método onNavigate. 

<app-header (navbarItemSelected)="onNavigate($event)"></app-header>

4. Vamos al app.component.ts y configuramos el método onNavigate

Por parámetro le vamos a pasar la información sobre a qué sección específica vamos a navegar, es decir, a recipes o a shopping list. Al parámetro lo llamamos navItem, que será de tipo string. 

Ahora debemos asegurarnos de almacenar dicho parámetro, así que declaramos una variable llamada loadedNavbarItem y le asignamos el valor de recipes. Le damos este valor porque así es como diferenciaremos si se hace click en el link de recipes o en el de shopping list. 

Dentro del método onNavigate, asignamos la variable loadedNavbarItem al valor que recibamos por parámetro, o sea, a navItem

  loadedNavbarItem = 'recipes';

  onNavigate(navItem: string) {
    this.loadedNavbarItem = navItem;
  }

5. Volvemos al app.component.html y añadimos un ngIf a cada child component. La lógica es sencillamente que deberá mostrarse el RecipesComponent si la propiedad loadedNavbarItem equivale a "recipes". O mostrarse el componente ShoppingList en caso de que loadedNavbarItem no sea igual a "recipes". 

      <app-recipes *ngIf="loadedNavbarItem === 'recipes'"></app-recipes>
      <app-shopping-list *ngIf="loadedNavbarItem !== 'recipes'"></app-shopping-list>

Y con este cambio, ve a tu navegador y comprueba que ahora sólo se muestra la sección de recipes o la de shopping list según si haces click en un enlace del navbar u otro. ?

Cómo convertir una receta en un componente y pasarle información desde su parent component

Lo siguiente que vamos a hacer es encargarnos de que cualquier receta (recipe-item) sea un componente independiente. Si nos fijamos en el archivo recipe-list.component.html, vemos que dentro de éste tenemos el código que define una receta individual. Es esta parte: 

    <div class="list-group" *ngFor="let recipe of recipes">
      <a href="#" class="list-group-item list-group-item-action d-flex">
        <div>
          <h4 class="list-group-item-heading">{{recipe.name}} </h4>
          <p class="list-group-item-text">{{recipe.description}} </p>
        </div>
        <span class="ml-auto my-auto">
          <img [src]="recipe.imagePath" alt="{{recipe.name}}" class="img-responsive" 
               style="max-height: 50px;">
        </span>
      </a>
    </div>

Recuerda que tenemos un RecipeItemComponent que de momento está vacío. Este es el componente sobre el que vamos a trabajar.

root (AppComponent)
recipes
recipe list
recipe item

1. Sabiendo cómo pasar información de un componente a otrovamos al archivo recipe-list.component.html y cortamos el código referente a una receta individual. O sea, el snippet de arriba. Lo pegamos en el archivo recipe-item.component.html.

Con este cambio, verás que la lista de recetas ha desaparecido de tu navegador. ?

2. Sacamos el ngFor loop de ese bloque de código,

    <div class="list-group">
      <a href="#" class="list-group-item list-group-item-action d-flex">
        <div>
          <h4 class="list-group-item-heading">{{recipe.name}} </h4>
          <p class="list-group-item-text">{{recipe.description}} </p>
        </div>
        <span class="ml-auto my-auto">
          <img [src]="recipe.imagePath" alt="{{recipe.name}}" class="img-responsive" 
               style="max-height: 50px;">
        </span>
      </a>
    </div>

3. y lo añadimos al selector <app-recipe-item> del archivo recipe-list.component.html.

<app-recipe-item *ngFor="let recipe of recipes"></app-recipe-item>

Fíjate en que, al haber trasladado el código de la receta individual al archivo recipe-item.component.html, ya no tenemos acceso a la variable recipe. Es decir, a la subrayada en amarillo: 

   <h4 class="list-group-item-heading">{{recipe.name}} </h4>
   <p class="list-group-item-text">{{recipe.description}} </p>
 </div>
 <span class="ml-auto my-auto">
   <img [src]="recipe.imagePath" alt="{{recipe.name}}" class="img-responsive" 
        style="max-height: 50px;">

4. Para solucionar esto, nos creamos una variable llamada recipe en el archivo recipe-item.component.ts, que será de tipo Recipe. No olvides importar el modelo desde su carpeta correspondiente. 

No vamos a asignarle ningún valor a esta variable, ya que queremos obtener su valor desde fuera, es decir, desde el parent component (el RecipeListComponent). Le añadimos el decorador @Input() para poder obtener un valor desde fuera de este componente, y lo importamos desde @angular/core.  

  @Input() recipe: Recipe;

5. Volvemos al archivo recipe-list.component.html y vinculamos nuestra propiedad recipe recién creada, usando property binding. Lo hacemos dentro del selector <app-recipe-item>y la vinculamos a la variable local recipe, del loop ngFor.

<app-recipe-item *ngFor="let recipe of recipes" [recipe]="recipe"></app-recipe-item>

Resulta bastante confuso tener la palabra "recipe" 3 veces, cada vez referido a cosas distintas

1er "recipe" ? variable local del loop

2º "recipe" ? propiedad del RecipeItemComponent

3er "recipe" ? variable local del loop

Así que vamos a hacerlo más claro, cambiando el nombre de la variable local del loop por recipeEle.

<app-recipe-item *ngFor="let recipeEle of recipes" [recipe]="recipeEle"></app-recipe-item>

¡Y listo! La sección de recetas de nuestra app debería tener el mismo aspecto que antes, pero ahora hemos mejorado el código.

sección recetas - receta indiividual

Cómo mostrar los detalles de una receta a partir de nuestra lista de recetas

Vamos a trabajar en la característica de mostrar a la derecha los detalles de una receta (recipe details) al hacer click en ella en el lado izquierdo (en la lista de recetas). Por ejemplo, al hacer click en el primer título, "Recipe title test". Ya que se llama igual que el segundo, para que nuestro ejemplo sea más ilustrativo, vamos a cambiarle el título en el recipe-list.component.ts, al menos uno de ellos:

    new Recipe('Paella valenciana',

Ah, mucho mejor, ahora el primer título dice "Paella valenciana" y el segundo "Recipe title test". Así resulta fácil distinguirlos. ?

Hecho esto, debemos entender cuáles son las relaciones actuales entre nuestros componentes. Es decir, quién es el parent component y quién el child. Aquí un esquemita que lo ilustra:

levels component communication

Como ves, tenemos 3 niveles y el segundo nivel está compuesto por dos componentes que actúan como parent de un componente y como child del RecipesComponent.

Trabajar con una jerarquía de componentes a 3 niveles no es muy práctico. ? Existe una manera más eficiente de hacer esto, pero por ahora, vamos a hacerlo así. Más adelante lo optimizaremos.

Aquí un esquema visual de lo que vamos a hacer:

esquema comunicación componentes

? Que no cunda el pánico, que vamos a explicarlo paso por paso. Después, vuelve al esquema y verás como lo entiendes mejor. A mí me ayuda leerlo desde abajo hacia arriba, porque ese es el orden que vamos a seguir para escribir nuestro código.

1. Vamos al archivo recipe-item.component.html. Lo que queremos hacer aquí es emitir un evento que informe a su parent component (el recipe-list) que se ha hecho click sobre una receta (sobre el <a>). Ya no necesitamos el <div> que engloba al <a>, porque el ngFor en el recipe-list.component.html se encarga de mostrar todos los <a> que necesitemos como si fuera una "lista". Así que lo borramos.  

2. Añadimos un click event al link de la receta y lo vinculamos a un método al que llamamos onSelected.

<a href="#" class="list-group-item list-group-item-action d-flex" (click)="onSelected()">
  <div>
    <h4 class="list-group-item-heading">{{recipe.name}} </h4>
    <p class="list-group-item-text">{{recipe.description}} </p>
  </div>
  <span class="ml-auto my-auto">
    <img [src]="recipe.imagePath" alt="{{recipe.name}}" class="img-responsive"
          style="max-height: 50px;">
  </span>
</a>

3. Configuramos el método onSelected en el archivo recipe-item.component.ts. Como queremos emitir nuestro propio evento, nos creamos un event emitter llamado recipeSelectedpor ejemplo. No olvides importarlo desde @angular/coreEl evento en sí no va a pasar ninguna información, así que indicamos void como tipo de dato.

Le añadimos el @Output decorator para que podamos escuchar este evento desde fuera (desde su parent component). No olvides importarlo desde @angular/core. 

Emitimos el evento a través de nuestro método onSelected, sin ninguna información.

  @Output() recipeSelected = new EventEmitter<void>();

  constructor() { }

  ngOnInit() {
  }

  onSelected() {
    this.recipeSelected.emit();
  }

4. Vamos al parent component (el recipe-list) y le pasamos nuestro evento recipeSelected por event binding. Y aquí viene lo complejo de este sistema. Debemos acceder a la receta que ha sido seleccionada, lo que en teoría podríamos hacer vinculando el evento recipeSelected con la variable local del ngFor loop (recipeEle). 

Pero esto no funcionaría, porque necesitamos emitir nuestro propio evento (otro, sí). Esto es así porque tenemos que subir un nivel en la escalera de componentes, ya que el componente recipes es el que implementa el child component recipe-list. 

Y todo esto para informar a un componente de que se ha hecho click en un link. ?

Así que nos toca emitir otro evento en el RecipeListComponent. Para ello, creamos un método al que llamamos onRecipeSelected y lo vinculamos al evento recipeSelected. A este método le pasamos por parámetro la receta que haya sido seleccionada, a la que tenemos acceso a través de la variable local recipeEle.

<app-recipe-item *ngFor="let recipeEle of recipes" [recipe]="recipeEle"
                 (recipeSelected)="onRecipeSelected(recipeEle)">
</app-recipe-item>

5. Vamos a configurar el método onRecipeSelected en el archivo recipe-list.component.ts. 

Ya que queremos recibir como información la receta sobre la que se ha hecho click, se lo indicamos por parámetro. El parámetro será de tipo Recipe. 

6. Creamos nuestro event emitter. Un nombre apropiado sería recipeSelected, pero dado que ya hemos usado ese nombre, para no hacernos un lío, vamos a inventarnos otro nombre, por ejemplo, recipeWasSelected

Nuestro evento emitirá una receta, así que le indicamos que el tipo de dato a emitir será una Recipe. No olvides añadir el @Output decorator delante del event emitter. 

Por último, añadimos nuestro event emitter al método onRecipeSelected, donde le pasamos el parámetro como dato a emitir. 

  @Output() recipeWasSelected = new EventEmitter<recipe>();
  recipes: Recipe[] = [
    new Recipe('Paella valenciana',
      'Recipe description',
      'https://upload.wikimedia.org/wikipedia/commons/thumb/e/ed/01_Paella_Valenciana_original.jpg/800px-01_Paella_Valenciana_original.jpg'),
    new Recipe('Recipe title test',
      'Recipe description',
      'https://p1.pxfuel.com/preview/683/172/968/cake-sponge-cake-bowl-cake-small.jpg')
  ];

  constructor() { }

  ngOnInit() {
  }

  onRecipeSelected(recipe: Recipe) {
    this.recipeWasSelected.emit(recipe);
  }

7. Vamos al parent component del recipe-list (RecipesComponent) y le añadimos nuestro custom event:

<app-recipe-list (recipeWasSelected)="">

Necesitamos vincular este evento con la receta que ha sido seleccionada, y guardar dicha receta. 

8. Para ello, vamos al archivo recipes.component.ts y nos creamos una variable llamada selectedRecipe. Será de tipo Recipe, así que no olvides importar el modeloPor el momento sólo vamos a declarar la variable sin asignarle ningún valor:

selectedRecipe: Recipe;

9. El valor lo vamos a extraer directamente desde la template del RecipesComponent. Ahí, vinculamos nuestro evento personalizado (recipeWasSelected) con la propiedad selectedRecipe, a la que asignamos el valor de $eventRecuerda que $event es una palabra clave que nos permite extraer información del evento al que esté vinculada. 

10. Con estos pasos hechos, ahora podemos pasarle cierta información a nuestro componente recipe-detail, que estamos usando por medio de su selector <app-recipe-detail> en el RecipesComponent.

Primero, debemos comprobar si alguna receta ha sido seleccionada. Esto lo hacemos usando un ngIf sobre el selector, vinculado a la propiedad selectedRecipe. En caso negativo, mostraremos un texto informando de que ninguna receta ha sido seleccionada. Para esto, usamos <ng-template> y una local reference, a la que llamamos #infoText. 

Esa local reference se la pasamos al ngIf como un else statement. Es decir:

  • Si una receta es seleccionada ? se aplica el código del bloque if
  • Si ninguna receta es seleccionada ? se aplica el código del bloque else (se muestra el mensaje).
<app-recipe-list (recipeWasSelected)="selectedRecipe = $event"></app-recipe-list>
  </div>
  <div class="col-md-7">
    <app-recipe-detail *ngIf="selectedRecipe; else infoText"></app-recipe-detail>
    <ng-template #infoText>
      <p class="mt-5">Please select a recipe!</p>
    </ng-template>
  </div>

11. Si vas al navegador, verás que, por defecto, sale nuestro mensaje Please select a recipe!. Pero al hacer click en una receta, no se muestran los detalles a la derecha. Eso es porque aún no se los hemos pasado... ?

Así que vamos al recipe-detail.component.ts y añadimos una propiedad que se encargue de esto. La llamamos recipe, que será de tipo Recipe, así que no olvides importar el modelo. Ya que queremos tener la posibilidad de obtener el valor de la propiedad recipe desde fuera del componente (desde el parent component), le añadimos el decorador @Input()

  @Input() recipe: Recipe;

12. Y con estos cambios ya podemos volver al archivo recipes.component.html y vincular con property binding la propiedad recipe recién creada a selectedRecipe.

<app-recipe-detail *ngIf="selectedRecipe; else infoText" [recipe]="selectedRecipe">
</app-recipe-detail>

13. Con el acceso a esta información, ahora podemos usar esa misma propiedad (recipe) para mostrar información de cada receta en la template del componente RecipeDetail usando string interpolation. Información como el recipe name, el path de la imagen y la description.

    <img [src]="recipe.imagePath" alt="{{ recipe.name }}" class="img-responsive">
  </div>
</div>
<div class="row">
  <div class="col">
    <h2>{{ recipe.name }} </h2>
  </div>
</div>
<div class="row my-2">
  <div class="col">
    <div class="dropdown">
      <button class="btn btn-primary dropdown-toggle" type="button"
              id="dropdownMenuButton" data-toggle="dropdown"
              aria-haspopup="true" aria-expanded="false">
        Manage recipe
      </button>
      <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
        <a class="dropdown-item" href="#">To shopping list</a>
        <a class="dropdown-item" href="#">Edit recipe</a>
        <a class="dropdown-item" href="#">Delete recipe</a>
      </div>
    </div>
  </div>
</div>
<div class="row">
  <div class="col">
    {{ recipe.description }}
  </div>
</div>

14. Si vas a tu navegador, verás que ya se muestras los detalles de la receta sobre la que hagas click. Pero tenemos un pequeño problema con el tamaño de imagen, que sencillamente corregimos añadiendo un tamaño máximo a la imagen con un inline style.

    <img [src]="recipe.imagePath" alt="{{ recipe.name }}"
         class="img-responsive" style="max-height: 300px;">

¡Y listo! ?

Cómo añadir ingredientes a la shopping list

Vamos a trabajar con la sección de la shopping list, haciendo funcionar los campos y los botones de manera muy básica, de momento. Más adelante utilizaremos los Forms de Angular para gestionar estos campos (en inglés, input fields).

Es importante que tengas importado el "FormsModule" en tu app.module.ts, tanto en los "imports" como en el array "imports".
...  
import { FormsModule } from '@angular/forms';   // TypeScript import
...  
imports:   [  
    ...  
    FormsModule                                 // Angular import
]  
...

Lo que vamos a hacer es habilitar la posibilidad de añadir ingredientes a nuestra lista. Vamos a trabajar con el componente shopping-list-edit, usando local references @ViewChild(). A través de estas herramientas, conseguiremos generar un nuevo ingrediente al pulsar el botón "add", añadiéndolo a nuestro array de ingredientes.

1. En el shopping-list-edit.component.html, añadimos local references a los inputs del name y el amount. Los llamamos #nameInput #amountInput respectivamente.

2. Añadimos un click event al botón "add" y lo vinculamos a un método al que llamamos onAddItem().

          <input type="text" id="name" class="form-control" #nameInput>
        </div>
        <div class="col-sm-2 form-group">
          <label for="amount">Amount</label>
          <input type="number" id="amount" class="form-control" #amountInput>
        </div>
      </div>
      <div class="row">
        <div class="col">
          <button class="btn btn-success" type="submit" (click)="onAddItem()">Add</button>

3. Nos vamos al archivo shopping-list-edit.component.ts y creamos una propiedad llamada nameInputRef, de tipo ElementRefDecoramos la propiedad con el @ViewChild. No olvides importar este data type y @ViewChild desde angular/core.

Al @ViewChild le pasamos como argumento la local reference del input del nombre. No olvidemos añadir un segundo argumento (obligatorio en Angular 8) al @ViewChild ({static: false}). ?

Replicamos estos pasos con una segunda propiedad, a la que llamamos amountInputRef, que naturalmente hace referencia al input de la cantidad. 

4. Creamos y emitimos nuestro propio evento. Lo llamamos ingredientAdded y le pasamos nuestro Ingredient model como data type. Recuerda que este modelo está compuesto sencillamente por un objeto con el nombre del ingrediente y la cantidad (ésos son nuestros key-value pairs). No olvides importarlo desde su carpeta correspondiente y añadirle el decorador @Output delante.

5. Configuramos nuestro método onAddItem(). Para ello, creamos una instance del Ingredient y la guardamos en una variable, a la que llamamos newIngredient. A esta variable le pasamos el valor que vaya a tener el input element del nombre (name input). Esto lo hacemos accediendo a la value del nameInputRef a través de su propiedad nativeElement.

De la misma manera, le pasamos un segundo argumento (el resultado del amount input).

  @ViewChild('nameInput', { static: false }) nameInputRef: ElementRef;
  @ViewChild('amountInput', { static: false }) amountInputRef: ElementRef;
  @Output() ingredientAdded = new EventEmitter<ingredient>();

  constructor() { }

  ngOnInit() {
  }

  onAddItem() {
    const newIngredient = new Ingredient(this.nameInputRef.nativeElement.value, this.amountInputRef.nativeElement.value);
  }

6. Dado que eso nos daría una línea de código larguísima, vamos a optimizarla creándonos dos variables (ingName ingAmount) donde guardar los resultados de los inputs name amount. Y pasamos dichas variables como argumentos al newIngredient.

7. Con esto hecho, ya podemos emitir nuestro propio evento y pasarle el newIngredient como dato a emitir.

  onAddItem() {
    const ingName = this.nameInputRef.nativeElement.value;
    const ingAmount = this.amountInputRef.nativeElement.value;
    const newIngredient = new Ingredient(ingName, ingAmount);
    this.ingredientAdded.emit(newIngredient);
  }

8. Volvemos a nuestro archivo shopping-list.component.html e incluimos nuestro evento ingredientAdded al elemento <app-shopping-list-edit>. Lo vinculamos a un método al que llamamos onIngredientAdded() usando event binding. 

Queremos obtener cierta información de dicho método, así que la obtendremos pasándole el $event.

<app-shopping-list-edit (ingredientAdded)="onIngredientAdded($event)">
</app-shopping-list-edit>

9. Vamos al archivo shopping-list.component.ts para configurar el método onIngredientAdded(). Le pasamos un argumento, que será un ingrediente de tipo Ingredient. Para añadir un nuevo ingrediente, simplemente utilizamos el método push, que añadirá el ingrediente al array ingredients.

  onIngredientAdded(ingredient: Ingredient) {
    this.ingredients.push(ingredient);
  }

Y con estos cambios, ya deberíamos poder ir a nuestro navegador y añadir un ingrediente y la cantidad. Evidentemente no es perfecto, pero hemos dado otro pasito adelante para mejorar nuestra app. ✌

ingredients added

¡BRAVO!

¡Y hasta aquí la parte #2 de nuestra Recipes App! Si quieres seguir aprendiendo, estate atento a la parte #3. 

Sobre la autora de este post

Soy Rocío, una abogada reconvertida en programadora. Soy una apasionada de aprender cosas nuevas y ferviente defensora de que la única manera de ser feliz es alcanzando un equilibrio entre lo que te encanta hacer y lo que te saque de pobre. Mi historia completa, aquí. 

Más recursos de aprendizaje

En mi experiencia, la manera más eficaz para aprender Angular es combinando varias vías de aprendizaje. Uno de mis métodos favoritos son los vídeo-cursos y mi plataforma predilecta para eso es Udemy. He hecho varios cursos pero sólo recomiendo aquellos que verdaderamente me han sido útiles. Aquí van:

angular the complete guide - curso Max S.

  Max Schwarzmüller

curso angular fernando herrera

   Fernando Herrera

Si necesitas apoyo en forma de libro, puede que éstos te sirvan de ayuda:

libro 1 angular
libro 2 angular

La programación es un mundo que evoluciona a una velocidad de vértigo. Los autores de estos libros lo saben, por eso suelen encargarse de actualizar su contenido regularmente. Asegúrate de que así sea antes de adquirirlos ?.

Participo en el programa de afiliados de Udemy y Amazon, lo que significa que, si compras alguno de estos cursos y/o libros, yo me llevaré una pequeña comisión y a ti no costará nada extra. Vamos, lo que se dice un win-win ?.

Otros artículos que pueden interesarte

Cómo aprendí a programar cuando estaba «programada» para ser de letras
[tcb-script src="https://player.vimeo.com/api/player.js"][/tcb-script]A nadie le gusta su trabajo. Eso es lo que me decía a mí misma cuando conseguí mi primer[...]
Días del 160 al 203 – ¡Primer objetivo conseguido!
“A veces podemos pasarnos años sin vivir en absoluto, y de pronto toda nuestra vida se concentra en un solo[...]
Claves para entender Angular. Qué es y cómo se utiliza
Angular es un framework creado por Google que nos permite construir Single Page Applications (SPA, por sus siglas en inglés).Frameworks¿Pero qué es[...]
Si crees que este post puede serle útil a alguien, por favor, ¡compártelo!:

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Esta web utiliza cookies para asegurar que se da la mejor experiencia al usuario. Si continúas utilizando este sitio se asume que estás de acuerdo. más información

Los ajustes de cookies en esta web están configurados para «permitir las cookies» y ofrecerte la mejor experiencia de navegación posible. Si sigues usando esta web sin cambiar tus ajustes de cookies o haces clic en «Aceptar», estarás dando tu consentimiento a esto.

Cerrar