Recipes App – Parte 1

¿Qué vamos a construir?

Una sencilla aplicación de recetas de cocina con Angular, basada en una de las prácticas de este curso de Angular.

¡ATENCIÓN!

Si acabas de aterrizar aquí y estos temas de Angular te suenan a chino, te recomiendo que eches un vistazo a esta introducción a Angular antes.

Conocimientos previos necesarios

Para poder seguir esta parte #1 de la serie sin perderte, deberás tener una base sólida de JavaScript (en adelante, JS) y conocimientos básicos sobre componentes, databinding y directivas.

Planificación inicial

El primer paso para crear cualquier app es definir una estructura inicial, es decir, un boceto que represente visualmente las diferentes partes que va a tener nuestra app.

Nuestro boceto recoge:

  • características (en inglés, features) que necesita nuestra app 
  • componentes
  • modelos (explicados abajo)

root (AppComponent)

header

shopping list

shopping list

shopping list edit

ingredient

recipe book

recipe list

recipe item

recipe detail

recipe

  • Nuestras features representan las secciones o bloques que tendremos en nuestra app. Cada bloque tiene sus propios componentes.
  • Los modelos representan nuestra información (en inglés, data) y cómo estructurarla o dividirla.
    • ingredient: nombre, cantidad, ...
    • recipe:  título, descripción, ingredientes, ...
  • Tendremos varios componentes anidados (en inglés, nested components), como el shopping list edit component, que irá dentro del shopping list component.

Ten en cuenta que lo normal es que un proyecto sufra pequeñas modificaciones en su estructura a medida que lo vamos construyendo. Así que cambiaremos el boceto cuando sea necesario, aunque el objetivo debería ser siempre definir una sólida estructura desde el principio y modificarla lo menos posible. 

Creación del esqueleto de un proyecto e instalación de Bootstrap

Hecho el plan, vamos a crearnos un nuevo proyecto en Angular al que llamaremos recipes-app.

1. Limpiamos el app.component.html del código con el que viene Angular por defecto, y lo dejamos completamente vacío.

2. Añadimos Bootstrap. Vamos a utilizar este fantástico framework de CSS para poder darle estilo a nuestra app de manera rápida y elegante. En mi caso, estoy usando la v4, tenlo en cuenta por si están leyendo este artículo en un futuro lejano. ?

3. Añadimos algo de código al app.component.html para comprobar que todo marcha bien.

<div class="container">
  <div class="row">
    <div class="col">
      <h4>I'm working fine!</h4>
    </div>
  </div>
</div>

Al guardar ya deberías ver ese texto del <h4> en tu navegador. ?

Creación de los componentes de la app

Vamos a crear la estructura de componentes de nuestro proyecto. No todos los componentes deben ser descendientes directos del AppComponent, sino que debemos anidarlos unos dentro de otros de una manera coherente. A estas alturas, vamos a hacer una pequeña modificación en el boceto del proyecto, ya que tiene más sentido crear un componente recipes que albergue como hijos al recipe list, recipe item recipe details. 

Boceto actualizado:

root (AppComponent)

header

shopping list

shopping list

shopping list edit

ingredient

recipe book

recipes

recipe list

recipe item

recipe detail

recipe

 Aquí un esquemita de nuestra estructura de carpetas, para entendernos:

  • app component
    • header component
    • recipes component ? componente con rol de contenedor (en inglés, container o wrapper)
      • recipe-list component
        • recipe-item component ? para definir cada elemento individual de la lista de recetas. Por ejemplo, "paella"
      • recipe-detail component ? para mostrar la info completa de una receta. Por ejemplo, "detalles de la receta paella". Lo mostraremos al lado del recipe-list
    • shopping-list component
      • shopping-list-edit component ? para añadir ingredientes y/o eliminar/ editarlos

1. Usamos Angular CLI para crear los componentes, mediante el comando ng generate component [component-name] --skipTests. El --skipTests evita que se cree un archivo de testing que no vamos a usar.

