Angular Ng-content and Content Projection: A Complete Guide – How To Use ng-Content To Improve Component API Design

The mockup implementation looks like this:

First try: Assuming we’ve created a custom Component whose selector is fa-input, let’s see how to use it:

@Component({
  selector: 'app-root'.template: ` 

FA Input

`
}) export class AppComponent { onNewValue(val) { console.log(val); }}Copy the code

Custom control FA-input implementation code:

@Component({
  selector: 'fa-input'.template: `   `.styleUrls: ['./fa-input.component.css']})export class FaInputComponent {
  @Input() icon: string;
  @Output() value = new EventEmitter<string> (); inputFocus =false;

  get classes() {
    const cssClasses = {
      fa: true
    };
    cssClasses['fa-' + this.icon] = true;
    return cssClasses;
  }

  @HostBinding('class.focus')
  get focus() {
    console.log(this.inputFocus);
    return this.inputFocus; }}Copy the code

CSS:

:host {
  border: 1px solid grey;
}

input {
  border: none;
  outline: none;
}

:host(.focus) {
  border: 1px solid blue;
}
Copy the code

(1) The icon attribute determines the appearance of the icon to be displayed. The consumer of this custom control needs to be passed in. (2) the component has a custom output event named value, that emits new values whenever the value of the text input changes. A custom event generator named value that notifies the control consumer of the new value when the text input changes. (3) Detect focus and blur events of the native HTML input field and set the corresponding flag bit inputFocus. Then bind inputFocus to the focus CSS style of the host element via @hostBinding.

There are some problems with the current design:

(1) Some attributes of the HTML native input field are not considered, such as:

<input type="email"  autocomplete="off" placeholder='Email'>
Copy the code

To fix this, you need to manually migrate these attributes to the custom Component one by one, but the template of the custom Component will swell considerably:


@Component({
  selector: 'fa-input'.template: `   `
})
export class FaInputComponent {...@Input() placeholder:string;
    @Input(a)type:string;
    @Input() autocomplete:string; . }Copy the code

The problem with this design is that consumers using custom controls are completely unaware of the input field containing an element behind the custom control, and thus lack an elegant mechanism to pass in the custom Component even if they want to use some of the attributes of the native Input field.

Let’s look at how the improved version of the custom Component is consumed:

@Component({
  selector: 'app-root'.template: ` 

FA Input

`
}) export class AppComponent {}Copy the code

One obvious difference is the presence of native input controls.

The new version uses ng-content implementation:

@Component({
  selector: 'fa-input'.template: `  
       `.styleUrls: ['./fa-input.component.css']})export class FaInputComponent {

  @Input() icon: string;

  get classes() {
    const cssClasses = {
      fa: true
    };
    cssClasses['fa-' + this.icon] = true;
    returncssClasses; }}Copy the code

More of Jerry’s original articles can be found in “Wang Zixi” :