Code Snippets

Here I present interesting use cases with short snippets of code that solve those problems

Perform a side effect on a stream but only once Sometimes we want to perform side effects on #rxjs streams, but only once, e.g., the very first time (for example if we want to "setValue" on a FormControl but ignore other emissions but showing next emissions in the UI). Can also be used to perform the side effect on a specific index of emission
function tapOnce<T>(cb: (item: T) => any, index = 0) {
  return function(source: Observable<T>) {
    return source.pipe(
      map((data, i) => ({data, i})), // get the index
      tap(({data, i}) => {
        if (i === index) { // check if index matches
          cb(data); // perform the side effect
        }
      }),
      map(({data}) => data), // map back to the original emission
    )
  }
}

of(1, 2, 3, 4, 5).pipe(
    tapOnce(console.log), // will log 1
).subscribe();
Ditch change detection but still use Angular as usual Using Angular 14's new "inject" function and JavaScript Proxies, build a state that manually triggers change detection
function useState<State extends Record<string, any>>(state: State) {
    const cdRef = inject(ChangeDetectorRef);
    cdRef.detach(); // we don't need automatic change detection
    setTimeout(() => cdRef.detectChanges()); // detect the very first changes when the state initializes
    return new Proxy(state, {
        set: (target, property: string, value) => {
            (target as Record<string, any>)[property] = value; // change the state
            cdRef.detectChanges(); // manually trigger the change detection
            return true;
        },
        get(target, property: string) {
            return (target as Record<string, any>)[property]; // just return the state property
        },
    });
}

@Component({
    selector: "my-component",
    template: `
    <div>
        {{text}}
    </div>
    <button (click)="onClick()">Click me!</button>
    `
})
export class MyComponent {
    vm = useState({text: 'Hello, World!'}); // now we have a state

    onClick() {
        this.vm.text = "Hello Angular"; // works as expected, changes are detected
    }
    get text() {
        console.log('working');
        return this.vm.text;
    }
}
Changing nested fields of an object inside of an array if the object satisfies a condition Use immerjs for this, to create a new instance while changing the values imperatively
import { produce } from 'immer';

const input = [
  {
    type: 'notToBeChanged',
    field: 'unchanged',
  },
  {
    type: 'toBeChanged',
    field: 'unchanged',
  },
];

// instead of this
const result = input.map(item => {
  if (item.type === 'toBeChanged') {
    return ({
      ...item,
      field: 'changed',
    })
  }
  return item;
})

// try this
const result = produce(input, draft => {
  draft.filter(item => item.type === 'toBeChanged').forEach(item => {
    item.field = 'changed';
  });
});
Opening a "You have been inactive popup" Use RxJS for this, listen to a bunch of events, buffer them and check if the buffer is empty
import { fromEvent, merge, interval } from 'rxjs'; 
import { filter, bufferWhen } from 'rxjs/operators';

const ACTIVE_EVENTS = ['click', 'scroll', 'contextmenu', 'dblclick', 'mousemove'];
// you can add as many events as you want to define "being inactive"

merge(...ACTIVE_EVENTS.map(event => fromEvent(document, event))).pipe(
  bufferWhen(() => interval(10_000)),
  filter(events => events.length === 0),
).subscribe(() => alert('You have been inactive for ten seconds!'));
Remove key from object type-safely Use destructuring for this, discard the property, keep the cleared object
// typesafe implementation

export function withoutKey<T extends object, K extends keyof T>(
    obj: T, key: K: M,
): Omit<T, K> {
    const {[]: r, ...cleared } = obj;
    return cleared;
}

const obj = {
    a: 1,
    b: 2,
    c: 3,
};

const cleared = withoutKey(obj, 'a');

cleared.b; // { b: 2, c: 3 } exists
cleared.a; // undefined, no longr present, recognized by TypeScript
Avoid toArray gotcha "toArray" waits for the source Observable to complete, rather than the one you switched to, to emit the values in an Array; if the source is "infinite", you will never get the values, even if the Observable you "switchMap" to does.
// instead of this
fromEvent(document.querySelector('input'), 'input').pipe(
  debounceTime(300),
  map(event => (event.target as HTMLInputElement).value),
  switchMap(query => getData(query)),
  switchAll(),
  map(({firstName, lastName}) => firstName + ' ' + lastName),
  toArray(), // you will never receive this value, as "fromEvent" never completes on itself
).subscribe(console.log);

// do this
fromEvent(document.querySelector('input'), 'input').pipe(
  debounceTime(300),
  map(event => (event.target as HTMLInputElement).value),
  switchMap(query => getData(query).pipe(
    switchAll(),
    map(({firstName, lastName}) => firstName + ' ' + lastName),
    toArray(), // now the inner Observavble completes and emits with each "fromEvent" emission
  )),
).subscribe(console.log);
Getting form value including disabled controls A special method exists to get the full value of the form
const form = this.formBuilder.group({
  name: [''],
  age: [''],
});
form.get('age').disable();

const value = form.value;
console.log(value); // {name: ''}
const rawValue = for.getRawValue();
console.log(rawValue); // {name: '', age: ''}
Retry an action if error happens on a condition Use RxJS "retry" with a config, provide another Observable to listen to
responseFromServer$.pipe(
    retry({
        count: 3, // we can also OPTIONALLY 
                  // provide how many times 
                  // a user is allowed to retry 
        delay: () => fromEvent(
              document.querySelector('#retryBtn'),
              'click',
        ), // wait for the user to click the button
    }),
);