Empezaremos creando el HeaderComponent.

Existe una versión abreviada de ese comando, que es la que usaré a lo largo del proyecto: ng g c header --s kipTests.

2. Una vez creado, vamos a editar su plantilla HTML (en inglés, template) con cualquier contenido, por ejemplo "this is the header component".

<h3>this is the header component</h3>

3. Lo añadimos al principio del archivo app.component.html usando su selector <app-header>.

<app-header></app-header>
<div class="container">
  <div class="row">
    <div class="col">
      <h4>I'm working fine!</h4>
    </div>
  </div>
</div>

4. Vamos a seguir creando nuestros componentes. Para anidar unos dentro de otros, simplemente hay que especificar la ruta (en inglés, path) del componente donde queremos crear un componente hijo.

ng g c recipes --skipTests
ng g c recipes/recipe-list --skipTests
ng g c recipes/recipe-detail --skipTests
ng g c recipes/recipe-list/recipe-item --skipTests
ng g c shopping-list --skipTests
ng g c shopping-list/shopping-list-edit --skipTests

Posicionando los componentes

Vamos a empezar a usar nuestros componentes, y para eso tenemos que colocarlos donde queremos que se muestren en la UI. 

1. Queremos mostrar los componentes RecipesComponent ShoppingListComponent dentro del AppComponent, así que añadimos sus selectores (<app-recipes> y <app-shopping-list>) al archivo app.component.html, sustituyendo al <h4>.

    <div class="col">
      <app-recipes></app-recipes>
      <app-shopping-list></app-shopping-list>
    </div>

2. Ahora vamos a trabajar con los componentes individuales de cada bloque (recuerda, nuestra app tiene dos "bloques": la shopping list y el recipe book). Empezaremos por el bloque del recipe book

Como hemos dicho antes, queremos mostrar la recipe-list y la recipe-detail una al lado de la otra. Por ejemplo, una lista de recetas a la izquierda, mostrando paella, lasaña, lentejas, etc, y un bloque a la izquierda que muestre la receta de la paella al hacer click sobre ella en la lista (en el bloque de la izquierda).

recetas

  • paella
  • lasaña
  • lentejas

receta paella

ingredientes
- ...
- ...
- ....
preparación
1.
2.
3.

En el archivo recipes.component.html, nos creamos un esqueleto de filas y columnas con la ayuda de Bootstrap, y colocamos dicho componentes uno al lado del otro. 

<div class="row">
  <div class="col-md-5">
    <app-recipe-list></app-recipe-list>
  </div>
  <div class="col-md-7">
    <app-recipe-detail></app-recipe-detail>
  </div>
</div>

3. Lo siguiente que vamos a hacer es incluir nuestro RecipeItemComponent dentro del recipe-list.component.html. Más adelante incluiremos una verdadera lista de recetas, pero por ahora vamos a limitarnos a mostrar ese componente. 

<app-recipe-item></app-recipe-item>

4. Con respecto al bloque de la shopping list, vamos a hacer algo parecido en cuanto a la distribución de componentes. 

En el component ShoppingList, queremos mostrar una lista de la compra (es decir, una shopping list) debajo del componente que nos servirá para añadir/editar ingredientes (el ShoppingListEdit). 

Le damos también un poco de formato con las clases de Bootstrap.  

<div class="row">
  <div class="col-10">
    <app-shopping-list-edit></app-shopping-list-edit>
    <hr>
    <p>the shopping list of ingredients will go here</p>
  </div>
</div>

Y con esto, ¡ya hemos colocado cada cosa en su sitio! ? ?

Dando forma al HeaderComponent

