Skip to content

Style guide

Callum Smith edited this page Apr 16, 2019 · 2 revisions

Style guide

Formatting

Template formatting

Component templates should always be inline as template strings (backticks: ``). Each tag attribute should be given a newline with no other attributes on that line, and all attributes for a given tag should be indented to the next indentation level. Attributes may be on the same line as the tag up to a maximum of two attributes. The closing angle bracket should follow the last attribute.

Example:

@Component(
    template: `
        <div
            class="my class"
            (click)="onClick($event)"
            [attr.aria-label]="this is a div">
            <span>This is some content</span>
            <ng-content selector=".other-content"></ng-content>
        </div>`
)

Ordering

Import ordering

Generally imports should be given one line per entry, unless there are 3 or fewer entries. Also prefer importing packages from @angular or third party packages before any and all other imports.

Example:

import {
    Component,
    Input,
    Output,
    EventEmitter,
    ElementRef,
    ViewChild,
    OnInit,
    AfterViewInit,
    OnDestroy,
    HostListener
} from "@angular/core";
import { foo, bar } from "foo-things";

Class ordering

Generally a class should feature properties and methods in this order:

  • all static properties
  • all static methods
  • Inputs
  • Outputs
  • public properties
  • protected properties
  • private properties (prefer protected to private in general)
  • constructor (including injected services)
  • ng* methods (ngOnInit, ngOnChanges, etc)
  • public methods
  • protected methods
  • private methods

Example:

class Component implements OnInit {
    protected static aStaticProp = "static";
    @Input() input: number;
    @Output() output: EventEmitter<string> = new EventEmitter<string>();
    public forUseInTemplates = "angular things";
    protected canBeSubclassed = true;
    private cannotBeSubclassed = true;
    
    constructor(
        protected elementRef: ElementRef
    ){}

    ngOnInit() {
        // do things here when we initilize
    }
    
    public canBeUsedInTemplates(): nubmer {
        return 42;
    }

    protected calculateSomething(): void {
        console.log(2+2);
    }

    private secretHelper(): boolean {
        return this.cannotBeSubclassed?"yes":"no";
    }
}

Naming

Component selector and class naming

The selector should follow the form ibm-foo. The prefix ibm is mandatory to prevent name collisions. The class name should simply be Foo, never IBMFoo or similar. Unless there is a name collision the component class should simply be Foo only use FooComponent if there is no alternative. The file the component lives in should always follow the format of foo.component.ts

Components may declare as many internal sub-components as necessary, however as a whole, the component must have one public facing declaration that follows these rules.

Example: (found in src/dropdown/dropdown.component.ts)

@Component({
    selector: "ibm-dropdown",
    // rest of component directive options omitted for space
})
export class Dropdown {
    // implementation omitted for space
}

Event handler and emitter handler naming

Event handlers should general follow the form onEventName, and take the event as it's first argument if needed.

Emitters should have an associated method that fires the event. This allows us to change how the event is created and dispatched internally without having to modify all potential call sites. It also becomes part of our public API surface for the component, allowing external components to fire events in a safe manner, and for descendant components to easily override and/or fire a parents event.

The @Output declaration should rely on Typescripts type inference, and a specific type argument should always be provided to the EventEmitter constructor.

Example:

@Output myEmitter = new EventEmitter<any>();

onClick(event) {
    console.log(event.target);
}

doMyEmitter() {
   this.myEmitter.emit();
}

Private variable and method naming

Only use underscore prefixes for declarations resolving a naming conflict with another declaration of any type. If there is a conflict, the getter/setter should not be using an underscore.