Introduction

The following is a collection of commands for various languages and tools.

This book is generated with mdBook and hosted by GitLab Pages.

See Also

Windows

Search Files.bat

REM From https://stackoverflow.com/a/13799990/11912
findstr /s "<term>" *.item

Battery reporting

# Generate a battery report.
powercfg /batteryreport /output "C:\battery-report.html"

# Sleep study.
powercfg /sleepstudy /output "C:\sleep-study.html"

Linux

Specifically using Ubuntu.

SSH

Type exit to exit out of a SSH session.

# SSH into a server with Raspberry Pi's default user.
ssh pi@<ip_address>

# SSH into a server as a particular user.
ssh <user_name>@<ip_address>

# Output current user's name.
whoami

# View last hour of SSH logs.
sudo journalctl -u ssh --since "1 hour ago"

# Determine shell being used.
ps -p "$$"

# Find process having a process id (PID).
ps -p <pid>

System information

# View OS information.
cat /etc/os-release

# View system information on Ubuntu, which is displayed on SSH login:
landscape-sysinfo

# System monitoring.
top
# Prettier version of top.
htop

# Memory usage.
free
# Human readable.
free -h
# Show in MBs.
free -m

# IP address
ip a
hostname -I

# Server uptime.
uptime

# View path variable.
echo $PATH

# Find path of binary.
which cd

Package management

# Show manually installed packages.
apt-mark showmanual
apt list --manual-installed

# List installed packages.
apt list --installed
dpkg --list

sudo dpkg --audit
sudo dpkg --get-selections

Service logs

# View logs for a particular service.
sudo journalctl -fu mycustom.service
sudo journalctl -fu mycustom.service --since "1 hour ago"

Copy files

# Copy a file to a directory.
scp file-name.ext <user>@<server>:~/path/to/directory/

# Copy the contents of a folder to a remote folder.
scp -r .\path\to\directory\* <user>@<server>:/path/to/remove/directory

Sudo

# Start a root shell. `exit` when done.
sudo -s

Firewall (ufw)

# Show status of firewall. If active, also lists rules.
sudo ufw status
# Show rules even when ufw is inactive.
sudo ufw show added

# Allow by service.
sudo ufw allow ssh
# Allows 80 and 443.
sudo ufw allow 'Nginx Full'

# Allow port-number.
sudo ufw allow <port-number>
sudo ufw allow 8080

# Remove allowed port-number.
sudo ufw delete allow <port-number>
sudo ufw delete allow 9041

Linux system management

# Reboot/restart.
sudo reboot
sudo shutdown -r now
# Reboot in 5 minutes.
sudo shutdown -r 5

# Shutdown
sudo poweroff

# Get machine and kernel information.
uname -mrs

# Get distribution-specific information.
lsb_release -a

# Get computer name.
hostname

# Get Debian version.
cat /etc/debian_version

Updates

# Get a list of current packages.
sudo apt update

# Get a list of upgradable packages.
apt list --upgradable
apt list --upgradable -a

# Upgrade all packages.
sudo apt upgrade

# Install or update specificed package.
sudo apt install <package-name>

File system

Information

# View disk space.
df
df -h

# Count number of files and folders in home directory.
ls ~ | wc -l

# List block devices. Helps with seeing partition information.
sudo lsblk -f -m

# View the total size of a directory.
sudo du -sh /path/to/directory
# Show by child directory size.
sudo du -h /path/to/directory --max-depth=1
# Show largest directories first.
sudo du -h /path/to/directory --max-depth=1 | sort -hr
# Sort by directory name.
sudo du -h /path/to/directory --max-depth=1 | sort -k2
# Largest directories.
sudo du --separate-dirs -h /path/to/directory | sort -hr | head
sudo du --separate-dirs -h /path/to/directory | sort -hr | head -n 2

# List file sizes (in MB) in a directory.
sudo ls -l --block-size=MB /var/log/nginx/
sudo ls -l --block-size=1MB /var/log/nginx/
# Human readable file sizes.
sudo ls -lh /var/log/nginx/

# Get all .mp4 files sorted by date and showing date, folder name, file name, and size.
# Excludes ._ and .DS_Store files (macOS).
find . -type f -name "*.mp4" ! -name "._*" ! -name ".DS_Store" -printf "%CY-%Cm-%Cd %CT     %h     %f     %s\n" | sort -n
# Print working directory (current directory).
pwd

# By itself, move to the current user's home directory. Tilde is short for home directory.
cd
cd ~

Modification

# Make a new directory.
mkdir name
mkdir name1 name2 name3

# Make a new directory and sub-directory/ies.
mkdir -p path/to/directory

# Move file(s)/directories to an existing directory.
mv file.txt dir1
mv file1.txt file2.txt dir1
mv file1.txt file2.txt new-dir dir1

# Rename a file.
mv file.txt new-name.txt

# Rename a directory.
mv dir-name new-dir-name

# Copy a file
cp path/to/file.txt .
cp file.txt copy-file.txt

# Remove/delete a file.
rm file.txt

# Remove/delete an empty directory.
rmdir path/to/directory

# Remove/delete a directory, even if it has files. (Only macOS?)
rmdir -r path/to/directory
# Works on Ubuntu.
rm -r path/to/directory

File viewing and creation

# Look at a file or multiple files.
cat file.txt
cat file1.txt file2.txt
cat file?.txt
cat file*

# Write to a file.
echo "Some text" > file.txt
echo "Some more text" >> file.txt

# File viewer. Press q to quit.
less file.txt

# List all files in a directory, with full paths, into a text file.
find wwwroot -type f > files.txt
# The above with directory, file name, file date, and size, into a tab-separated file.
find . -type f ! -name "._*" ! -name ".DS_Store" -printf "%h\t%f\t%CY-%Cm-%Cd %CT\t%s\n" > files.tsv

# Get full path to a file.
readlink -f relative/path/to/file

# Read lines of a file
sudo head -n 10 /var/log/nginx/access.log
sudo tail -n 10 /var/log/nginx/access.log

Hidden content

# Make a hidden directory.
mkdir .dir1

# Make a hidden file.
mkdir .hide.txt

# List all hidden files/folders.
ls -a

Users and permissions

User management

# Show who's logged in.
w

# Show the last logged in users.
last

# Show the last bad login attempts.
lastb

# Set the password for a user.
passwd <user>

# List user information, including groups.
id <user>

# List all users.
cat /etc/passwd

# List all users across multiple sources.
getent passwd

# Install sudo on Debian or the like (that don't have it by default).
apt update
apt install sudo

# Grant a user sudo.
usermod -aG sudo <username>
# Add the current user to a group (<group-name>).
sudo usermod -aG <group-name> ${USER}

Permissions

# List all users.
compgen -u
getent passwd

# List all groups.
compgen -g
getent group

# List all groups current user is in.
groups
# List all groups a user (username) is in.
groups username

# Add write access to group.
chmod g+w file-or-directory

# Remove write and execute from group.
chmod g-wx file-or-directory

# Remove read, write, and execute from others.
chmod o-rwx file-or-directory

# Users, groups, and others have read, write, and execute.
chmod ugo+rwx file-or-directory

# Grant all users read-only access to a file or directory.
chmod a=r file-or-directory

# View permissions.
ls -l
ls -ld

nginx

Configuration

# Test/verify configuration.
sudo nginx -t

# View base configuration.
sudo cat /etc/nginx/nginx.conf

# View default site configuration.
sudo cat /etc/nginx/sites-enabled/default

# List enabled sites.
ls -l /etc/nginx/sites-enabled/

# List available sites.
ls /etc/nginx/sites-available/

# Enable site via a symbolic link.
sudo ln -s /etc/nginx/sites-available/SITE_CONFIG_FILE_NAME /etc/nginx/sites-enabled/

# Remove symbolic link/enabled site.
sudo rm SITE_CONFIG_FILE_NAME
sudo unlink SITE_CONFIG_FILE_NAME
# Remove with confirmation.
sudo rm -i SITE_CONFIG_FILE_NAME