Vamos a usar un menú de navegación (en inglés, navbar) de los que trae Bootstrap y adaptarlo a nuestras necesidades. 

  • borramos el contenido del header.component.html.
  • en la parte izquierda tendremos una zona a la que posteriormente añadiremos Angular routingque nos llevará a la home page. Aquí es donde irá el título de nuestra app, por ejemplo, Tasty recipes book.
  • en cuanto a los links de nuestro navbar, vamos a añadir dos <ul>
    • una englobará los accesos a nuestros dos bloques (recipe book shopping list)
    • la otra irá a la derecha y contendrá un dropdown menu, sobre el que creamos nuestra propia directive para hacer funcionar el dropdown. Pero por el momento, los links no harán nada.
      • aquí añadiremos opciones (que haremos funcionar más adelante) para poder guardar información en un servidor o solicitarla de un servidor.

Con esta configuración, nuestro archivo header.component.html nos quedaría así:

<nav class="navbar navbar-expand navbar-light bg-light">
  <a class="navbar-brand" href="#">Tasty recipes book</a>
  <div class="collapse navbar-collapse">
    <ul class="navbar-nav mr-auto">
      <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>
    </ul>
    <ul class="navbar-nav">
      <li class="nav-item dropdown">
        <a class="nav-link dropdown-toggle" href="#" role="button" 
           data-toggle="dropdown">Manage</a>
        <div class="dropdown-menu">
          <a class="dropdown-item" href="#">Save data</a>
          <a class="dropdown-item" href="#">Fetch data</a>
        </div>
      </li>
    </ul>
  </div>
</nav>

Si guardas, tu app debería tener este aspecto:

header component

Construcción de nuestro primer modelo: el Recipe model

En nuestro RecipesComponent tenemos los componentes recipe-list recipe-detail. Vamos a trabajar en el componente recipe-list para poder llenarlo de contenido. 

1. Vamos al recipe-list.component.ts y creamos una propiedad vacía llamada recipes, que será un array.

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-recipe-list',
  templateUrl: './recipe-list.component.html',
  styleUrls: ['./recipe-list.component.css']
})
export class RecipeListComponent implements OnInit {

  recipes = [];

  constructor() { }

  ngOnInit() {
  }

}

Ahora vamos a definir las características que debe tener una receta, es decir, un nombre, una imagen, una descripción, etc. Lo normal. Igual que de un coche podríamos hablar de su color, su tamaño, su año de fabricación, etc. ?

Hacemos esto porque vamos a usar ese objeto (una receta) muy a menudo a lo largo de este proyecto, por tanto, es importante que siempre tenga la misma estructura. Para eso sirven los modelos (en inglés, models). ?

Un modelo es sencillamente una clase de JavaScript ES6. Es decir, una especie de plantilla (en inglés, "blueprint") que podemos utilizar de base para crear cuantas recetas necesitemos, creando una instancia (en inglés, "instance") de esa clase.

2. Para crear un modelo, vamos a la carpeta recipes y creamos un archivo llamado recipe.model.tsEl .model no es obligatorio, pero es una convención para poder distinguirlo fácilmente. 

3. Creamos una clase de TS y la llamamos Recipe. Fíjate que no necesitamos añadir ningún decorador tipo @ngModule(), porque con declarar la clase ya es suficiente. Esta clase nos permitirá instanciarla y crear objetos basados en ella (blueprints).

4. Definimos las características (traducidas a propiedades de la clase) que tendrá una receta:

  • un name
  • una description
  • un imagePath ➡ apuntará a una URL

5. Añadimos un accessor delante de las propiedades para especificar que es una clase pública, es decir, susceptible de ser instanciada externamente

6. Añadimos el data type de cada propiedad.

7. Añadimos un constructor dentro de la clase. Eso nos permitirá instanciarla más adelante (en la siguiente sección) usando la keyword "new" y un constructor en una instance de la clase.

export class Recipe {
  public name: string;
  public description: string;
  public imagePath: string;

  constructor() {
      
  }
}

8. Dentro de nuestro constructor asignamos los argumentos a las propiedades.

  constructor(name: string, desc: string, imagePath: string) {
      this.name = name;
      this.description = desc;
      this.imagePath = imagePath;
  }

Y con esto ya tendríamos un modelo de receta listo para usar ? ? ?.

