Skip to content
tinytip

Latest tips / 8

Angular Outputs can be RxJS Observables

Angular uses the @Output decorator and the EventEmitter class to emit events to the parent component. But you can use RxJS streams instead.

The EventEmitter class is actually a wrapper around the RxJs Subject class. If you have an existing Subject or Observable that you want to use as an output, you can directly use it with the @Output decorator.

TypeScript
export class SearchComponent {
  // With Angular EventEmitter
  @Output()
  search = new EventEmitter<string>();

  // With RxJs Subject / Observable
  @Output()
  search = this.form.valueChanges.pipe(
    debounceTime(500),
    map((value) => value.term),
    distinctUntilChanged(),
  );
}

CSS calc() requires unit

Most CSS properties require a unit, for example padding: 10px. If you use 0 you can omit the unit, like padding: 0.

An exception is the calc() function, which always requires a unit, even for 0.

CSS
.example-class {
  padding: 0; /* works */

  height: calc(100vh - 0); /* does NOT work */
  height: calc(100vh - 0px); /* works */
}

That is mainly important if you use CSS custom properties (aka variables) with a default value of 0 inside calc():

CSS
.example-class {
  height: calc(100vh - var(--header-height, 0)); /* does NOT work */
  height: calc(100vh - var(--header-height, 0px)); /* works */
}

Exclude certain types in TypeScript

The Exclude type in TypeScript is a way to exclude certain types from a union.

Let’s say you use a third-party package that exports a function with an optional parameter. The parameter uses an inline type which is not exported:

TypeScript
export function fetch(sortBy?: "name" | "date" | "rating") {}

You now want to extract the type of sortBy but exclude undefined from the union (undefined is allowed because the param is optional).

You can use Parameters<T> to get the parameters of the function and Exclude<T> to exclude undefined:

TypeScript
type SortBy = Exclude<Parameters<typeof fetch>[0], undefined>;
// Results in: type SortBy = "name" | "date" | "rating"

Data binding to aria attributes in Angular

Data Binding in Angular Templates binds to properties not attributes. As aria-* attributes are not properties, they cannot be bound to. If you want to bind to aria attributes, you need to use the [attr.aria-*] syntax.

HTML
<!-- Does NOT work -->
<div [aria-hidden]="isHidden">
  <!-- ... -->
</div>

<!-- Works -->
<div [attr.aria-hidden]="isHidden">
  <!-- ... -->
</div>

Keep the separators when splitting a string in JavaScript

The split() function in JavaScript splits a string into an array by a separator and removes the separator:

JavaScript
// Split by "-"
"hello-from-javascript".split("-");
// Result: ["hello", "from", "javascript"]

// Split by "-" or "_"
"hello-from_javascript".split(/-|_/g);
// Result: ["hello", "from", "javascript"]

Use a RegEx group (...) if you want to keep the separator in the array:

JavaScript
// Split by "-" or "_"
"hello-from_javascript".split(/(-|_)/g);
// Result: ["hello", "-", "from", "_", "javascript"]