In order to contribute to this project, please follow the below code formatting guidelines.
For the project policy regarding testing, please refer to the following documentation.
On an element inside your template code, we recommend you keep to the following sorting of attributes:
- Structural directives like
*ngIf
and*ngFor
- Angular directives such as
ngStyle
andngClass
- Inputs sorted alphabetically
- Outputs sorted alphabetically
- Other attributes like
class
,tabIndex
etc. - sorted alphabetically
If two elements are on the same level in the DOM hierarchy, put whitespace between these elements, in order to improve readability.
<div>
<p>test</p>
<!-- whitespace -->
<p>test 2</p>
</div>
Inject services into components with private readonly
:
...
constructor(private readonly testService: TestService) {
}
...
All properties and methods in a class should be marked with access modifiers.
Any properties not used outside of a component, should be marked as private. The property name
should be prefixed with an underscore: private _testProperty: string;
.
Any properties with a constant reference, should be marked as readonly. For example:
public readonly testObservable$ = new BehaviourSubject<string>('')
.
All constant properties should be written in screaming snake case:
public readonly MY_VALUE = 'my value';
Unless initialized with a primitive or an explicit constructor, all properties and methods should have a return type specified:
...
public myProperty!: TestObject;
private _myString = 'test';
public myVoidFunction(testParams: TestParam): void {
...
}
private myTestFunction(testParams: TestParam): TestResult {
let testResult!: TestResult;
...
return testresult
}
...
As much possible, we recommend you keep to the following ordering of properties in Angular classes:
- Property decorators like
ViewChild
,ViewChildren
etc. Input()
Output()
- Public properties
- Public readonly properties
- Private properties
- Private readonly property
- Getters/setters
- Constructor
- Angular lifecycle hooks
- ngOnInit
- ngAfterViewInit
- ngOnChanges
- ngOnDestroy
- Public methods
- Private methods
Properties and methods belonging to a certain logical domain should be bundled together within a class. The bundle itself is preferably sorted alphabetically. This is a recommended way of organizing a class, and can be deviated from if another way is more practical.
We recommend that logical grouping of methods takes precedence over grouping by access modifiers, since it is more likely that a developer will want to have easy access to methods relating to a certain domain, rather than methods with the same access modifier. Sorting by access modifier takes precedence over optional alphabetical sorting.
...
/*
Methods relating to title are bundled together and sorted alphabetically.
This takes precedence over sorting by access modifiers.
The grouping by access modifier in turn takes precedence over alphabetical sorting.
*/
public addTitle(title: string): void {
...
}
public removeTitle(title: string): void {
...
}
private compareTitle(title: string): void {
...
}
// Methods relating to description are bundled together and sorted alphabetically
private addDescription(description: string): void {
...
}
private compareDescription(description: string): void {
...
}
private removeDescription(description: string): void {
...
}
...
Suffix event emitters with Event
and write them in camelCase:
...
@Output() deleteEvent = new EventEmitter<Array<string>>();
...
Prefix a method which responds to an event emitter with on
and write them in camelCase.
<valtimo-delete-role-modal
<!-- Method responding to event is prefixed with on -->
(deleteEvent)="onDelete($event)"
[showDeleteModal$]="showDeleteModal$"
[deleteRowKeys]="deleteRowKeys$ | async"
>
</valtimo-delete-role-modal>
...
public onDelete(roles: Array<string>): void {
...
}
...
When possible, set the changeDetection
strategy of components to ChangeDetectionStrategy.OnPush
.
If the selector of a component is not going to be used directly (for example when the component is linked to a route and not used elsewhere), do not define it.
When a component does not separate styling, do not create a stylesheet for it. The styleUrls
property is not necessary then.
If an if statement contains a single expression, and is not likely to be expanded in the future, it is allowed to write it without curly brackets. If it is lengthy, or likely that more expressions are added inside the statement later on, always include curly brackets.
// short expression readable without curly brackets
if (selectedTheme) this._preferredTheme$.next(selectedTheme);
// longer expression is more readable with curly brackets. Leaves room for expansion in the future.
if (selectedTheme) {
this.themeService.parseThemeAndSaveAccentColorsInApi(selectedTheme);
}