Añadiendo contenido estático al RecipeListComponent

1. Volvemos al archivo recipe-list.component.ts, donde ahora ya podemos definir el data type de nuestro array. Será de tipo Recipe[]refiriéndonos así al modelo que hemos creado. Le añadimos los corchetes porque esa es la forma de indicar que el data type será un array de múltiples "Recipe", igual que si escribiésemos un array de números o de cualquier otro data type, solo que este data type es casero. ?‍?

2. Avisamos  a TS de dónde viene nuestro modelo. Y con esto hecho, ¡podemos por fin usarlo!

3. Vamos a crear una receta estática por ahora. En nuestra propiedad recipes, instanciamos nuestro modelo usando new Recipe() y le pasamos un nombre, una descripción y una URL a la imagen (los que quieras). Con esto, hemos configurado un objeto usando nuestro modelo como blueprint, aunque todavía no podemos ver nada en el navegador. 

import { Component, OnInit } from '@angular/core';
import { Recipe } from '../recipe.model';

@Component({
  selector: 'app-recipe-list',
  templateUrl: './recipe-list.component.html',
  styleUrls: ['./recipe-list.component.css']
})
export class RecipeListComponent implements OnInit {

  recipes: Recipe[] = [
    new Recipe('Recipe title test',
      'Recipe description',
      'https://upload.wikimedia.org/wikipedia/commons/thumb/e/ed/01_Paella_Valenciana_original.jpg/800px-01_Paella_Valenciana_original.jpg')
  ];

  constructor() { }

  ngOnInit() {
  }

}

Para poder ver los resultados en el navegador, tendremos que editar el template del RecipeListComponent, donde por ahora solo tenemos el selector <app-recipe-item>. Más adelante iteraremos ese selector para poder mostrar cada receta dinámicamente. Pero por ahora no vamos a hacer eso porque es una tarea más avanzada. ?

4. Nos creamos un pequeño esqueleto HTML con la ayuda de Bootstrap. Incluimos un botón que posteriormente configuraremos para poder añadir recetas nuevas. Recuerda que lo que estamos construyendo aquí es una muestra de lo que será una receta que aparecerá en una lista junto con otras muchas recetas, así que el diseño que queremos conseguir es que sea un bloque clicable, como una especie de tarjeta. 

<div class="row">
  <div class="col">
    <button class="btn btn-success my-3">New recipe</button>
  </div>
</div>
<div class="row">
  <div class="col">
    <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">Description</p>
        </div>
        <span class="ml-auto my-auto">
          <img src="" alt="" class="img-responsive" style="max-height: 50px;">
        </span>
      </a>
    </div>
  </div>
</div>
<app-recipe-item></app-recipe-item>

Lo que nos daría un resultado como este:

recipe list template

Como ves, el contenido que se muestra es el que hemos incluido en la template de manera estática. Pero nosotros lo que queremos es que el contenido se añada dinámicamente, y para eso tenemos que trabajar sobre los elementos del código que he subrayado en amarillo.

Cómo añadir contenido dinámico a la lista de recetas

Para añadir contenido dinámico vamos a usar un loop ngFor y utilizar la varible local del ngFor para acceder a las propiedades de una receta (name, description, imagePath) mediante string interpolation property binding

Recuerda que la directiva ngFor nos permite repetir código tantas veces como queramos sin tener que escribir dicho código.

    <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>

¡Y ya lo tenemos!

receta con ngFor

Ten en cuenta que para el atributo src podríamos haber usado tanto string interpolation como property binding. Es decir:

[src]="recipe.imagePath"

o

src="{{recipe.imagePath}}"

Como verás, nuestra receta no hace nada si hacemos click sobre ella, pero nos encargaremos de arreglar eso más adelante ?

Para probar la utilidad del ngFor, puedes ir al archivo recipe-list.component.ts y crear otro objeto basado en nuestro Recipe model. Ponle los nombres que quieras, el mío ha quedado así:

  recipes: Recipe[] = [
    new Recipe('Recipe title test',
      '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')
  ];

