-
Notifications
You must be signed in to change notification settings - Fork 308
Style guide
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>`
)
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";
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";
}
}
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 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();
}
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.