Logging

# View access logs.
sudo cat /var/log/nginx/access.log

# Get top 20 most used user agents.
sudo cat /var/log/nginx/access.log | awk -F\" '{print $6}' | sort | uniq -c | sort -nr | head -20

Management

# Restart nginx.
sudo systemctl restart nginx

Raspberry Pi Commands

Note to self: Information about Pis I own can be found at https://strivinglife.gitlab.io/book-raspberry-pi/.

Current Uptime

uptime

Restarting

sudo reboot

Shutdown

sudo shutdown -h now

Internet Configuration

ifconfig

# Try to bring a network interface up.
sudo ifup wlan0

Samba

# Restart Samba services.
sudo /etc/init.d/samba restart

# Backup copy of Samba configuration.
sudo cp /etc/samba/smb.conf /etc/samba/smb.conf.old
# Edit Samba configuration. Restart (see above) for changes to be picked up.
sudo nano /etc/samba/smb.conf

# Check Samba services.
systemctl status smbd

SSH

# Check SSH service status.
systemctl status sshd

# SSH into a server with Raspberry Pi's default user.
# Enter `exit` and press enter to quit the session.
ssh pi@<ip_address>

Terminal

# View past commands.
history

# View process information
top

Drive Information

# List device information.
sudo fdisk -l

# List mounts.
sudo mount -l

# Example: cd into a mounted USB device.
cd /media/usbhdd1

USB/Port Information

# Basic list of each port.
lsusb
# Lists out each port, with Product if there's a connected device.
usb-devices

Display Information

# List information about the connected display.
tvservice -s

Hardware Information

See https://elinux.org/RPi_HardwareHistory#Board_Revision_History for more information.

# Includes Hardware and Revision information.
cat /proc/cpuinfo

OS Information

cat /etc/os-release

RetroPie audio issues

  1. Make sure the correct audio output device is selected in the RetroPie configuration.
  2. sudo nano /boot/config.txt and uncomment #hdmi_drive=2. ctrl+o, enter, ctrl+x, sudo reboot.

Start GUI

startx

Linux directories overview

From root (cd /):

  • /bin essential executables, always available
  • /sbin essential super user executables
  • /lib shared common libraries (for bin and sbin)
  • /etc (editable text configuration)
  • /usr
    • /usr/local
      • /usr/local/bin locally compiled binaries
    • /usr/bin installed binaries for users
  • /home user data
    • /home/___ individual user directory (also ~)
  • /boot required to boot the system (like Linux kernel)
  • /dev devices/hardware/drivers
  • /opt optional/add-on software, rarely will be used
  • /var variable files that change as the system is used (like log and cache files)
  • /tmp temporary files
  • /proc running processes

Detailed in the Filesystem Hierarchy Standard.

macOS

Homebrew

brew update
# Install example.
brew install git
# List outdated formulae.
brew outdated
brew upgrade

Angular

This content had previously been migrated to Angular Framework Notes but has been added back here.

Basics

Written as of Angular 12+.

Install the Angular CLI

npm install -g @angular/cli

Help/available commands

ng help
ng ___ --help

Create a new site/project

ng new new-project-name
# Use SCSS stylesheet format.
ng new new-project-name --style scss
# This option can be used if there's an issue during npm install.
ng new new-project-name --skip-install
# Use a custom prefix for selector names.
ng new new-project-name --prefix ___
# Create with a specific Angular version.
npx @angular/cli@12 new new-project-name

Start a development server

Runs on port 4200 by default.

ng serve
# Opens the site in the default browser.
ng serve --open
ng serve -o

Code scaffolding

See ng generate.

# Create a new component.
ng generate component heroes
ng generate component hero-detail
ng g c input-button-unit
ng g c products/product-detail
# Create a component in the current directory, without creating a directory.
ng g c product-detail --flat
ng g c products/product-detail --flat
# Preview creating a new component.
ng g c messages --dry-run
ng g c messages -d
# Create a new service.
ng generate service hero
ng generate service message
ng g s services/todo-list
# Create an interface
ng g i interfaces/todo-item
# Create a new module, putting it into AppModule.
ng g m products/product --flat -m app
# Create a new module, with routing.
ng g m admin-stuff --flat --routing

Build the project

# Puts in dist/ by default.
ng build

Add an in-memory web API

For mocking up a backend.

npm install angular-in-memory-web-api --save
# Generate a module in src/app (flat) and register it in AppModule imports (module=app)
ng generate module app-routing --flat --module=app
ng generate component dashboard
# In-memory Web API
npm install angular-in-memory-web-api --save
ng generate service InMemoryData
ng generate component hero-search

Angular Material

ng add @angular/material

Add ESLint

See angular-eslint for current information.

Note that eslint-plugin-jsdoc has been added to the install process.

ng add @angular-eslint/schematics
# Or alpha version for early Angular v13 support (as of 11/5/2021).
ng add @angular-eslint/schematics@next
npm install --save-dev eslint-plugin-jsdoc

New script in package.json:

"eslint": "eslint ./src --ext .js,.jsx,.ts,.tsx || (exit 0)",

Update *.ts overrides (plugins is completely new) in .eslintrc.json:

      "plugins": [
        "jsdoc"
      ],
      "extends": [
        "plugin:@angular-eslint/recommended",
        "plugin:@angular-eslint/template/process-inline-templates",
        "plugin:jsdoc/recommended"
      ],
      "rules": {
        "@angular-eslint/directive-selector": [
          "error",
          {
            "type": "attribute",
            "prefix": "app",
            "style": "camelCase"
          }
        ],
        "@angular-eslint/component-selector": [
          "error",
          {
            "type": "element",
            "prefix": "app",
            "style": "kebab-case"
          }
        ],
        "jsdoc/require-jsdoc": [1, {
          "contexts": ["ArrowFunctionExpression", "ClassDeclaration", "ClassExpression", "ClassProperty", "FunctionDeclaration", "MethodDefinition", "TSInterfaceDeclaration", "TSEnumDeclaration", "TSEnumMember"]
        }],
        "jsdoc/require-description": [1, {
          "contexts": ["ArrowFunctionExpression", "ClassDeclaration", "ClassExpression", "ClassProperty", "FunctionDeclaration", "MethodDefinition", "TSInterfaceDeclaration", "TSEnumDeclaration", "TSEnumMember"]
        }],
        "jsdoc/require-param-type": "off",
        "jsdoc/require-returns-type": "off"
      }

Test

npm run eslint

Configure for GitLab Pages

New script in package.json:

"buildProd": "ng build --base-href=/project-name/ --output-path=public",

Add new .gitlab-ci.yml in root directory:

image: node:14.17.6
pages:
  cache:
    paths:
      - node_modules/
  script:
    - npm install -g @angular/[email protected]
    - npm install
    - npm run buildProd
  artifacts:
    paths:
      - public
  only:
    - main
    - pages

Add Jest

npm install --save-dev jest jest-preset-angular @types/jest
# For Angular v13.
npm install --save-dev jest-preset-angular@next

New scripts in package.json:

"jest": "jest",
"jest:coverage": "jest --coverage",
"jest:coverageWatch": "jest --coverage --watchAll"

New setup-jest.ts file in the root directory:

import 'jest-preset-angular/setup-jest';

New jest.config.js file in the root directory:

// jest.config.js
module.exports = {
  preset: 'jest-preset-angular',
  setupFilesAfterEnv: ['<rootDir>/setup-jest.ts'],
  testPathIgnorePatterns: [
    '/node_modules/',
    '<rootDir>/src/test.ts'
  ],
  // An array of glob patterns indicating a set of files for which coverage information should be collected
  collectCoverageFrom: [
    "<rootDir>/src/**/*.ts",
    // Exclude main Angular files.
    "!<rootDir>/src/main.ts",
    "!<rootDir>/src/polyfills.ts",
    "!<rootDir>/src/environments/**",
    // Exclude Karma file.
    "!<rootDir>/src/test.ts",
    "!<rootDir>/src/**/*.d.ts",
    '!<rootDir>/node_modules/**',
  ],
  coverageReporters: [
    //"json",
    "text", // Command line output
    "text-summary", // Command line output of the summary
    "lcov",
    //"clover"
  ],
};