Y ahora deberías ver ese segundo elemento en tu navegador en el mismo formato que el primero. ? ?

Cómo mostrar los detalles de una receta de manera estática

Vamos a trabajar con el RecipeDetailComponent. Este es el componente que contendrá la información de la receta que seleccionemos de la lista de recetas (todavía estática) que acabamos de construir. Esos detalles de la receta se mostrarán a la derecha de la pantalla, ya que así lo hemos configurado en el componente RecipesComponent, ¿te acuerdas? ?‍?

1. Añadimos un esqueleto HTML con la ayuda de Bootstrap en el archivo recipe-detail.component.html, con una estructura que englobe:

  • la imagen de la receta
  • el nombre de la receta
  • botones para interactuar con la receta (añadiremos un dropdown pero por el momento no funcionará)
  • la descripción completa de la receta
  • los ingredientes de la receta
<div class="row">
  <div class="col">
    <img src="" alt="" 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">
    Description
  </div>
</div>
<div class="row">
  <div class="col">
    Ingredients
  </div>
</div>

Si guardas y vas al navegador, deberías ver algo así:

recipe detail esqueleto

Esa es la representación (a la derecha) de cómo se verá una receta cuando hagamos click en ella en la lista de la izquierda. Pero para poder vincular la información del RecipeListComponent con la del RecipeDetailComponent tenemos que aprender a utilizar la comunicación entre componentes, cosa que haremos más adelante. ?

Trabajando con los componentes del bloque shopping list

 1. Vamos a trabajar con el ShoppingListComponent. En la template del componente tenemos, entre otras cosas, un <p> que dice "the shopping list of ingredients will go here". Vamos a borrar ese <p>, porque ahí es donde irá nuestra verdadera lista de ingredientes a comprar. 

2. En lugar del párrafo, nos creamos un esqueleto tipo unordered list. El único elemento de la lista de momento será un <a>, sin el atributo href para que no recargue la página ni cause comportamientos indeseados. Añadimos también un pequeño inline style para conseguir el efecto clicable de un botón.

<div class="row">
  <div class="col-10">
    <app-shopping-list-edit></app-shopping-list-edit>
    <hr>
    <ul class="list-group">
      <a class="list-group-item" style="cursor: pointer;"></a>
    </ul>
  </div>
</div>

Pero para mostrar algo en la template necesitamos un array de ingredientes. Es el mismo concepto que el array de recipes que nos creamos en el archivo recipe-list.component.ts.

3. Así que en el archivo shopping-list.component.ts nos creamos un array vacío llamado ingredients. 

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-shopping-list',
  templateUrl: './shopping-list.component.html',
  styleUrls: ['./shopping-list.component.css']
})
export class ShoppingListComponent implements OnInit {

  ingredients = [];

  constructor() { }

  ngOnInit() {
  }

}

Nuestro segundo modelo: el Ingredient model

Durante el desarrollo de nuestra app usaremos constantemente elementos relacionados con ingredientes de una receta, así que tiene sentido que nos creemos un modelo de ingrediente para poder reutilizarlo fácilmente. 

En cuanto a nuestro Recipe model, lo hemos creado y guardado dentro de la carpeta del RecipesComponent, porque sólo vamos a usarlo en ese ámbito. De la misma manera, debemos definir un lugar adecuado para guardar nuestro Ingredient model. 

1. El sitio correcto no existe aún, pero lo vamos a crear: será una carpeta compartida dentro de la carpeta raíz (app). La llamaremos "shared" y se llama así porque contendrá elementos que serán usados tanto por el bloque recipe book como por el bloque shopping list. 

Recuerda que el objetivo de un modelo es definir qué características tendrá un cierto elemento (un ingrediente, en este caso). En este contexto, determinaremos un nombre y una cantidad. Al archivo lo llamamos ingredient.model.ts.

export class Ingredient {
  public name: string;
  public amount: number;

