Nested components o componentes anidados en Angular con ng-content
Published: 2020-10-15 23:44:19


Un caso muy común para hacer un ejemplo de componentes anidados y usando ng-content, son los Cards, tal cual lo hace Angular Material, en este caso se nos presenta un diseño similar al siguiente

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/cah8xc6o6pwc4nxtcx26.png)

Una buena practica que debemos tener como desarrolladores y que a mi me funciona es definir la arquitectura antes de "echar" codigo.

Luego de analizar el diseño anterior, nos encontramos que tenemos un Home, con dos Cards, que si uno quisiera podrían ser dos componentes separados compartiendo estilos, pero al final se puede cruzar la línea delgada de reescribir código. 

En este caso crearemos un componente Card, que sera nuestro Layout del card, un componente CardHeader, y un componente CardBody, ya que vemos que si tiene Header es el mismo comportamiento, y con nuestro amigo "ng-content" recibiremos los componentes o data que le pasemos al Card. 

Para este ejercicio vamos a representar visualmente las cosas, no va a ser necesario comportamientos del form o de las urls de los posts, eso es solo demostrativo

Iniciamos el proyecto
```
ng new MyNestedComponents
```
Y para darle un poco de estilo como del diseño, nuestro style.css el app.component.css quedan de la siguiente manera
```css
/* You can add global styles to this file, and also import other style files */
body {
  margin: 0;
  font-family: system-ui;
}
```
Ahora creamos los componentes a utilizar:
```
ng g c components/card
ng g c components/card-header
ng g c components/card-body
ng g c components/contact-form
ng g c components/item-post
```

Nuestro app.module.ts debe quedar de la siguiente manera:


```typescript
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { CardComponent } from './components/card/card.component';
import { CardHeaderComponent } from './components/card-header/card-header.component';
import { ContactFormComponent } from './components/contact-form/contact-form.component';
import { ItemPostComponent } from './components/item-post/item-post.component';
import { CardBodyComponent } from './components/card-body/card-body.component';

@NgModule({
  declarations: [
    AppComponent,
    CardComponent,
    CardHeaderComponent,
    ContactFormComponent,
    ItemPostComponent,
    CardBodyComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
```
Empezamos a crear nuestros componentes

Agregamos los estilos correspondientes a cada Layout y creamos el html

card.component.css
```css
.card {
  background-color: white;
  box-shadow: 0 2px 1px -1px rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14),
    0 1px 3px 0 rgba(0, 0, 0, 0.12);
}
```

.card-component.html
```html

<div class="card">
  <ng-content></ng-content>
</div>
```

.card-component.css
```css
.header {
  border-bottom: 1px solid #d0d0d0;
  padding: 14px;
  font-size: 1rem;
  font-weight: 700;
}
```
.card-header.component.html
```html
<div class="header">
  <ng-content></ng-content>
</div>
```
.card-body.component.css

```css
.card-body {
  padding: 1rem;
}
```

.card-body.component.html

```html
<div class="card-body">
  <ng-content></ng-content>
</div>
```
Antes de escribir el primer card, modifiquemos el componente item-post

.item-post.component.ts

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

@Component({
  selector: 'app-item-post',
  templateUrl: './item-post.component.html',
  styleUrls: ['./item-post.component.css'],
})
export class ItemPostComponent implements OnInit {
  @Input() value: string;
  constructor() {}

  ngOnInit(): void {}
}
```

.item-post.component.css

```css
.item-post {
  border: 1px solid gray;
  padding: 0.5rem;
  font-size: 1rem;
  font-weight: 700;
  margin-bottom: 1rem;
}
```

.item-post.component.html

```html
<div class="item-post">
  {{ value }}
</div>
```

En el app.component.ts listamos los "posts"

```tsx
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  title = 'AngularNestedComponents';

  posts: string[] = ['Primer post', 'Segundo Post', 'Tercer post'];
}
```

Y nuestro primer Card quedaría de la siguiente manera:

```html
<app-card>
  <app-card-header> Mis Posts </app-card-header>
  <app-card-body>
    <ng-container *ngFor="let item of posts">
      <app-item-post [value]="item"></app-item-post>
    </ng-container>
  </app-card-body>
</app-card>
```

Ahora maquetamos nuestro contact-form

.contact-form.component.html

```html
<div class="content-form">
  <div class="form-group">
    <label>Nombre</label>
    <input type="text" class="form-control" />
  </div>

  <div class="form-group">
    <label>Email</label>
    <input type="email" class="form-control" />
  </div>

  <div class="form-group">
    <label>Mensaje</label>
    <textarea name="" id="" cols="30" rows="10"></textarea>
  </div>

  <div class="cta">
    <button>Enviar</button>
  </div>
</div>
```

```css
.content-form {
  padding: 14px;
  margin: 0 auto;
  max-width: 440px;
  width: 90%;
}

.content-form .form-group {
  margin-bottom: 10px;
}
.content-form .form-group label {
  font-weight: 700;
  display: block;
}
.content-form .form-group input {
  padding: 6px;
  border: 1px solid #b7b7b7;
  border-radius: 4px;
  margin-top: 7px;
  width: 100%;
}

.content-form .form-group textarea {
  padding: 6px;
  border: 1px solid #b7b7b7;
  border-radius: 4px;
  margin-top: 7px;
  width: 100%;
}

button {
  background-color: #00d5ff;
  border: 0;
  padding: 10px;
  border-radius: 5px;
  width: 170px;
}
```

Y nuestro app.component.html

```html
<main>
  <div class="container">
    <h1>HenryGBCDev</h1>

    <app-card>
      <app-card-header> Mis Posts </app-card-header>
      <app-card-body>
        <ng-container *ngFor="let item of posts">
          <app-item-post [value]="item"></app-item-post>
        </ng-container>
      </app-card-body>
    </app-card>

    <app-card>
      <app-card-header> Contactanme </app-card-header>
      <app-card-body>
        <app-contact-form></app-contact-form>
      </app-card-body>
    </app-card>
  </div>
</main>
```

Podemos ver los beneficios del `ng-content` y que también nos ayuda a leer mucho mejor el código, entre mas componetizado este nuestra aplicación, aparte de que se ve mucho mejor, nos ayuda con el mantenimiento a los desarrolladores. 

[https://github.com/HenryGBC/AngularNestedComponents](https://github.com/HenryGBC/AngularNestedComponents)

Sígueme en mis redes, donde sigo compartiendo contenido de código y sobre todo de Frontend.