Memproyeksikan konten dalam dokumentasi angular atau hilang-konten

Ketika belajar Angular, sangat sering mereka kehilangan atau tidak cukup memperhatikan konsep seperti proyeksi konten. Ini adalah alat yang sangat kuat untuk membuat komponen yang fleksibel dan dapat digunakan kembali. Tetapi dokumentasinya hanya menyebutkan beberapa paragraf di bagian Siklus Hidup kait . Mari kita coba perbaiki kelalaian ini.



Memproyeksikan konten menggunakan konten ng


Proyeksi konten adalah cara untuk mengimpor konten HTML dari luar komponen dan menempelkannya ke templat komponen di lokasi tertentu. (terjemahan dokumentasi gratis)
Definisi ini cukup rumit, tetapi pada kenyataannya, semuanya jauh lebih sederhana. Kami memiliki beberapa jenis komponen, dan semua yang ada di antara tag pembuka dan penutupnya adalah konten.

<app-parent>
    <!-- content -->
    I'm content!
    <!-- content -->
</app-parent>

Dan Angular memungkinkan Anda untuk menanamkan kode HTML (konten) apa pun ke templat komponen ini menggunakan elemen ng-content.

Mari kita coba mencari tahu mengapa ini diperlukan dan bagaimana cara kerjanya dengan sebuah contoh. Katakanlah kita memiliki komponen tombol sederhana. Teks dari tombol ini kami berikan kepada templat input property.

// button.component.ts
import {Component, Input} from '@angular/core';

@Component({
  selector: 'app-button',
  template: '<button>{{text}}</button>'
})
export class ButtonComponent {
  @Input() text: string;
}

// app.component.ts
import {Component} from '@angular/core';

@Component({
  selector: 'app-root',
  template: `<app-button [text]="'Button'"></app-button>`,
})
export class AppComponent {
}

Sepertinya terlihat bagus. Tapi tiba-tiba kami perlu menambahkan ikon ke teks untuk beberapa tombol. Kami sudah memiliki komponen ikon. Anda hanya perlu menambahkannya ke templat tombol, melampirkan arahan ngIfdan menulis yang lain input propertyuntuk tampilan ikon yang dinamis.

// icon.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-icon',
  template: '☻',
})
export class IconComponent {
}

// button.component.ts
import {Component, Input} from '@angular/core';

@Component({
  selector: 'app-button',
  template: `<button>
               <app-icon *ngIf="showIcon"></app-icon>
               {{text}}
             </button>`,
})
export class ButtonComponent {
  @Input() text: string;
  @Input() showIcon = true;
}

Semuanya berfungsi. Tetapi apa yang terjadi jika Anda perlu mengubah lokasi ikon relatif terhadap teks? Atau menambah item baru? Anda harus mengedit kode yang ada, menambah properti baru, dll.

Semua ini bisa dihindari dengan ng-content. Itu dapat dianggap sebagai pengganti untuk konten. Ini menampilkan semua yang Anda tempatkan di antara tag pembuka dan penutup komponen.

// button.component.ts
import {Component} from '@angular/core';

@Component({
  selector: 'app-button',
  template: `<button>
               <ng-content></ng-content>
             </button>`,
})
export class ButtonComponent {
}

// app.component.ts
import {Component} from '@angular/core';

@Component({
  selector: 'app-root',
  template: `<app-button>
               <app-icon></app-icon>
               Button
             </app-button>`,
})
export class AppComponent {
}

Kode Stackblitz

Sekarang, jika kita membutuhkan tombol dengan ikon, kita cukup menempatkan komponen ikon di antara tag tombol. Anda bisa menambahkan apa saja dan apa saja. Bukankah itu surga? Komponen tombol kami telah menjadi fleksibel dan indah.

Apa peran yang dimainkan atribut pilih untuk konten-ng?


Terkadang kita perlu mengatur beberapa konten di tempat tertentu relatif terhadap konten lainnya, dalam hal ini kita dapat menggunakan atribut selectyang menerima pemilih ( .some-class, some-tag, [some-attr]).

// button.component.ts
import {Component} from '@angular/core';

@Component({
  selector: 'app-button',
  template: `<button>
               <ng-content></ng-content>
               <div>
                 <ng-content select="app-icon"></ng-content>
               </div>
             </button>`,
})
export class ButtonComponent {
}

Kode Stackblitz

Sekarang ikon selalu ditampilkan di bawah, terlepas dari konten yang tersisa. Perfecto!

Apa itu ngProjectAs?