  constructor(name: string, amount: number) {
    this.name = name;
    this.amount = amount;
  }
}

2. La de arriba es una configuración tan típica que TS nos ofrece un atajo:

export class Ingredient {
  constructor(public name: string, public amount: number) { }
}

 Aunque ahora declaradas como argumentos del constructor, name amount siguen siendo propiedades del modelo que podremos usar. Eso sí, no olvides añadir la keyword publicque actúa como "función de acceso" (en inglés, accessora las propiedades desde el exterior del archivo.

¡Nuestro modelo está listo! ?

Cómo usar el Ingredient model para mostrar una lista de ingredientes

1. Igual que hicimos con nuestro Recipe model, ahora podemos instanciar un ingrediente o todos los que queremos desde el archivo shopping-list.component.ts. No olvidemos importar el modelo.

import { Ingredient } from '../shared/ingredient.model';

@Component({
  selector: 'app-shopping-list',
  templateUrl: './shopping-list.component.html',
  styleUrls: ['./shopping-list.component.css']
})
export class ShoppingListComponent implements OnInit {

  ingredients: Ingredient[] = [
    new Ingredient('bananas', 3),
    new Ingredient('strawberries', 10)
  ];

2. Vamos a mostrarlo en nuestro navegador. Para eso, nos vamos al archivo shopping-list.component.html y añadimos un ngFor para iterar nuestro array de ingredients y mostrarlos todos. Hacemos esto en la etiqueta <a>. Mediante string interpolation usamos la variable que creamos dentro de ngFor (ingredient) para acceder a las propiedades de nuestro modelo (name amount).

<a class="list-group-item" style="cursor: pointer;" *ngFor="let ingredient of ingredients">
        {{ingredient.name}} ({{ingredient.amount}})
</a>

? Ten en cuenta que los paréntesis del ingredient.amount son parte del contenido del texto, no elementos de lógica de programación. Con esto, guardamos y ¡listo! Ahora deberías ver en tu navegador una lista con los ingredientes que le hemos pasado, que por el momento no hace nada.

lista ingredientes estatica

Cómo añadir una sección para editar nuestra lista de la compra

Vamos a trabajar con nuestro ShoppingEditComponent, que será el componente que gestionará nuestra lista de la compra para poder añadir, editar o borrar ingredientes.

En el archivo shopping-edit.component.html, borramos el contenido por defecto con el que venía y nos creamos nuestro esqueleto

  • añadimos un <form> cuya funcionalidad añadiremos más adelante
  • nuestros <input> serán para el nombre y la cantidad del ingrediente
  • añadimos botones para gestionar una serie de acciones: add, delete, clear. 
<div class="row">
  <div class="col">
    <form>
      <div class="row">
        <div class="col-sm-5 form-group">
          <label for="name">Name</label>
          <input type="text" id="name" class="form-control">
        </div>
        <div class="col-sm-2 form-group">
          <label for="amount">Amount</label>
          <input type="number" id="amount" class="form-control">
        </div>
      </div>
      <div class="row">
        <div class="col">
          <button class="btn btn-success" type="submit">Add</button>
          <button class="btn btn-danger mx-2" type="button">Delete</button>
          <button class="btn btn-info" type="button">Clear</button>
        </div>
      </div>
    </form>
  </div>
</div>

Y ahora en el navegador deberías ver algo así:

shopping list edit estatico

¡Genial! ?

¡BIEN HECHO

Así está nuestra app por el momento. Nada mal, ¿no? Evidentemente tenemos muchas cosas que implementar todavía, pero de eso nos encargaremos en las siguientes entregas.

Entre las mejoras que vamos a añadir en el futuro, están:

  • mostrar las recetas y la lista de la compra en páginas diferentes
  • establecer una verdadera comunicación entre componentes

?¡No te pierdas la continuación de esta serie para ver cómo implementarlas! ?️‍♀️ Puedes apuntarte gratis a la newsletter para estar al tanto de todas las novedades del blog.

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 ?.

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í. 

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