You may need to add the following in module.exports if it can't find your imports:

  modulePaths: [ "<rootDir>" ]

New ignore in .gitignore:

# Jest
/coverage

Update tsconfig.spec.json in root directory:

    "types": [
      "jasmine",
      "jest"
    ]

Test

npm run jest

Add Stylelint

npm init stylelint
npm install --save-dev stylelint-config-standard-scss

New script in package.json:

"stylelint": "stylelint \"src/**/*.scss\" --cache --formatter verbose --output-file stylelint.log || (exit 0)"

New .stylelintrc.js file in the root directory:

{
  "extends": ["stylelint-config-standard", "stylelint-config-standard-scss"]
}

New ignore in .gitignore:

# Stylelint
.stylelintcache
stylelint.log

Test

npm run stylelint

VS Code

  • Install Stylelint official extension.
  • Update settings to include scss files.

Updating

See the Angular Update Guide for version-specific steps.

Angular

ng update @angular/cli @angular/core
# If using Angular Material (also updates cdk package):
ng update @angular/material

Updating to latest patch version

ng update @angular/cli@^<major_version> @angular/core@^<major_version> @angular/material@^<major_version>
# Update, ignoring pending changes
ng update @angular/cli@^<major_version> @angular/core@^<major_version> @angular/material@^<major_version> --allow-dirty

ESLint

ng update @angular-eslint/schematics
npm run eslint

Jest

npm install @types/jest jest jest-preset-angular
npm run jest

Stylelint

npm install --save-dev stylelint stylelint-config-sass-guidelines stylelint-config-standard
npm run stylelint

Testing

Run unit tests

By default uses Karma/Jasmine.

ng test

Add ESLint

See angular-eslint on GitHub for current information. As of 9/5/2021:

ng add @angular-eslint/schematics

Fixing Broken Tests

Basics

  • ng test.
    • If you want to generate a code coverage report, run ng test --no-watch --code-coverage.
  • You can also limit tests to a directory: ng test --include='src/app/path/**/*.spec.ts'.
    • It also supports patterns:
      • ng test --include='src/app/[n-z]*/**/*.spec.ts'
      • ng test --include='src/app/admin/[d,g-i]*/**/*.spec.ts'

Disabling Tests

If you would like to skip a test, you can either change describe() to xdescribe() or it() to xit().

Skipped tests will display in the Karma report in a yellow-green color, and as a pending spec.

Fixing Common Errors

Error: Found the synthetic property @slideInAnimation. Please include either "BrowserAnimationsModule" or "NoopAnimationsModule" in your application.

Import NoopAnimationsModule, unless you need the other:

beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
        declarations: [ AppComponent ],
        imports: [ NoopAnimationsModule ]
    }).compileComponents();
}));

Error: NG0302: The pipe 'stringArray' could not be found!

Error: NG0302: The pipe 'yesNoIndicator' could not be found!

Error: NG0302: The pipe 'yesNoUnknownIndicator' could not be found!

Required pipes need to be added to declarations:

beforeEach(async () => {
    await TestBed.configureTestingModule({
        declarations: [ ProviderViewComponent, StringArrayPipe, YesNoIndicatorPipe, YesNoUnknownIndicatorPipe ]
    })
    .compileComponents();
});

NullInjectorError: No provider for ActivatedRoute!

Import RouterTestingModule:

beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
        declarations: [ AdminFacilityComponent ],
        imports: [ RouterTestingModule ]
    })
    .compileComponents();
}));

NullInjectorError: No provider for FormBuilder!

Import ReactiveFormsModule:

beforeEach(async () => {
    await TestBed.configureTestingModule({
        declarations: [ AdminCredentialUpsertComponent ],
        imports: [ ReactiveFormsModule ]
    })
    .compileComponents();
});

NullInjectorError: No provider for HttpClient!

Import HttpClientTestingModule:

beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
        declarations: [ FileUploadComponent ],
        imports: [ HttpClientTestingModule ]
    })
    .compileComponents();
}));

NullInjectorError: No provider for MatDialog!

Import MatDialogModule:

beforeEach(async () => {
    await TestBed.configureTestingModule({
        declarations: [ AdminCredentialTableComponent ],
        imports: [ MatDialogModule ]
    })
    .compileComponents();
});

NullInjectorError: No provider for MatDialogRef!

NullInjectorError: No provider for InjectionToken MatDialogData!

Update providers to provide MatDialogRef and MAT_DIALOG_DATA:

describe('AdminProviderEmploymentsInsertDialogComponent', () => {
    let component: AdminProviderEmploymentsInsertDialogComponent;
    let fixture: ComponentFixture<AdminProviderEmploymentsInsertDialogComponent>;

    beforeEach(async () => {
        await TestBed.configureTestingModule({
            declarations: [ AdminProviderEmploymentsInsertDialogComponent ],
            providers: [
                { provide: MatDialogRef, useValue: {} },
                { provide: MAT_DIALOG_DATA, useValue: { } }
            ]
        })
        .compileComponents();
    });
});

You may pass an object via useValue if needed, such as { provide: MAT_DIALOG_DATA, useValue: { credentialId: 0 } }.

NullInjectorError: No provider for MatSnackBar!

Import MatSnackBarModule:

beforeEach(async () => {
    await TestBed.configureTestingModule({
        declarations: [ AdminCredentialComponent ],
        imports: [ MatSnackBarModule ]
    })
    .compileComponents();
});

NullInjectorError: No provider for Router!

Import RouterTestingModule:

beforeEach(() => {
    TestBed.configureTestingModule({
        providers: [AdminImportPreprocessGuard],
        imports: [ RouterTestingModule ]
    });
});

TypeError: Cannot read properties of null (reading 'paramMap')

The component likely has some code like the following:

this.facilityId = +this.route.snapshot.parent.paramMap.get('id');

(The order of these may vary; you'll want to make note of the order and make sure that the following fix has the same order.)

You'll want to configure the route in the providers of your test, like the following example:

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      providers: [
        { provide: ActivatedRoute, useValue: { snapshot: { parent: { paramMap: convertToParamMap({ id: -1 }) } } } }
      ]
    })
    .compileComponents();
  });

This isn't specific to paramMap. For example:

//this.vendor = this.route.snapshot.data.vendor;
{ provide: ActivatedRoute, useValue: { snapshot: { data: { vendor: TEST_VENDOR } } } }

Error: No value accessor for form control with name: '...'

If this references a ng-select if your template HTML, import NgSelectModule (and likely NoopAnimationsModule if you haven't already) and add the declaration NgSelectFormFieldControlDirective:

await TestBed.configureTestingModule({
    declarations: [ AdminSpecialtiesGroupSpecialtyUpsertComponent, NgSelectFormFieldControlDirective ],
    imports: [ NgSelectModule, NoopAnimationsModule ]
})
.compileComponents();

If using mat-checkbox, import MatCheckboxModule, etcetera.

Fixing Common Console-Only Errors

These errors typically don't show in the browser, but will show in the console that ran ng test.

ERROR: 'NG0303: Can't bind to 'routerLink' since it isn't a known property of 'a'.'

ERROR: 'NG0304: 'router-outlet' is not a known element:

Import RouterTestingModule:

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ AdminSpecialtiesComponent ],
      imports: [ RouterTestingModule ]
    })
    .compileComponents();
  });

ERROR: 'NG0303: Can't bind to 'ngModel' since it isn't a known property of 'input'.'

Import FormsModule:

await TestBed.configureTestingModule({
  declarations: [ CommonListCrudTableComponent ],
  imports: [ FormsModule ]
})
.compileComponents();

