// material-components.module.ts
const modules = [
CommonModule,
MatAutocompleteModule,
MatButtonModule,
MatCardModule,
MatDialogModule,
MatFormFieldModule,
MatInputModule,
MatListModule,
MatSortModule,
MatTableModule
];
@NgModule({
imports: modules,
exports: modules
})
export class MaterialComponentsModule { }
// home.component.ts
@Component({
selector: 'app-home',
standalone: true,
imports: [MaterialComponentsModule],
templateUrl: './home.component.html',
styleUrl: './home.component.scss'
})
export class HomeComponent {
Home Dashboard
This web application is hosted on my local IIS server and is displayed on a Raspberry Pi w/ touchscreen. It’s 100% custom, using Angular Material with a dotnet backend. It communicates with several IoT APIs, including:
- Philips Hue
- Samsung SmartThings
- Hydrawise
- OpenMeteo
- Roku
Repeat an observable in Angular (RXJS)
interval(1000).subscribe(() => {
this._getLatest().subscribe(latest => {
//...
});
});
Lego Deals
This application displays the current prices of Lego sets at major retailers. Every hour, a scheduled task scrapes 29 retailer websites for their discounted Lego sets, and those prices are saved to a database. I started collecting the hourly price history in December 2022 and it continues to run today. I’m able to set alerts via Telegram when the price of a particular set drops below a given threshold. The application also keeps track of which sets I already own, which ones I am interested in, and which ones I never want to see. I can switch to a view that only shows sets I care about.
Lego set details, like number of pieces, are pulled from Rebrickable’s daily dataset. Once per day, a scheduled task downloads the latest Rebrickable dataset and adds the new entries to my database. MSRP is scraped from the Lego website and the BestBuy API. BestBuy just happens to have the best API of all the retailers. And it’s FREE!
Another page in the application displays Ebay buy-it-now, free shipping listings. Due to inconsistency with Ebay data, these prices are not tracked in my database. Ebay deals are displayed on a totally separate page from the retailer listings; however, the Ebay page can be filtered by the same criteria.
The backend, dotnet API and Angular application are hosted on a local IIS server. Several scheduled tasks, like MSRP updates, Rebrickable datasets, alerts, etc. run on the same server. The MySQL database is hosted on Bluehost.
New angular app is missing environment.ts and environment.prod.ts
The ng new
command no longer automatically includes environments. You must run the following:
ng g environments
Angular pipe to replace characters in a string
Generate a new pipe using the Angular CLI
ng g pipe pipes/replace
Code inside the new pipe (pipes/replace.pipe.ts)
...
transform(input: string, from: string, to: string): string {
const regEx = new RegExp(from, 'g');
return input.replace(regEx, to);
}
Usage
// page.component.html
// replaces underscores with spaces
...
<span>{{ myString | replace:'_':' '}}</span>
Mock a non-singleton Angular service in a component unit test
describe('MyComponent', () => {
let mockService: jasmine.SpyObj<MyService>;
beforeEach(async () => {
mockService = jasmine.createSpyObj<MyService>(['download']);
await TestBed.configureTestingModule({
...
}).overrideComponent(MyComponent, {
set: {
providers: [
{ provide: MyService, useValue: mockService }]
}
}).compileComponents();
beforeEach(() => {
...
});
it('should create', () => {
...
});
});
Add theme switching to Angular Material
// styles.scss
$dark-theme: mat.define-dark-theme(
vars.$primaryPalette,
vars.$accentPalette,
vars.$warnPalette);
$light-theme: mat.define-light-theme(
vars.$primaryPalette,
vars.$accentPalette,
vars.$warnPalette);
.light-theme {
@include mat.all-component-themes($light-theme);
}
.dark-theme {
@include mat.all-component-themes($dark-theme);
}
// app.component.ts
constructor(
private _settingsService: SettingsService,
@Inject(DOCUMENT) private _document: Document) { }
ngOnInit(): void {
this._settingsService.theme$.subscribe(theme => {
if (theme === Themes.DARK) {
this._setDarkTheme();
} else {
this._setLightTheme();
}
});
}
private _setDarkTheme(): void {
this._document.documentElement.classList.add(Themes.DARK);
this._document.documentElement.classList.remove(Themes.LIGHT);
}
private _setLightTheme(): void {
this._document.documentElement.classList.add(Themes.LIGHT);
this._document.documentElement.classList.remove(Themes.DARK);
}
This is just a summary, so some of the more trivial parts are omitted.
ref: https://octoperf.com/blog/2021/01/08/angular-material-multiple-themes
Custom Angular Material theme generator
HTTPS redirect on web server
create .htaccess
file at public_html
root
# WHERE domain = the domain
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{SERVER_PORT} 80
RewriteCond %{HTTP_HOST} ^(www\.)?domain\.com
RewriteRule ^(.*)$ https://www.domain.com [R,L]
RewriteBase /
</IfModule>
Pass and retrieve data in Mat-Dialog
// Pass data to Dialog
constructor(
private _dialog: MatDialog
) {}
someFunction(whatever) {
let dialogConfig = new MatDialogConfig();
dialogConfig.data = {
whatever: whatever,
};
this._dialog.open(
MyDialog,
dialogConfig
);
}
// Retrieve within the dialog
constructor(
@Inject(MAT_DIALOG_DATA) private _inputParams: any
) {}
ngOnInit(): void {
this.whatever = this._inputParams.whatever;
}
Update global npm packages
// Updating all globally-installed packages
npm update -g
// or
npm install npm@latest -g
// Determining which global packages need updating
npm outdated -g --depth=0
// Updating a single global package
npm update -g <package_name>
list all globally installed npm modules
npm ls -g --depth 0