DEV Community

Cover image for Nested components o componentes anidados en Angular con ng-content
Henry Bravo
Henry Bravo

Posted on

Nested components o componentes anidados en Angular con ng-content

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

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
Enter fullscreen mode Exit fullscreen mode

Y para darle un poco de estilo como del diseño, nuestro style.css el app.component.css quedan de la siguiente manera

/* You can add global styles to this file, and also import other style files */
body {
  margin: 0;
  font-family: system-ui;
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

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 { }
Enter fullscreen mode Exit fullscreen mode

Empezamos a crear nuestros componentes

Agregamos los estilos correspondientes a cada Layout y creamos el html

card.component.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);
}
Enter fullscreen mode Exit fullscreen mode

.card-component.html


<div class="card">
  <ng-content></ng-content>
</div>
Enter fullscreen mode Exit fullscreen mode

.card-component.css

.header {
  border-bottom: 1px solid #d0d0d0;
  padding: 14px;
  font-size: 1rem;
  font-weight: 700;
}
Enter fullscreen mode Exit fullscreen mode

.card-header.component.html

<div class="header">
  <ng-content></ng-content>
</div>
Enter fullscreen mode Exit fullscreen mode

.card-body.component.css

.card-body {
  padding: 1rem;
}
Enter fullscreen mode Exit fullscreen mode

.card-body.component.html

<div class="card-body">
  <ng-content></ng-content>
</div>
Enter fullscreen mode Exit fullscreen mode

Antes de escribir el primer card, modifiquemos el componente item-post

.item-post.component.ts

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 {}
}
Enter fullscreen mode Exit fullscreen mode

.item-post.component.css

.item-post {
  border: 1px solid gray;
  padding: 0.5rem;
  font-size: 1rem;
  font-weight: 700;
  margin-bottom: 1rem;
}
Enter fullscreen mode Exit fullscreen mode

.item-post.component.html

<div class="item-post">
  {{ value }}
</div>
Enter fullscreen mode Exit fullscreen mode

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

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'];
}
Enter fullscreen mode Exit fullscreen mode

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

<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>
Enter fullscreen mode Exit fullscreen mode

Ahora maquetamos nuestro contact-form

.contact-form.component.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>
Enter fullscreen mode Exit fullscreen mode
.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;
}
Enter fullscreen mode Exit fullscreen mode

Y nuestro app.component.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>
Enter fullscreen mode Exit fullscreen mode

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

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

Top comments (0)