ERROR: 'NG0303: Can't bind to 'formGroup' since it isn't a known property of 'form'.'

ERROR: 'NG0303: Can't bind to 'formControl' since it isn't a known property of 'form'.'

Import ReactiveFormsModule:

await TestBed.configureTestingModule({
    declarations: [ SpecialtyUpsertComponent ],
    imports: [ ReactiveFormsModule ]
})
.compileComponents();

ERROR: 'NG0304: 'mat-card' is not a known element:

ERROR: 'NG0304: 'mat-card-title' is not a known element:

ERROR: 'NG0304: 'mat-card-content' is not a known element:

ERROR: 'NG0304: 'mat-card-actions' is not a known element:

Import MatCardModule:

await TestBed.configureTestingModule({
    declarations: [ AdminSpecialtiesGroupUpsertComponent ],
    imports: [ MatCardModule, MatSnackBarModule, ReactiveFormsModule ],
    providers: [
    { provide: SpecialtyClient, useValue: mockSpecialtyClient }
    ]
})
.compileComponents();

ERROR: 'NG0304: 'mat-form-field' is not a known element:

ERROR: 'NG0304: 'mat-label' is not a known element:

ERROR: 'NG0304: 'mat-error' is not a known element:

Import MatFormFieldModule, MatInputModule, and NoopAnimationsModule:

    await TestBed.configureTestingModule({
      declarations: [ AdminSpecialtiesGroupUpsertComponent ],
      imports: [ MatFormFieldModule, MatInputModule, NoopAnimationsModule, ReactiveFormsModule ],
      providers: [
        { provide: SpecialtyClient, useValue: mockSpecialtyClient }
      ]
    })
    .compileComponents();

ERROR: 'NG0303: Can't bind to 'dataSource' since it isn't a known property of 'table'.'

ERROR: 'NG0303: Can't bind to 'matHeaderRowDef' since it isn't a known property of 'tr'.'

ERROR: 'NG0303: Can't bind to 'matRowDefColumns' since it isn't a known property of 'tr'.'

Import MatTableModule:

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ AdminSpecialtiesGroupTableComponent ],
      imports: [ MatCardModule, MatDialogModule, MatTableModule ]
    })
    .compileComponents();
  });

ERROR: 'NG0304: 'mat-datepicker' is not a known element:

Import MatDatepickerModule and MatMomentDateModule (unless using a different DateAdapter):

await TestBed.configureTestingModule({
    declarations: [ AdminSpecialtiesGroupSpecialtyUpsertComponent ],
    imports: [ MatDatepickerModule, MatMomentDateModule ]
})
.compileComponents();

ERROR: 'NG0304: 'mat-icon' is not a known element:

Import MatIconModule:

await TestBed.configureTestingModule({
    declarations: [ AdminSpecialtiesGroupComponent ],
    imports: [ MatIconModule, MatSnackBarModule ],
    providers: [
    { provide: SpecialtyClient, useValue: mockSpecialtyClient }
    ]
})
.compileComponents();

ERROR: 'NG0304: 'mat-nav-list' is not a known element:

MatListModule

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ AdminSpecialtiesComponent ],
      imports: [ MatListModule, RouterTestingModule ]
    })
    .compileComponents();
  });

ERROR: 'NG0304: 'mat-paginator' is not a known element:

Import MatPaginatorModule:

TestBed.configureTestingModule({
  declarations: [ ProviderSearchComponent ],
  imports: [ MatPaginatorModule, MatTableModule ],
  providers: [
    { provide: ProviderClient, useValue: mockProviderClient }
  ]
})
.compileComponents();

ERROR: 'NG0304: 'app-...' is not a known element:

Where 'app-...' is the name of a custom component, create and declare a fake component, including any @Input()s:

describe('DemographicsGenderComponent', () => {
  let component: DemographicsGenderComponent;
  let fixture: ComponentFixture<DemographicsGenderComponent>;

  @Component({
    selector: 'app-demographics-gender-table',
    template: ''
  })
  class FakeDemographicsGenderTableComponent {
    @Input() genders;
  }

  beforeEach(async () => {
    // ...

    await TestBed.configureTestingModule({
      declarations: [ DemographicsGenderComponent, FakeDemographicsGenderTableComponent ],
      imports: [ MatIconModule, MatSnackBarModule ],
      providers: [{ provide: DemographicsClient, useValue: mockDemographicsClient }]
    })
    .compileComponents();
  });

  // ...
});

TypeError: Cannot read properties of undefined (reading 'something')

Where something is an @Input(), set it when creating the component before running tests:

describe('AddressViewComponent', () => {
  let component: AddressViewComponent;
  let fixture: ComponentFixture<AddressViewComponent>;
  // Addition 1.
  const TEST_ADDRESS: IAddressViewModel = {
    addressType: new AddressTypeViewModel()
  };

  beforeEach(async () => {
    // ...
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(AddressViewComponent);
    component = fixture.componentInstance;
    // Addition 2.
    component.address = TEST_ADDRESS;
    fixture.detectChanges();
  });

  // ...
});

TinyMCE

For components that use TinyMCE, it's best to fake it for testing. If you do not, you'll end up with random errors mentioning length and open.

  @Component({
    selector: 'editor',
    template: '',
    providers: [
      { provide: NG_VALUE_ACCESSOR, useValue: { writeValue: () => {}, registerOnChange: () => {}, registerOnTouched: () => {} }, multi: true }
    ]
  })
  class FakeEditorComponent {
    @Input() init;
  }

  // ...
    await TestBed.configureTestingModule({
      declarations: [ FakeEditorComponent ]
      // ...
    })
    .compileComponents();
  // ...

.NET API Swagger Clients

  • NullInjectorError: No provider for AdminClient!
  • NullInjectorError: No provider for AuthClient!
  • NullInjectorError: No provider for CommonClient!

Start by creating a simple mock client that has no methods that should be spied upon.

  1. Define a variable for the mock client.
  2. Set it to jasmine.createSpy() in the beforeEach().
  3. Add the new mock client in providers.
describe('AppAuthService', () => {
  // Addition 1.
  let mockAuthClient;

  beforeEach(() => {
    mockAuthClient = jasmine.createSpy();

    TestBed.configureTestingModule({
        providers: [ { provide: AuthClient, useValue: mockAuthClient } ]
    })
  });
  // ...
});

Once it starts throwing an error, such as TypeError: this.adminClient.adminProviderCredentialsView is not a function then you can modify your test.

  1. Swap in jasmine.createSpyObj(), passing in any methods that will need to be spied upon.
  2. Try using .and.returnValue(of(true)) initially, and then tweak it based upon what the component is doing.
describe('AdminFacilityComponent', () => {
  let component: AdminFacilityComponent;
  let fixture: ComponentFixture<AdminFacilityComponent>;
  // Previous addition 1.
  let mockFacilityClient;

  beforeEach(waitForAsync(() => {
    // Change to addition 2.
    mockFacilityClient = jasmine.createSpyObj(['getFacilityAdminView']);
    mockFacilityClient.getFacilityAdminView.and.returnValue(of(true));

    TestBed.configureTestingModule({
      declarations: [ AdminFacilityComponent ],
      providers: [
        // Previous addition 3.
        { provide: FacilityClient, useValue: mockFacilityClient }
      ]
    })
    .compileComponents();
  }));
  // ...
});

Angular: Getting Started

My notes from Deborah Kurata's Angular: Getting Started.

My repo with pull request with training notes.

Angular Component Communication

My notes from Deborah Kurata's excellent Angular Component Communication.

Module 3

Binding and structural directives

Interpolation

// Component
pageTitle: string = 'Product List';
<!-- Template -->
<div>{{pageTitle}}</div>

Can also use functions ({{ getPageTitle() }}) but may run into performance issues due to how Angular checks for changes.

Property binding

// Component
imageWidth: number = 50;
<!-- Template -->
<img [style.width.px]="imageWidth" />

Event binding

