Today I Learned

TIL, 2022-09-01, Components

Parse, don’t validate, incoming data in TypeScript.

Reference

  • Runtypes, Zod, and io-ts are all runtime type validators that can parse your data into a specific type (or fail).
  • The type is the schema and is also the parser. We raise errors if the data was wrong, and we also have a TS type created.
  • The “parse, don’t validate” mantra is all about parsing incoming data to a specific type, or failing in a controlled manner if the parsing fails. It’s about using trusted, safe and typed data structures inside your code and making sure all incoming data is handled at the very edges of your programs. Don’t pass incoming data deep into your code, parse it right away and fail fast if needed.

Here’s what you should know when creating flexible and reusable components in Angular

Reference

  • The base idea of reusable and flexible components is simple. You have a reusable host component. This component contains some standard behaviour and template which is always the same.
  • Defining slots - these are where the flexible content will end up.
  • Modern browsers support slot and template
<template id="foo">
 <style>
  h4 { color: blue }
 </style>
 <h4><slot name="title"></slot></h4>
</template>

Angular

  • ng-template has the ability to hold content without rendering it. It’s the mechanism to ngIf and ngFor.
  • Most basic example:
<ng-template #theTruth>
  <h4>Real Madrid - best football club ever</h4>
</ng-template>

<heading [title]="theTruth"> </heading>

<ng-container *ngTemplateOutlet="title"></ng-container>
  • ng-content is like slot - allows us to specify spots in the host components. Those spots define where the dynamic content will end up.
  • When projecting content via ng-content, the lifecycle hooks are bound to the life cycle of the parent component.
    • ngOnInit is called once the host is rendered
    • ngOnDestroy is called once the host is destroyed
    • Removing and rendering the projected content via ngIf doesn’t call the life cycles.
    • This can cause issues with ngOnDestroy if the streams are never properly unsubscribed.
  • ng-template - is way less readable than ng-content.
  • Mimic ng-content with ng-template - done with ContentChild.
<expander>
  <ng-template>
    <clock></clock>
  </ng-template>
</expander>

Difference between ElementRef and TemplateRef

Reference

  • ElementRef refers to an element of the DOM. Reference Contains nativeElement.
    • This is simply like document.getElemenetById("myId").
  • TemplateRef represents an embedded template. Reference Can be used to instantiate embedded views.
    • You can use in ViewContainerRef.createEmbeddedView.

Exploring Angular DOM manipulation techniques using ViewContainerRef

Reference

  • ViewChild and ViewChildren: They behave the same, and are paired with template reference variables.
Copy
@Component({
    selector: 'sample',
    template: `
        <span #tref>I am span</span>
    `
})
export class SampleComponent implements AfterViewInit {
    @ViewChild("tref", {read: ElementRef}) tref: ElementRef;

    ngAfterViewInit(): void {
        // outputs `I am span`
        console.log(this.tref.nativeElement.textContent);
    }
}
  • Parameter read is not always required, since Angular can infer the reference type by the type of the DOM element.
  • ElementRef - Just get the native element it’s associated with.
    • Angular doesn’t encourage this because I think it doesn’t run through change detection.
    • ViewChild is used to get a reference to get a DOM element in its view (template).
  • TemplateRef.
    • Templates work like this: The framework removes the template element and inserts a comment in its place.
@Component({
    selector: 'sample',
    template: `
        <ng-template #tpl>
            <span>I am span in template</span>
        </ng-template>
    `
})
export class SampleComponent implements AfterViewInit {
    @ViewChild("tpl") tpl: TemplateRef<any>;

    ngAfterViewInit() {
        let elementRef = this.tpl.elementRef;
        // outputs `template bindings={}`
        console.log(elementRef.nativeElement.textContent);
    }
}
  • It only has one method: createEmbeddedView.
  • ViewRef:
    • An Angular view is the smallest grouping of elements which are created and destroyed together. Angular philosophy encourages developers to see the UI as a composition of Views, not as a tree of standalone HTML tags.
    • Creating a host view: This is when a component gets instantiated.
constructor(private injector: Injector,
            private r: ComponentFactoryResolver) {
    let factory = this.r.resolveComponentFactory(ColorComponent);
    let componentRef = factory.create(injector);
    let view = componentRef.hostView;
}
  • ViewContainerRef
    • A container where one or more views can be attached.
    • Angular doesn’t insert views inside the element, but appends them to the element bound to ViewContainer.
@Component({
    selector: 'sample',
    template: `
        <span>I am first span</span>
        <ng-container #vc></ng-container>
        <span>I am last span</span>
    `
})
export class SampleComponent implements AfterViewInit {
    @ViewChild("vc", {read: ViewContainerRef}) vc: ViewContainerRef;

    ngAfterViewInit(): void {
        // outputs `template bindings={}`
        console.log(this.vc.element.nativeElement.textContent);
    }
}
  • The API for creating views:
class ViewContainerRef {
    ...
    clear() : void
    insert(viewRef: ViewRef, index?: number) : ViewRef
    get(index: number) : ViewRef
    indexOf(viewRef: ViewRef) : number
    detach(index?: number) : ViewRef
    move(viewRef: ViewRef, currentIndex: number) : ViewRef
}
  • ngTemplateOutlet: makes a DOM element as a ViewContainer and inserts an embedded view created by a template.
  • ngComponentOutlet creates a host view.

Understanding ViewContainerRef in Angular 2

Reference

  • A DOM element (container) where I can put your newly component as a sibling to this element.
@Component({
  selector: 'vcr',
  template: `
    <template #tpl>
      <h1>ViewContainerRef</h1>
    </template>
  `,
})
export class VcrComponent {
  @ViewChild('tpl') tpl;
  constructor(private _vcr: ViewContainerRef) {
  }

  ngAfterViewInit() {
    this._vcr.createEmbeddedView(this.tpl);
  }
}

@Component({
  selector: 'my-app',
  template: `
      <vcr></vcr>
  `,
})
export class App {
}

This project is maintained by daryllxd