Atribut selecty ng-contentmengatasi tag yang berada di tingkat pertama dari komponen induk. Tetapi apa yang terjadi jika kita meningkatkan level bersarang untuk komponen ikon dengan membungkusnya dalam tag?

// app.component.ts
import {Component} from '@angular/core';

@Component({
  selector: 'app-root',
  template: `<app-button>
               <ng-container>
                 <app-icon></app-icon>
               </ng-container>
               Button
             </app-button>`
})
export class AppComponent {}

Kita akan melihat bahwa itu selecttidak berfungsi, seolah-olah itu tidak ada sama sekali. Ini terjadi karena <ng-content select="...">pencarian hanya pada tingkat pertama dari konten induk. Ada atribut untuk menyelesaikan masalah ini ngProjectAs. Dibutuhkan pemilih dan "topeng" seluruh simpul DOM di bawahnya.

// app.component.ts
import {Component} from '@angular/core';

@Component({
  selector: 'app-root',
  template: `<app-button>
               <ng-container ngProjectAs="app-icon">
                 <app-icon></app-icon>
               </ng-container>
               Button
             </app-button>`
})
export class AppComponent {}

Kode Stackblitz

Kasus * ngIf + konten-ng


Mari kita periksa kasus menarik lainnya. Misalkan kita perlu mengklik tombol hide / show icon. Tambahkan properti boolean ke kelas komponen tombol yang bertanggung jawab untuk menampilkan ikon, ubah dengan mengklik tombol dan gantung ngIf.

// button.component.ts
import {Component, Input} from '@angular/core';

@Component({
  selector: 'app-button',
  template: `<button (click)="toggleIcon()">
               <ng-content></ng-content>
               <div *ngIf="showIcon">
                 <ng-content select="app-icon"></ng-content>
               </div>
             </button>`,
})
export class ButtonComponent {
  showIcon = true;

  toggleIcon() {
    this.showIcon = !this.showIcon;
  }
}

Ikon disembunyikan / muncul dengan klik. Baik! Tapi mari kita tambahkan beberapa log untuk pengait OnInitdan OnDestroyuntuk komponen ikon. Ini adalah fakta yang diketahui bahwa arahan, ngIfketika suatu kondisi diubah, sepenuhnya menghilangkan / menciptakan elemen, dan OnDestroy/ OnInitharus bekerja sesuai setiap kali.

// icon.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';

@Component({
  selector: 'app-icon',
  template: '☻',
})
export class IconComponent implements OnInit, OnDestroy {
  ngOnInit() {
    console.log('app-icon init');
  }

  ngOnDestroy() {
    console.log('app-icon destroy')
  }
}

Kode pada Stackblitz

Beberapa kali kita mengklik tombol, pastikan ikonnya menghilang, dan kemudian muncul. Lalu kita pergi ke konsol pengembang dengan harapan melihat log yang diidam-idamkan kita, namun ... tidak!

Hanya ada satu log untuk membuat komponen. Ternyata komponen ikon kami tidak pernah dihapus, tetapi hanya disembunyikan. Mengapa ini terjadi?

ng-content tidak membuat konten baru, itu hanya memproyeksikan yang sudah ada. Oleh karena itu, komponen di mana konten dinyatakan bertanggung jawab untuk membuat dan menghapus. Bagi saya itu adalah momen yang sama sekali tidak jelas. Mari kita perbaiki solusi kami sehingga berfungsi seperti yang diharapkan pada awalnya.

// button.component.ts
import {Component} from '@angular/core';

@Component({
  selector: 'app-button',
  template: `<button>
                <ng-content></ng-content>
                <ng-content select="app-icon"></ng-content>
             </button>`,
})
export class ButtonComponent {
}

// app.component.ts
import { Component } from "@angular/core";

@Component({
  selector: 'app-root',
  template: `<app-button (click)="toggleIcon()">
              <div *ngIf="showIcon" ngProjectAs="app-icon">
                <app-icon></app-icon>
              </div>
              Button
            </app-button>`,
})
export class AppComponent {
  showIcon = true;

  toggleIcon() {
    this.showIcon = !this.showIcon;
  }
}

Kode pada Stackblitz

Setelah membuka log, kita dapat melihat bahwa komponen ikon dibuat dan dihapus sebagaimana mestinya.

Alih-alih sebuah kesimpulan


Saya harap artikel ini sedikit membantu Anda dalam memproyeksikan konten di Angular.
Bagi saya sangat tidak masuk akal mengapa dokumentasi resmi mengabaikan topik ini. Repositori Angular bahkan menggantung masalah ini sejak 2017. Tampaknya, tim Angular memiliki hal-hal yang lebih penting untuk dilakukan.

All Articles