// Component
toggleImage(): void {
	this.showImage = !this.showImage;
}
<!-- Template -->
<button (click)="toggleImage()">Toggle Image</button>

Two-way binding

// Component
listFilter: string;
<!-- Template -->
<input type="text" [(ngModel)]="listFilter" />

*ngIf

// Component
showImage: boolean = false;
<!-- Template -->
<img *ngIf="showImage" [src]="product.imageUrl" />

*ngFor

// Component
products: IProduct[];
<!-- Template -->
<tr *ngFor="let product of products">

Two-way binding, long way

[(ngModel)]="listFilter" is shorthand for [ngModel]="listFilter" (ngModelChange)="listFilter=$event".

So you could do [ngModel]="listFilter" (ngModelChange)="onFilterChange($event)", where onFilterChange updates listFilter.

Getters and setters

// Component
listFilter: string;
// Component
private _listFilter: string;
get listFilter(): string {
	return this._listFilter;
}
set listFilter(value: string) {
	this._listFilter = value;
}

Module 4

In my opinion, using these has more downsides than benefits.

ViewChild

// Angular Directive
@ViewChild(NgModel) filterInput: NgModel; // <input type="text" [(ngModel)]="listFilter" />
// Custom Directive or Child Component
@ViewChild(StarComponent) star: StarComponent;
// Template Reference Variable
@ViewChild('divElementVar') divElementRef: ElementRef; // <div #divElementVar>{{pageTitle}}</div>
// Above is available during/after ngAfterViewInit(), which is after constructor() and ngOnInit().
// However, if within a *ngIf you may run into an issue.
// ElementRef has a nativeElement which allows for access to any HTML element properties or methods.
@ViewChild(NgForm) editForm: NgForm; // if using template-driven forms.

With NgModel we can for example:

@ViewChild(NgModel) filterInput: NgModel;
this.filterInput.valueChanges.subscribe(() => this.performFilter(this.listFilter));

Otherwise, NgModel and NgForm are read-only.

ViewChildren

