Advanced Patterns for Angular Components

- Hayden Braxton

 

Why Patterns?

A few notes ...

Inspiration?

Alternative solutions?

Overview

Overview

- Composition vs Inheritance

- Compound Components

- Semantic Inputs and Template Inputs

- Render Prop

- State Reducer

Composition vs Inheritance

Code Smell 👃💩



@Component({
    selector: 'this-is-painful',
    template: `
    <h3>too bad I don't inherit any template or styles from my parent</h3>
    <p>
        something from parent {{parentProperty}}.
        It's really clear where this data came from 🙃
    </p>
    <button (click)="parentHandler()">redundant plumbing</button>
    `
})
export class InheritanceComponent extends ParentComponent {
    constructor(
        private myDependency: CoolService,
        private anotherDep: AnotherService,
        private parentDep: DontKnowWhyService,
        private anotherForTheParent: SomeService
    ) {
        // hope i got the access modifiers right ...
        // hope I got these in the right order ...
        // I'm sure this is scalable ¯\_(ツ)_/¯
        super(parentDep, anotherForTheParent);
    }
}

Compound Compomnents

Code Smell 👃💩



@Component({
    selector: 'list-page',
    template: `
    <h1>List of Cool Things</h1>
    <cool-list-item
        *ngFor="let item of list; let i = $index;"
        [selected]="isSelected(i)"
        (click)="selectItem(i)"
        [item]="item"
    ></cool-list-item>
    `
})
export class MaybeShouldBeCompoundComponent {
    private selectedIndex: number;
    list = [...]; // or maybe I got this from a service
    isSelected = (index: number) => index === this.selectedIndex;
    selectItem = (index: number) => this.selectedIndex = index;
}

Semantic Inputs and Template Inputs

Code Smell 👃💩



@Component({
    selector: 'this-is-fine',
    template: `
    <h3>innerHtml all the things!</h3>
    <!-- too bad my styles dont apply to the innerHtml result -->
    <div
        class="message"
        [innerHtml]="someRichText"
    ></div>
    `,
    styles: [`.message p { color: orange }`]
})
export class RichTextComponent {
    @Input() someRichText = `
        <h1>default title</h1>
        <p>default paragraph that won't be orange</p>
    `;
}

Render Prop

Code Smell 👃💩



@Component({
    selector: 'one-size-fits-all',
    template: `
    <div [ngClass]="getClass()">
        <div
            *ngIf="configSwitch && configOption === 'option1'"
            (click)="doTheThing()"
        >display like this</div>
        <div
            *ngIf="configSwitch && configOption === 'option2'"
            (click)="doTheThing()"
        >display it different</div>
        <div
            *ngIf="!configSwitch"
            (click)="doTheThing()"
        >display another thing</div>
    </div>
    `,
    styles: [`.message p { color: orange }`]
})
export class DisplayManyCasesComponent {
    @Input() configOption: string;
    @Input() configSwitch: boolean;
    getClass = () => ({ [this.configOption]: true });
    doTheThing = () => {...}
}

State Reducer

Code Smell 👃💩



@Component({
    selector: 'doing-too-many-things',
    template: `<button (click)="doTheThing">Do the thing</button>`,
})
export class DoingTooManyThingsComponent {
    @Input() configOption: string;
    @Input() configSwitch: boolean;

    doTheThing = () => {
        if (configSwitch && configOption === 'option1') {
            // do it like this
        } else if (configSwitch && configOption === 'option2') {
            // do it like that
        } else if (!configSwitch) {
            // handle a different case
        } else {
            // this is very scalable ¯\_(ツ)_/¯
        }
    }
}

Thanks!

Resources

Advanced Patterns for Angular Components

By haydenbraxton

Advanced Patterns for Angular Components

Getting started with Angular components is easy enough, but sometimes we need more than simple inputs and outputs to satisfy complex scenarios. Perhaps your component requires complicated local state management? Maybe two of your components need to play nicely together, yet maintain an appropriate level of separation? How can we efficiently reuse code between multiple components that require the same behavior but display different templates? In this talk, we’ll leverage Angular features like form controls, templates, and queries to build components with flexible, intuitive APIs and explore a couple of code smells that signal a need for better patterns.

  • 772