Angular Recipes

Or: things I have implemented multiple times while developing in Angular 2 / Angular 6. Mostly for my own reference, but if it helsp someone else, great. This is a living post that I will add to and edit as I continue to develop in Angular without necessarily timestamping changes.

<a [routerLink]="['/interface-client']">All Clients</a> > <a [routerLink]="['/interface-client/', id]">{{ client.name }}</a>

Display Scrollbar while Bootstrap Modal is open

.modal-open
{
  overflow-y: scroll;
}

Source

Monitor how Bootstrap Modal was closed from parent

parent.component.ts

openModal() {
  const modalRef = this.modalService.open(ConfirmModalComponent); // Open modal
  modalRef.componentInstance.model = this.model; // Pass object to modal

  // Change the variable submitted based on how the modal was closed
  modalRef.result.then(value => { this.submitted = value === 'submitted' ? true : false; });
}

confirm-modal.component.ts:

onSubmit() {
  this.dataService.newApp(this.model);
  this.activeModal.close('submitted');
}

onCancel() {
  this.activeModal.close('canceled');
}

Dynamically Change Dropdown Contents (Angular Forms)

Say I have two dropdown elements in the form of select tags, one with id “species” and the other with id “name”. I want the available names to change based on the species currently selected, and I never want either dropdown to be blank.

More good advice: KastePanyan24 on Medium

Here is a working example on stackblitz.

app.component.html

<h3>INPUT:</h3>

<form *ngIf="formReady === true">
  <div class="form-group">
    <label for="species">Species: </label>
    <select class="form-control"
            id="species"
            (change)="onChange($event.target.value)"
            [(ngModel)]="model.species"
            name="f_species">
      <option *ngFor="let species_singular of species" [value]="species_singular">
        {{ species_singular }}
    </select>
  </div>

  <div class="form-group">
    <label for="name">Name: </label>
    <select class="form-control"
            id="name"
            [(ngModel)]="model.name"
            name="f_name">
      <option *ngFor="let name of appropriateNames" [value]="name.name">
        {{ name.name }}
    </select>
  </div>
</form>

<h3>OUTPUT:</h3>
<pre>{{ model.species }}, {{ model.name }}</pre>

app.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
  /* The model the form begins with. */
  model = { species: 'cat', name: 'spot' }

  /* Our data */
  species: string[] = ['cat', 'dog'];
  names = [
    { species: 'cat', name: 'smokey' },
    { species: 'cat', name: 'spot' },
    { species: 'dog', name: 'kobe' },
    { species: 'dog', name: 'cheddar' }
  ];

  /* The names allowed to be used with the currently selected species */
  appropriateNames;

  /* Delay rendering of the form until appropriateName has been populated.
   * Without this the name dropdown is empty until the species is changed, which
   * is rather inconvenient if you want to use the first species in the dropdown */
  formReady: boolean = false; 

  ngOnInit()
  {
    /* Run the change event handler on page load to populate appropriateNames
     * variable and the name dropdown */
    this.onChange(this.model.species);
    this.formReady = true;
  }

  /**
   * Runs every time the species drop down is changed and populates
   * the name dropdown with the names available for that species.
   * 
   * @param species The species currently selected
   */
  onChange(species: string) {
    console.log('selected species: ', species);

    /* Everytime the dropdown changes the name array is iterated through. 
     * With large arrays this could slow things down, but I haven't had any 
     * issues with small arrays */
    this.appropriateNames= this.names.filter(obj => {
      return obj.species === species;
    });

    /* Change the name dropdown to the first item in the appropriateName array */
    this.model.name = this.appropriateNames[0].name;

    console.log(this.appropriateNames);
  }
}

Ng2-Smart-Table is a great library. Unfortunately, linking inside of a cell is a little difficult. I wanted to use [routerLink] inside the cell, but Angular kept sanitizing it.

To use [routerLink] inside of a cell, create a sub-component.

The table variables look like this:

// Table Stuff
private values = [];
source: LocalDataSource;
settings = {
  actions: {
    add: false,
    edit: false,
    delete: false
  },
  mode: 'external',
  columns:
  {
    tableData: {
      title: 'Hostname',
      type: 'custom',
      renderComponent: LinkComponent
    }
  }

And to populate the table data:

this.dataService.fetchHosts(key).subscribe((result: object[])  => {
  result.map(mapped => {
    const value = mapped['value'];
    this.values.push({ tableData: value });
  });
  this.source.load(this.values);
});

The subcomponent, LinkComponent looks like this:

export class HostnameCellComponent implements OnInit{
  @Input() value: string;

Pass multiple pieces of data in to a Ng2-Smart-Table sub-component

Okay, but what if your link needs multiple variables to generate it? Unfortunately, the easiest way I found to do this is to create a sub-component as above, concatenate your variables in to a single, comma seperated, string, pass said string in to the component, and split it up there.

This is gross, but it means the table is still sortable.

valuelink.component.ts:

export class HostnameCellComponent implements OnInit{
  @Input() value: string;
  value_part1: string = '';
  value_part2: string = '';

  constructor(private route: ActivatedRoute, private router: Router) {
  }

  ngOnInit()
  {
    const array: string[] = this.value.split(',');
    this.value_part1 = array[0];
    this.value_part2 = array[1];
  }
}