@ViewChildren(NgModel) inputs: QueryList<NgModel>;
// Above would support checking for status.
@ViewChildren(StarComponent) stars: QueryList<StarComponent>;
@ViewChildren('divElementVar' divElementRefs: QueryList<ElementRef>;
@ViewChildren('filterElement, nameElement' divElementRefs: QueryList<ElementRef>;
// Tracks changes in the DOM.
this.divElementRefs.changes.subscribe(() => { /* act */ });

Module 5

Parent to child component communication

  • Child: @Input, getters/setters, OnChanges
  • Parent: Template reference variable, @ViewChild
  • Use a service

@Input()

Child:

@Input() propertyName: string;

Parent:

<app-child propertyName="binding source"></app-child>

Or:

parentProperty: string;
<app-child [propertyName]="parentProperty"></app-child>

Getter and setter

Child component:

private _propertyName: string;
get propertyName(): string {
	rerturn this._propertyName;
}
@Input() set propertyName(value: string) {
	this._propertyName = value;
}

OnChanges

Child component:

@Input() propertyName: string;

ngOnChanges(changes: SimpleChanges): void {
	// Note: values start at undefined.
	if (changes['propertyName']) {
		changes['propertyName'].currentValue
	}
}

Template reference value

<app-child #childReference [propertyName]="parentProperty"></app-child>

{{ childReference.propertyName }}
{{ childReference.methodName() }}

@ViewChild

<app-child #childReference [propertyName]="parentProperty"></app-child>
@ViewChild('childReference') childComponent: ChildComponent;

or

<app-child [propertyName]="parentProperty"></app-child>
@ViewChild(ChildComponent) childComponent: ChildComponent;
parentPropertyName: string;

ngAfterViewInit(): void {
	this.parentVariable = this.childComponent.propertyName;
}

Summary

  1. Use a child component when:
    • for a specific task
    • complex
    • reusable
  2. @Input, getter/setter, and OnChanges are easier for parent to child
    • Favor getter/setter if you only need to react to changes to specific properties
    • Favor OnChanges if you want to react to any input property changes, or if you need current/previous values
      • The key here is that it's @Input() property changes.
  3. Template reference variable if you want to use it in the parent's template
  4. ViewChild if you want to use it in the class
    • but it won't receive notification of changes

Module 6

Child to parent component communication

  • Event notification: @Output
  • Provide information: Template reference variable, @ViewChild
  • Service

@Output

Child:

@Output() valueChange = new EventEmitter<string>(); // in @angular/core

this.valueChange.emit(value);

Parent:

<app-child (valueChange)="onValueChange($event)"></app-child>
onValueChange(value: string): void {
	// ...
}

Module 7 - Services

Managing state options

From simple to complex:

  1. Property bag
  2. Basic state management
  3. State management with notifications
  4. ngrx (inspired by Redux)

Property bag

Service that just contains properties.

Service:

@Injectable()
export class ThingService {
	propertyName1: string;
	propertyName2: boolean;
}

Component:

get propertyName1(): string {
	return this.thingService.propertyName1;
}
set propertyName1(value: string) {
	this.thingService.propertyName1 = value;
}

constructor(private thingService: ThingService) {
}

Great for 'stashing away properties for itself or other components.'

Service scope

Register the service based upon what you want to be able to use it (scope), and how long it is retained for (lifetime).

  • Register in the component - @Component({ providers: [ ThingService ]}) - for that component and children (template or via router).
    • Good if you need multiple instances of the service for different component instances.
  • Register in a module - @NgModule({ providers: [ ThingService ] }) - no matter which module it's registered in (unless lazy-loaded) it will be available to all components.
    • Lazy-loaded module services are only available to components declared in that module, but is then available for the entire application lifetime.

ngOnDestroy(): void { } if you want to see the lifetime of a service.

Guidelines

Property bag for:

  • Retaining view state
  • Retaining user selections
  • Sharing data or other state
  • Communicating state changes
  • Okay if any component can read or change the values
  • Components are only notified of state changes if they use template binding

Module 8

Basic state management

Essentially store data on the service in a private property, and populate it from the server only when needed.

  1. Provide state values
  2. Maintain and update state
  3. Observe state changes
@Injectable()
export class ThingService {
	private things: IThing[];
	
	getThings(): Observable<IThing[]> {
		if (this.things) {
			return of(this.things);
		}
		// get things from the data store and save to this.things
	}

	getThing(id: number): Observable<IThing> {
		if (this.things) {
			const foundThing = this.things.find(item => item.id === id);
			if (foundThing) {
				return of(foundThing);
			}
		}
		// get thing from the server and return it
	}
}

For create/update/delete either post to the server and update the item in the property list, or pull fresh content from the server, depending upon need.

May want to always pull fresh data when doing an edit.

You may also want to store a pull or expiration date so that you don't have stale data.

Concurrent components

Could put a public property in a property bag or state management service that could be read/updated.

In the component that reads the property, use a getter to return this.thingService.currentThing; if you want it to update every time currentThing is changed.

  • Define a property in the service
  • Bind that property in a template
  • Use a getter in the component class

Note that you'll either need to use binding in the template to have Angular pick up changes or have a timer (import { timer } from 'rxjs/observable/timer'; has one) in ngOnInit that polls for changes. Timers are not ideal.

ngOnInit() {
	timer(0, 1000).subscribe(t => console.log(this.thing));
	// unsubscribe on destroy
}

Module 9

Service notifications

Can add notifications to any service, not just a state management service.

EventEmitter only works child to parent, and you don't want to force it for service notifications.

Use Subject or a variant like BehaviorSubject instead. Subject is a type of Observable, and an Observer. Don't necessarily need, if you can use binding.

Service:

@Injectable()
export class ThingService {
	private selectedThingSource = new Subject<IThing | null>();
	// source = source of knowledge about selected thing
	selectedThingChanges$ = this.selectedThingSource.asObservable();
	// $ convention = observable (that can be subscribed to)
	// could make the source public but then anyone could push to it
	// instead the asObservable makes it read-only externally
	
	changeSelectedThing(selectedThing: IThing | null): void {
		this.selectedThingSource.next(selectedThing);
	}
}

Component, updating:

// ...
this.thingService.changeSelectedThing(thing);
// ...

Component, subscribing:

export class ThingDetailComponent implements OnInit, OnDestroy {
	thing: IThing | null;
	thingSub: Subscription;

	constructor(private thingService: ThingService) { }
	
	ngOnInit(): void {
		this.thingSub = this.thingService.selectedThingChanges$.subscribe(
			selectedThing => this.thing = selectedThing
		);
	}
	
	ngOnDestroy(): void {
		this.thingSub.unsubscribe();
	}
}

BehaviorSubject

Many variants of subject, but this one:

  • requires an initial value
  • provides the current value on a new subscription

Service:

//private selectedThingSource = new Subject<IThing | null>();
private selectedThingSource = new BehaviorSubject<IThing | null>(null);

Works even when components are destroyed. However, will want to make sure the component that updates also subscribes, if it needs to.

Summary

Don't need to use Subject if notifications are not required, or the only notifications are for changes to bound properties.

Subjects can also be used to sync multiple observables (advanced, not covered by this course).

There is a Subject variant that can provide all previous messages.

Module 10

Route parameters

  • Required
  • Optional
  • Query

Required parameters

Define:

{ path: 'products/:id', component: ProductDetailComponent }

Activate:

<a [routerLink]="['/products', product.id]">...</a>
this.router.navigate(['/products', this.product.id]);

Read:

this.route.snapshot.paramMap.get('id');

Optional parameters

In the URL, uses ; as the delimiter. Can be lost during navigation (versus standard query parameters).

Define:

{ path: 'products', component: ProductListComponent }

Activate:

<a [routerLink]="['/products', { name: cart, code: g }]">...</a>
this.router.navigate(['/products', { name: 'cart', code: 'g' }]);

Read:

this.route.snapshot.paramMap.get('name');

Query parameters

In the URL, uses standard ? and &. Can be retained across routes.

Define:

{ path: 'products', component: ProductListComponent }

Activate:

<a [routerLink]="['/products']" [queryParams]="{ name: cart, code: g }">...</a>
this.router.navigate(['/products'], { queryParams: { name: 'cart', code: 'g' }});

Read:

this.route.snapshot.queryParamMap.get('name');

Summary

  • Simple
  • Bookmarkable and sharable
  • Good for small amounts of data

Related: Creating Layouts with CSS Grid

Playing around while following Matt Henry's course on Pluralsight.

My repo from the course.

Notes

Basics

  • grid-column-start
  • grid-column-end
  • grid-template-columns
  • grid-template-rows
html, body {
	height: 100%;
}

body {
	/* Changes all direct children into grid items. */
	/* By default 1 column 1 row grid. */
	display: grid;
	/* Number of columns and width. */
	grid-template-columns: 15em auto 15em;
	/* Number of rows and heights. */
	/* min-content is minimum amount to fit content, and no more. */
	/* auto only works here because of 100% height on html and body. */
	grid-template-rows: min-content auto min-content;
	/* Above is the same as the following. rows / columns. */
	/*
	grid-template: min-content auto min-content / 15em auto 15em;
	*/
	/* Can also use the more powerful grid, with just what we want. */
	/* grid is also recommended, while grid-template is not. */
	/*
	grid: min-content auto min-content / 15em auto 15em;
	*/
}

header, footer {
	/* Grid line to start at. */
	grid-column-start: 1;
	/* Grid line to end at. */
	grid-column-end: 4;
}

Sizing

Viewport-responsive:

  • Percentage
  • fr
    • These will be min-content until all other tracks reach their growth limit (for example, minmax()).
  • auto
    • Growth limit is max-content.
    • This means if a 1fr is used it won't get any larger than max-content.

Content-responsive:

  • min-content

  • max-content

  • fit-content()

  • fr fractional unit.

  • minmax(max-content, 50%) define minimum and maximum values for a track.

  • fit-content(30em) try to fit the content, but don't go any larger than an amount.

  • repeat(3, 20em) repeats x times a track size of a certain amount.

    • repeat(6, 1fr 2fr)
  • repeat(auto-fill, 10em) fills up available space with tracks of set size.

    • repeat(auto-fill, minmax(15em, 1fr)) is an example of combining for a responsive design.
  • auto-fit tries to fill empty columns, versus auto-fill. /* Default. Fills it row-by-row. */

  • grid-auto-flow: row; is the default and tries to fill the grid row-by-row.

    • grid-auto-flow: column; fills column-by-column instead.
    • Alternative is to put it in the grid definition. For example grid: auto-flow 10em / 10em 10em.
  • writing-mode: vertical-rl; and writing-mode: horizontal-tb are examples of ways to get grid-auto-flow to start a certain way, but may also make columns look like rows.

  • grid-auto-rows can impact implicit rows created if the grid definition doesn't account for all grid items.

    • Supports more than one size, in which case it will alternate.
    • grid-auto-columns is the other.
    • Depends upon grid-auto-flow definition.

Determing track size (slide)

  1. All tracks start at their base size.
  2. Extra space is allocated evenly to tracks which haven't reached their growth limit.
  3. Additional remaining space is given to fractional unit (fr) tracks.
  4. If there are no fractional unit tracks, additional space is given to auto tracks.

Gaps

  • column-gap, row-gap, and gap (shorthand) are the recommended properties.
    • Makes it consistent with CSS Grid, Flexbox, and Multi-column Layout.
    • grid- prefix is also acceptable.
  • margin on grid container for outer margin.

Positioning

  • Can use -1 for end position (or any negative numbers) instead of a positive number. Negative numbers count from the end of the row to the start.
  • However, avoid micromanaging.
  • On a grid item, can use span x to span a certain number of tracks.
    • grid-column-end: span 2;
  • grid-column: start / end;
    • grid-column: 3 / 5;
    • grid-column: 3 / span 2;
    • grid-column: span 2;
    • grid-row
  • grid-area: 2 / 1 / 5 / 6; is grid row start, column start, row end, column end.
    • May want to just use grid-column and grid-row for readability.

Alignment

  • Can align a grid on a web page or the content within a grid cell, and there must be extra space to align within.
  • Properties:
    • justify-content default is start.
      • Other options are center, end, space-around, space-between, and space-evenly. Controls entire grid.
    • align-content default is start.
      • Other options are center, end, space-around, space-between, and space-evenly. Controls entire grid.
    • justify-items default is stretch.
      • Other options (for this and next three properties) are start, center, and end. Controls content within grid item.
    • align-items default is stretch. Controls content within grid item.
    • justify-self default is stretch. Controls an individual item.
    • align-self default is stretch. Controls an individual item.
  • Justify = left to right.
  • Align = top to bottom.

Accessibility

  • Can use dense in grid-auto-flow and it will try to fill in empty gaps (in the case of spanning grid items).
  • Can also use order to force items before/after items. Default is order: 0;.
  • However, try to keep the source order = display order.
  • Can also overlaps grid items by overlapping row/column placements. Combine with z-index as needed.

Naming

  • When defining track sizes, you can name them.
    • grid-template-columns: [left-edge] 1fr 1fr [midpoint] 1fr 1fr [right-edge];
    • Then use like grid-column: left-edge / right-edge;.
  • Something like body { display: grid; grid-template-rows: [header-start] 2em 5em [header-end body-start] 10em 10em [body-end]; } is a bit more conventional.
  • Can also use names within repeat() which can cause multiple named lines.
    • When used for grid items, this will cause them to jump to the closest named line.
    • Can put a number afte the name to use that instance.
    • Can also use span x before to span over multiple, but be careful when repeating.

Simple layouts

body {
	display: grid;
	grid-template-areas: "header header header"
						"nav main aside"
						"footer footer footer";
	grid-template-rows: min-content auto min-content;
	grid-template-columns: 15em 1fr 1fr;
}

header { grid-area: header; }
nav { grid-area: nav; }
main { grid-area: main; }
aside { grid-area: aside; }
footer { grid-area: footer; }

Good for simple layouts.

  • Must be rectangular shape.
  • Must have same number of cells.
  • ... to define empty areas.
  • Lines automatically get names based upon area names.
  • Named lines automatically create named areas.

Shorter version of the above:

body {
	display: grid;
	grid: "header header header" min-content "nav main aside" auto "footer footer footer" min-content / 15em 1fr 1fr;
}

Or to improve readability:

body {
	display: grid;
	grid: "header header header" min-content
		"nav main aside" auto
		"footer footer footer" min-content /
		15em 1fr 1fr;
}

Responsive design

  • Start small.
  • Use media queries. :|

Subgrids

  • May not be well supported (as of me typing this, still only Firefox supported).
  • You can of course put a grid within a grid.

.NET

  • dotnet new
    • dotnet new list
    • dotnet new gitignore adds a .gitignore to the current directory.
# View .NET installed versions and information.
dotnet --info
# Restore packages.
dotnet restore

dotnet watch run

Environment variables

# launchSettings.json can typically set these.
# See https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-7.0#development-and-launchsettingsjson
$Env:ASPNETCORE_ENVIRONMENT = "Development"
$Env:NETCORE_ENVIRONMENT = "Development"

The ASPNETCORE_ENVIRONMENT value overrides DOTNET_ENVIRONMENT.

Entity Framework Core Tools

CLI reference.

# Install a tool globally.
dotnet tool install --global dotnet-ef

# Update a tool to the latest version.
dotnet tool update --global dotnet-ef

# Install a specific version.
dotnet tool install --global dotnet-ef --version 8.0.10
dotnet tool install --global dotnet-ef --version 8.0.10 --allow-downgrade

# List installed tools.
dotnet tool list -g

# Search for tools matching `searchTerm`.
dotnet tool search searchTerm
dotnet tool search dotnet-ef --detail --take 1

Add SQLite

dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Design

appsettings.json

{
  "ConnectionStrings": {
    "DefaultConnection": "Data source=./Database/name.db"
  }
}

Data/DataContext.cs

using API.Models;
using Microsoft.EntityFrameworkCore;

namespace API.Data
{
    public class DataContext : DbContext
    {
        public DbSet<Thing> Things { get; set; }

        public DataContext(DbContextOptions options) : base(options) { }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
        }
    }
}

Program.cs

// Add near the top.
builder.Services.AddDbContext<DataContext>(options =>
{
    options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection"));
});

// Optional: apply migrations on startup. Add after builder.Build().
using (var scope = app.Services.CreateScope()) {
    var db = scope.ServiceProvider.GetRequiredService<DataContext>();
    db.Database.Migrate();
}

Create Initial Migration

dotnet ef migrations add InitialCreate -o Data/Migrations
dotnet ef database update
# List the last 5 migrations.
dotnet ef migrations list --no-build | Select-Object -last 5
# Create a migration for a specific context.
dotnet ef migrations add AddXEntity -o Data/Migrations/Application -c ApplicationDbContext
dotnet ef database update -c ApplicationDbContext

.NET Examples

Example: .NET API with Angular Frontend and XUnit Testing

This example sets up a new .NET webapi project with XUnit testing and an Angular frontend.

# From repo root:
dotnet new sln
dotnet new webapi -o API
dotnet sln add API
dotnet new gitignore
git init
git add .
git commit -m "Add new .NET webapi project and solution"

# XUnit project.
dotnet new xunit -o API.Tests
dotnet add .\API.Tests\API.Tests.csproj reference .\API\API.csproj
dotnet sln add API.Tests
git add .
git commit -m "Add new XUnit project"

# Create Angular application.
ng new client
git add .
git commit -m "Add new Angular application"

Project Workspace via Windows Terminal

wt -d .\ --title 'Repo Root' `; nt -d .\client\ --title 'ng serve' `; split-pane -H -d .\API\ --title 'dotnet watch run' `; nt -d .\client\src\app\ --title 'ng g ...'`; nt -d .\client\ --title 'ng test' `; split-pane -H -d .\ --title 'dotnet test'

Alternatively

# From repo root:
cd .\API\
dotnet run

# From repo root:
dotnet test

# From repo root:
cd .\client\
ng serve

# From repo root:
cd .\client\
ng test

GDScript Basics

Go

# Initialize a new module.
go mod init example.com/hello

# Run the current directory.
go run .

# Update and cleanup go.mod.
go mod tidy

# Run tests (in files that end with _test.go).
go test
go test -v

# testing.Short() returns true. Useful for t.Skip()ing long/integration tests.
go test -short

# Generate a platform-specific application.
go build

# Install the current application to the Go path.
go install

Information

# Get Go's version.
go version

# Find where the current module would be installed to.
go list -f '{{.Target}}'

# List environment information, including where packages are installed via go get.
go env

Requirements

# Update go.mod to point a module to a local directory.
go mod edit -replace example.com/greetings=../greetings

# Get an external module and add to go.mod require.
go get golang.org/x/example

# Get dependencies for code in the current directory (already added as an import).
go get .

Workspaces

# Initialize a workspace with an existing module.
go work init ./hello

# Add a child module directory to the workspace.
go work use ./example

Utilities

# Generate a TLS/SSL cert. Get GOPATH from `go env`.
go run 'C:\Program Files\Go\src\crypto\tls\generate_cert.go' --rsa-bits=2048 --host=localhost

Testing

# Get basic test coverage, per file.
go test -cover ./...

# Generate a coverage report by method and function.
go test -coverprofile='profile.out' ./...
# Read the report and output to the command line.
go tool cover -func='profile.out'
# Read the report and output to HTML.
go tool cover -func='profile.out'
# Generate a coverage report with number of times each statement is executed during testing.
# Use -covermode=atomic if running any tests in parallel.
go test -covermode=count -coverprofile='profile.out' ./...

T-SQL

Get all tables in a database.

SELECT TABLE_SCHEMA, TABLE_NAME
FROM <database_name>.INFORMATION_SCHEMA.TABLES 
WHERE TABLE_TYPE = 'BASE TABLE'
ORDER BY TABLE_SCHEMA, TABLE_NAME

Get all views in a database.

SELECT TABLE_SCHEMA, TABLE_NAME
FROM <database_name>.INFORMATION_SCHEMA.TABLES 
WHERE TABLE_TYPE = 'VIEW'
ORDER BY TABLE_SCHEMA, TABLE_NAME

Get all stored procedures in a database.

SELECT SPECIFIC_SCHEMA, SPECIFIC_NAME
FROM <database_name>.INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_TYPE = 'PROCEDURE'
ORDER BY SPECIFIC_SCHEMA, SPECIFIC_NAME

Table altering, in T-SQL

Change table schema

Change the schema on an existing table.

ALTER SCHEMA projectManagement
TRANSFER dbo.RequestPriority

Change column

Alter a column in an existing table.

ALTER TABLE [projectManagement].[Task]
ALTER COLUMN Name varchar(250) not null

Add identity and primary key

Add a new identity column to an existing table.

-- See http://stackoverflow.com/a/3698824/11912
ALTER TABLE JobSupplies add Id INT IDENTITY
ALTER TABLE JobSupplies add constraint PK_JobSupplies primary KEY(Id)

PostgreSQL

Unless otherwise noted, non-SQL commands are running under Ubuntu.

# On Ubuntu, run psql as default postgres user.
sudo -u postgres psql

# List all databases.
sudo -u postgres psql -l
-- List all databases.
\l

-- Quit psql.
\q

Configuration

# Show the full path to the PostgreSQL configuration file.
sudo -u postgres psql -c 'SHOW config_file'
# Example: /etc/postgresql/12/main/postgresql.conf

# Show the full path to the HBA configuration file.
sudo -u postgres psql -c 'SHOW hba_file'
# Example: /etc/postgresql/12/main/pg_hba.conf
-- Locate the HBA configuration file.
SHOW hba_file;
-- Example: /etc/postgresql/12/main/pg_hba.conf

-- Or just query the file for rules.
select * from pg_hba_file_rules();

User management

-- List all users.
\du

-- Create a new user.
CREATE USER <name>;

Database management

# Create a new database.
sudo -u postgres createdb <name>
# Verify it was created.
sudo -u postgres psql -l

# Create a new database, echoing out the commands run.
sudo -u postgres createdb <name> -e

# Drop a database.
sudo -u postgres dropdb <name>

SQLite

For a CLI on Windows, download sqlite-tools-win32-x86-___.zip

Import a tsv file into a new database.

This assumes the tsv has a header row.

.open files.sqlite3
.mode tabs
.import files.tsv files

Node

Upgrade npm

npm install npm@latest -g

View versions of a package

npm view package-name versions

Global Packages

Find all globally installed packages

npm ls -g --depth 0

Install/update a package globally

npm install http-server -g

Find outdated global packages

npm outdated -g --depth=0

Auditing

Find any production packages with vulnerabilities. Id est, ignore any development packages.

npm audit --production

Determine why a package is required.

npm explain package-name
npm why package-name
npm ls package-name

My Node Globals

The following is a list of globals I tend to install with Node. Items are listed in order of relative importance, and then alphabetically.

npm install ___ -g
npm install -g ___

Again, you can run npm ls -g --depth 0 to view any packages that have been installed globally.

Node Utilities

  • npm-check-updates

General Utilities

  • http-server

TypeScript

  • typescript
  • eslint
  • typedoc
  • dts-gen

Frameworks

  • @angular/cli
  • @ionic/cli
  • @vue/cli
  • create-react-app
  • gulp-cli

Visual Studio Code Extension Development

  • yo
  • generator-code

PowerShell

List all files with a particular extension in the current directory and its children, with results sorted by full file name.

Get-ChildItem -Path .\ -Filter *.sln -Recurse -File | Select Fullname | Sort-Object Fullname

List the 10 largest files in the current directory and subdirectories.

gci -r | sort Length -desc | select @{n="Length";e={$_.length}}, fullname -f 10

Search Files.ps1

Get-ChildItem -Recurse -Include *.item | select-string "<term>"
gci -r -i *.item | select-string "<term>"
gci -r -i *.* -exclude *.dll,*.xml | select-string "<term>"

Search with Git Grep.ps1

# Can only be run within a Git repository, but this will also search untracked files, as well as those that are tracked.
git grep --untracked '<term>'

View a file's contents

Get-Content .\path\to\file.ext

Find path of a binary.

where.exe git.exe

Run git push on all child folders of the current directory.

gci -Directory | % { Push-Location $_.FullName; git push; Pop-Location }
Get-ChildItem -Directory | foreach { Push-Location $_.FullName; git push; Pop-Location }

Environment variables

Get all environment variables.

dir Env:

Get environment variables at a certain scope.

[System.Environment]::GetEnvironmentVariables('User')
[System.Environment]::GetEnvironmentVariables('Machine')

Get a particular environment variable.

[Environment]::GetEnvironmentVariable("NAME_OF_VARIABLE")
[Environment]::GetEnvironmentVariable("NAME_OF_VARIABLE", "Machine")
[Environment]::GetEnvironmentVariable("NAME_OF_VARIABLE", "User")

Set an environment variable

$Env:NAME_OF_VARIABLE = "value"

Remove an environment variable

Remove-Item Env:\NAME_OF_VARIABLE

History

View where PowerShell's history is saved to.

(Get-PSReadlineOption).HistorySavePath

View session error information

Get-Error -newest 2
Get-Error -last 2
# Names only
$Error

PowerShell Profile

Refresh PowerShell Profile

. $profile

My Windows PowerShell Profile

Import-Module Terminal-Icons
Import-Module PSReadLine

Set-PSReadLineOption -PredictionSource History

oh-my-posh init pwsh --config ~/OneDrive/Apps/oh-my-posh/aritraroy.omp.json | Invoke-Expression
$env:POSH_GIT_ENABLED = $true

function Set-Title {
<#
.Description
Set-Title sets the window/tab title, such as for Windows Terminal.
#>
	param(
		[Parameter(Mandatory = $true)]
		[string]
		$title
	)
	$Host.UI.RawUI.WindowTitle = $title
}

function Set-Title-Folder {
<#
.Description
Set-Title-Folder sets the window/tab title, such as for Windows Terminal, based on the current folder.
#>
	$Host.UI.RawUI.WindowTitle = Split-Path -Path (Get-Location) -Leaf
}

function Start-ExtensionOpen {
<#
.Description
Start-ExtensionOpen opens a single file with a matching extension, if possible.
#>
	param(
		[string]
		$FileExtension
	)
	$matchingFiles = Get-ChildItem . -Filter *.$FileExtension
	$matchCount = ($matchingFiles | Measure-Object).Count

	if ($matchCount -eq 1) {
		Write-Output "Opening $($matchingFiles.Name)"
		Invoke-Item $matchingFiles[0]
	} elseif ($matchCount -eq 0) {
		Write-Error "No matching files for $($FileExtension)"
	} else {
		Write-Output $matchingFiles.Name
	}
}

Docker

Unless otherwise noted, commands run on Ubuntu 22.

Setup

# Add the current user to the docker group so docker can be run without sudo.
sudo usermod -aG docker ${USER}
su - ${USER}
exit

Basics

# Get installed version.
docker -v

# Verify Docker is setup / run the Hello World image.
sudo docker run hello-world

# Check whether Docker is running.
sudo systemctl status docker
sudo systemctl is-active docker

# Search Docker Hub for all images matching a search term (<term>).
docker search <term>

# Show all downloaded images.
docker images

# Remove an image.
docker rmi <image-id>

# View information about an image.
docker inspect <image-name>
# Get the size of an image from the above.
docker inspect -f "{{ .Size }}" <image-name>

Image Tags

# Give an image/tag an additional tag.
docker tag <image-name>:<tag-name> <image-name>:<another-tag-name>

# These extra tags can then be removed, while keeping the image.
docker tag <image-name>:<another-tag-name>

Containers

# List running containers.
docker ps
# List all containers.
docker ps -a

# Start a container with <container-id> or <container-name>.
docker start <container-id>
docker start <container-name>

# Stop a container.
docker stop <container>

# Restart a container.
docker restart <container>

# Remove/delete a container.
docker rm <container>

# View container's logs.
docker logs <container>

Working with Running Containers

docker cp path/to/file <container>:./destination/path/

# Run bash on the container, if installed.
docker exec -it <container> bash

System

# Show disk usage.
docker system df
docker system df --verbose

# Show active container stats.
docker stats

# View and prune images.
docker image ls --filter dangling=true
docker image prune

Compose

# Stop based upon docker-compose.yml in directory.
docker compose stop

# Start services based upon docker-compose.yml.
docker compose up -d

# View the config of a docker-compose.yml in current directory.
docker compose config

Install on Ubuntu

On Ubuntu, from https://docs.docker.com/engine/install/ubuntu/:

sudo apt-get update
sudo apt-get install ca-certificates curl gnupg

sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt-get update

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Test.
sudo docker run hello-world

Windows Terminal

Open a new Windows Terminal window.

wt

Open a new tab in the current window.

wt -w 0

Visual Studio

Install particular NuGet package.ps1

# Installs a particular version of a package.
# See http://stackoverflow.com/q/16126338/11912
Install-Package jQuery -Version 1.10.2

Team Foundation Server

Last reviewed around November 2013.

Delete workspace for user.bat

REM Run from VS developer command prompt
tf workspace /delete _workspace_;_domain_\_user_ /server:http://_server_:8080/tfs
PAUSE

List all workspaces.bat

REM Run from VS developer command prompt
tf workspaces /server:http://_server_:8080/tfs /owner:*
PAUSE