Using ControlValueAccessor to Create Custom Form Controls in Angular

import { Component, forwardRef, HostBinding, Input } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'rating-input',
  template: `
    <span
      *ngFor="let starred of stars; let i = index"
      (click)="onTouched(); rate(i + (starred ? (value > i + 1 ? 1 : 0) : 1))">
      <ng-container *ngIf="starred; else noStar">⭐</ng-container>
      <ng-template #noStar>·</ng-template>
    </span>
  `,
  styles: [`
    span {
      display: inline-block;
      width: 25px;
      line-height: 25px;
      text-align: center;
      cursor: pointer;
    }
  `],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => RatingInputComponent),
      multi: true
    }
  ]
})
export class RatingInputComponent implements ControlValueAccessor {

  stars: boolean[] = Array(5).fill(false);

  // Allow the input to be disabled, and when it is make it somewhat transparent.
  @Input() disabled = false;
  @HostBinding('style.opacity')
  get opacity() {
    return this.disabled ? 0.25 : 1;
  }

  // Function to call when the rating changes.
  onChange = (rating: number) => {};

  // Function to call when the input is touched (when a star is clicked).
  onTouched = () => {};

  get value(): number {
    return this.stars.reduce((total, starred) => {
      return total + (starred ? 1 : 0);
    }, 0);
  }

  rate(rating: number) {
    if (!this.disabled) {
      this.writeValue(rating);
    }
  }

  // Allows Angular to update the model (rating).
  // Update the model and changes needed for the view here.
  writeValue(rating: number): void {
    this.stars = this.stars.map((_, i) => rating > i);
    this.onChange(this.value)
  }

  // Allows Angular to register a function to call when the model (rating) changes.
  // Save the function as a property to call later here.
  registerOnChange(fn: (rating: number) => void): void {
    this.onChange = fn;
  }

  // Allows Angular to register a function to call when the input has been touched.
  // Save the function as a property to call later here.
  registerOnTouched(fn: () => void): void {
    this.onTouched = fn;
  }

  // Allows Angular to disable the input.
  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

}

References
https://alligator.io/angular/custom-form-control/
https://blog.thoughtram.io/angular/2016/07/27/custom-form-controls-in-angular-2.html

Loading data before components using Router resolver in Angular

As the name suggests, we can add a resolve function to the route which loads the component that has an API call to do. This will cause the component to be only loaded and displayed by Angular once the API call (or whatever else we define) is loaded.

Create a separate class which we’ll have the resolver functionality

// resolver.ts
import { Injectable } from '@angular/core';
import { Resolve } from '@angular/router';
import { Observable } from 'rxjs';
import { ApiService } from './api.service';

@Injectable()
export class Resolver implements Resolve<Observable<string>> {
  constructor(private api: ApiService) { }

  resolve() {
    return this.api.getProducts();
  }
}

Add this class as a provider

// app.module.ts
import { Resolver } from './resolver';
// ...
providers: [Resolver],

app-routing.module.ts

// app-routing.module.ts
import { Resolver } from './resolver';

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'products', component: ProductsComponent, resolve: { products: Resolver } }
];
// products.component.ts
import { ActivatedRoute } from '@angular/router';
// ...
constructor(private route: ActivatedRoute) { }
products;
ngOnInit() {
  this.products = this.route.snapshot.data.products;
}

Access to Route Params

import { Injectable } from '@angular/core';
import { HnService } from './hn.service';

import { Resolve } from '@angular/router';

import { ActivatedRouteSnapshot } from '@angular/router';

@Injectable()
export class HnResolver implements Resolve<any> {
  constructor(private hnService: HnService) {}

  resolve(route: ActivatedRouteSnapshot) {
    return this.hnService.getPost(route.paramMap.get('id'));
  }
}

References
https://blog.fullstacktraining.com/loading-data-before-components-in-angular/
https://alligator.io/angular/route-resolvers/

Tmux Cheat Sheet & Quick Reference

Start a new session

tmux
tmux new
tmux new-session

Start a new session with the name mysession

tmux new -s mysession

kill/delete session mysession

tmux kill-session -t mysession

kill/delete all sessions but the current

tmux kill-session -a

kill/delete all sessions but mysession

tmux kill-session -a -t mysession

Show all sessions

tmux ls
tmux list-sessions

Attach to a session with the name mysession

tmux a -t mysession
tmux at -t mysession
tmux attach -t mysession
tmux attach-session -t mysession

Rename session

Ctrl + b $

Detach from session

Ctrl + b d

Create window

Ctrl + b c

Rename current window

Ctrl + b ,

Close current window

Ctrl + b &

Previous window

Ctrl + b p

Next window

Ctrl + b n

Switch/select window by number

Ctrl + b 0 ... 9

Scroll

Ctrl-b [
# Press q to quit scroll mode
Ctrl-b PgUp

References
https://tmuxcheatsheet.com/
https://gist.github.com/MohamedAlaa/2961058

Use MongoDB createIndex in Java

private MongoCollection<Document> createCollection(String collectionName) {
    String indexFieldName1 = "itemId";
    String indexFieldName2 = "time";
    MongoCollection<Document> mongoCollection = this.database.getCollection(collectionName);

    if (mongoCollection == null) {
        this.database.createCollection(collectionName);
        mongoCollection = this.database.getCollection(collectionName);
    }

    IndexOptions indexOptions1 = new IndexOptions().unique(false)
            .background(false).name(indexFieldName1);
    Bson keys1 = new Document(indexFieldName1, Integer.valueOf(1));
    mongoCollection.createIndex(keys1, indexOptions1);

    IndexOptions indexOptions2 = new IndexOptions().unique(false)
            .background(false).name(indexFieldName2);
    Bson keys2 = new Document(indexFieldName2, Integer.valueOf(1));
    mongoCollection.createIndex(keys2, indexOptions2);

    return mongoCollection;
}

References
https://www.programcreek.com/java-api-examples/?class=com.mongodb.client.MongoCollection&method=createIndex

yarn upgrade

yarn upgrade
yarn upgrade left-pad
yarn upgrade left-pad@^1.0.0
yarn upgrade left-pad grunt
yarn upgrade @angular
yarn upgrade --pattern gulp
yarn upgrade left-pad --pattern "gulp|grunt"
yarn upgrade --latest --pattern "gulp-(match|newer)"
yarn upgrade --latest
yarn upgrade left-pad --latest
yarn upgrade left-pad grunt --latest --tilde

References
https://yarnpkg.com/lang/en/docs/cli/upgrade/

Bootstrap Login Screen with Floating Labels

HTML

<!-- This snippet uses Font Awesome 5 Free as a dependency. You can download it at fontawesome.io! -->

<body>
  <div class="container">
    <div class="row">
      <div class="col-sm-9 col-md-7 col-lg-5 mx-auto">
        <div class="card card-signin my-5">
          <div class="card-body">
            <h5 class="card-title text-center">Sign In</h5>
            <form class="form-signin">
              <div class="form-label-group">
                <input type="email" id="inputEmail" class="form-control" placeholder="Email address" required autofocus>
                <label for="inputEmail">Email address</label>
              </div>

              <div class="form-label-group">
                <input type="password" id="inputPassword" class="form-control" placeholder="Password" required>
                <label for="inputPassword">Password</label>
              </div>

              <div class="custom-control custom-checkbox mb-3">
                <input type="checkbox" class="custom-control-input" id="customCheck1">
                <label class="custom-control-label" for="customCheck1">Remember password</label>
              </div>
              <button class="btn btn-lg btn-primary btn-block text-uppercase" type="submit">Sign in</button>
              <hr class="my-4">
              <button class="btn btn-lg btn-google btn-block text-uppercase" type="submit"><i class="fab fa-google mr-2"></i> Sign in with Google</button>
              <button class="btn btn-lg btn-facebook btn-block text-uppercase" type="submit"><i class="fab fa-facebook-f mr-2"></i> Sign in with Facebook</button>
            </form>
          </div>
        </div>
      </div>
    </div>
  </div>
</body>

CSS

:root {
  --input-padding-x: 1.5rem;
  --input-padding-y: .75rem;
}

body {
  background: #007bff;
  background: linear-gradient(to right, #0062E6, #33AEFF);
}

.card-signin {
  border: 0;
  border-radius: 1rem;
  box-shadow: 0 0.5rem 1rem 0 rgba(0, 0, 0, 0.1);
}

.card-signin .card-title {
  margin-bottom: 2rem;
  font-weight: 300;
  font-size: 1.5rem;
}

.card-signin .card-body {
  padding: 2rem;
}

.form-signin {
  width: 100%;
}

.form-signin .btn {
  font-size: 80%;
  border-radius: 5rem;
  letter-spacing: .1rem;
  font-weight: bold;
  padding: 1rem;
  transition: all 0.2s;
}

.form-label-group {
  position: relative;
  margin-bottom: 1rem;
}

.form-label-group input {
  height: auto;
  border-radius: 2rem;
}

.form-label-group>input,
.form-label-group>label {
  padding: var(--input-padding-y) var(--input-padding-x);
}

.form-label-group>label {
  position: absolute;
  top: 0;
  left: 0;
  display: block;
  width: 100%;
  margin-bottom: 0;
  /* Override default `<label>` margin */
  line-height: 1.5;
  color: #495057;
  border: 1px solid transparent;
  border-radius: .25rem;
  transition: all .1s ease-in-out;
}

.form-label-group input::-webkit-input-placeholder {
  color: transparent;
}

.form-label-group input:-ms-input-placeholder {
  color: transparent;
}

.form-label-group input::-ms-input-placeholder {
  color: transparent;
}

.form-label-group input::-moz-placeholder {
  color: transparent;
}

.form-label-group input::placeholder {
  color: transparent;
}

.form-label-group input:not(:placeholder-shown) {
  padding-top: calc(var(--input-padding-y) + var(--input-padding-y) * (2 / 3));
  padding-bottom: calc(var(--input-padding-y) / 3);
}

.form-label-group input:not(:placeholder-shown)~label {
  padding-top: calc(var(--input-padding-y) / 3);
  padding-bottom: calc(var(--input-padding-y) / 3);
  font-size: 12px;
  color: #777;
}

.btn-google {
  color: white;
  background-color: #ea4335;
}

.btn-facebook {
  color: white;
  background-color: #3b5998;
}

/* Fallback for Edge
-------------------------------------------------- */

@supports (-ms-ime-align: auto) {
  .form-label-group>label {
    display: none;
  }
  .form-label-group input::-ms-input-placeholder {
    color: #777;
  }
}

/* Fallback for IE
-------------------------------------------------- */

@media all and (-ms-high-contrast: none),
(-ms-high-contrast: active) {
  .form-label-group>label {
    display: none;
  }
  .form-label-group input:-ms-input-placeholder {
    color: #777;
  }
}

References
https://startbootstrap.com/snippets/login/