Monday, May 23, 2022

No provider for ControlContainer!

As you can probably deduce from the title, this post is all about an annoying error in Angular that reads "No provider for ControlContainer!". If you're just getting this error in general, the first thing you should do is make sure you've imported the FormsModule and ReactiveFormsModule (yes, both of them). That will probably clear it up for you. If you've done that and you're still getting this error, specifically while running your unit tests, I have a solution for you. I'm nearly 100% certain I didn't come up with this on my own, but since I'm not sure where I originally found it I can't link you over to it. Sorry for that.

First off, let me show you the code that triggered this issue when I started testing. Like a good programmer following DRY (Don't Repeat Yourself) I will often componentize even small things that are reused and require some bit of setup or configuration. For instance, when creating forms that require masked input fields (like phone number or credit card) I'll create a component that allows me to quickly and easily drop that into my form and just specify which mask to use. The way I do that requires me to inject ControlContainer directly into the component. Here's some example code:

import { Component, Input, OnInit } from '@angular/core';
import { ControlContainer, FormControl, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-masked-input'
  templateUrl: './masked-input.html'
})
export class MaskedInputComponent implements OnInit {
  @Input() controlName: string;
  @Input() label: string;
  @Input() mask: any;

  constructor(public controlContainer: ControlContainer) { }

  public ngOnInit(): void {
    // Set our form property to the parent control
    // (i.e. FormGroup) that was passed to us, so that our
    // view can data bind to it
    this.form = this.controlContainer.control as FormGroup;
    this.control = this.form.get(this.controlName) as FormControl;
  }
}

That's the relevant part to this error. If we try to run unit tests around that code we'll get the error "No provider for ControlContainer!" even if we import FormsModule and ReactiveFormsModule in our test file. To fix that error, we have to manually create a FormGroupDirective in our test file and change the provider for ControlContainer to use that FormGroupDirective. Here are the relevant bits of code:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ControlContainer, FormControl, FormGroup, FormGroupDirective, FormsModule, ReactiveFormsModule } from '@angular/forms';
...snip...
describe('MaskedInputComponent'), () => {
  let component: MaskedInputComponent;
  let fixture: ComponentFixture<MaskedInputComponent>;
  const formGroup: FormGroup = new FormGroup({
    dynamicControlName: new FormControl('')
  });
  const formGroupDirective: FormGroupDirective = new FormGroupDirective([], []);
  formGroupDirective.form = formGroup;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ MaskedInputComponent ],
      imports: [ FormsModule, ReactiveFormsModule ],
      providers: [ { provide: ControlContainer, useValue: formGroupDirective } ],
    })
    compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(MaskedInputComponent);
      component = fixture.componentInstance;
      component.controlName = 'dynamicControlName';
      fixture.detectChanges();
    })
    compileComponents();
  });
});

That's it! This will alleviate the "No provider for ControlContainer!" error in your test run. It's simple enough once you know what to do, but it was a pain figuring it out. Hope this helps.

Friday, March 25, 2022

rxjs EMPTY

I know the blog is technically called "Answers I Couldn't Find Anywhere Else" and I did find this answer somewhere else, but it's a very valuable answer that I want to keep for future reference. 

This other blog helped me understand what was going on with my unit tests and why forkJoin wasn't being triggered when I used EMPTY.

The TL;DR of it is that EMPTY immediately emits a complete without ever emitting a value, but forkJoin fires when all observables emit their value. Since EMPTY never emits a value, forkJoin never fires. Simple as that.

Friday, February 18, 2022

Printing with Angular

I'm in the middle of migrating an older project from Angular 7 to Angular 12. My team made a decision to start from scratch and build the application up using good patterns and practices this time (the original project had to be thrown together rather quickly to meet a business deadline; so it goes). This week I arrived at a piece of code intended to print a receipt. The receipt itself is an isolated component that is typically (but not always) displayed in a modal (we use ng-bootstrap). In the old version we have a rather sloppy (but effective) piece of code that opens a new window and writes the HTML contents of the receipt into the new window, then immediately invokes the print function. It does work, but man is it unseemly. At first I tried to just migrate that code over, but the newer version of TypeScript didn't like some things about it (and I didn't spend much time trying to figure out what it didn't like). For posterity, here's the old code.


print() {
  let printContents, popupWin;
  printContents = document.getElementById('print-section').innerHTML
  printContents = `<div div class="text-center mb-4 mt-4"><img src="https://logo.url" class="receipt-logo"></div>${printContents}`;
  popupWin = window.open('/', '_blank', 'top=0,left=0,height=100%,width=auto');
  popupWin.document.open();
  popupWin.document.write(`
    <html>
      <head>
        <title>Receipt</title>
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">
        <style>
          @page {
            size: auto;   /* auto is the initial value */
            margin: 5%;
          }
          body {
            padding-top: 50px;
          }
          .btn {
            display: none;
          }
          .text-primary {
            color: #333333;
          }
          .receipt-logo {
            width: 200px;
          }
        </style>
      </head>
      <body onload="window.print();setTimeout(function() { window.close(); }, 500); // Timout is Safari hack. Safari dosn't wait for print to close window">${printContents}</body>
    </html>
  );
  popupWin.document.close();
}

I want to stress once more that this absolutely does work, but... well, just look at it. It's unpleasant. Since we're taking the time to rewrite the entire application I wanted a better way to do this, but Angular sure didn't make it easy.

The TL;DR of what I did is pretty simple: I created a component that injects another component into a directive that sits as a sibling to my root component.

I'll first acknowledge that what I'm doing here is so common that the Angular docs actually have a tutorial on how to do it, which you can find here. I had to make some modifications to that code to make it work for me, which I based on a previous blog post I created, which you can find here.

Now for the details. There are a few factors I had to consider.
  1. The component's contents must be printable while the component is hosted in a modal and while it is hosted outside a modal
  2. I don't want to reload the entire contents of the receipt just so I can print it, but I'm OK with using data that's in state (which is how the application is constructed) to generate new HTML
  3. I want to be able to control what is printed on the receipt, and potentially have that be different than what is displayed in the UI
My app.component is pretty basic, but here's what it looked like before I added in my print stuff:

template: `<router-outlet></router-outlet><app-footer></app-footer>`

Side note: within the <router-outlet> the very first component will always be <app-wrapper>. I did that because I have a public section and a secure (behind a login) section of my app. Each of those sections has a different wrapper, but both use the <app-wrapper> selector.

The first thing I need is a directive that will host my receipt component.

import { Directive, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[appPrint]'
})
export class PrintDirective {
  constructor(public viewContainerRef: ViewContainerRef) { }
}

Next, I need a service so I know which component will be printed (this could be eliminated if you only always want to print the same component, but I already know that I'll have another component down the road that will need to be printed as well).

import { Injectable, Type } from '@angular/core';

import { BehaviorSubject } from 'rxjs';
import { v4 as uuidv4 } from 'uuid';

import { PrintableComponent } from '../../../data/models';

@Injectable({
  providedIn: 'root'
})
export class PrintStateService {
  public printNotifier: BehaviorSubject<string | null> = new BehaviorSubject<string | null>(null);
  public type?: Type<PrintableComponent>;

  public clear(): void {
    this.type = undefined;
    this.printNotifier.next(null);
  }

  public set(type: Type<PrintableComponent>): void {
    this.type = type;
    this.printNotifier.next(uuidv4());
  }
}

Now that I have the directive and the service I need the component to actually instantiate the component and plug it into the directive.

import { AfterViewInit, Component, ComponentFactoryResolver, OnDestroy, ViewChild, ViewEncapsulation } from '@angular/core'

import { Subject } from 'rxjs'
import { takeUntil } from 'rxjs/operators';

import { PrintStateService } from '../../../core';
import { PrintDirective } from '../../directives/print.directive';


  
@Component({
  selector: 'app-print',
  template: '<ng-template atlasPrint><ng-template>',
  styleUrls: ['./print-component.scss'],
  encapsulation: ViewEncapsulation.None
})
export class PrintComponent implements AfterViewInit, OnDestroy {
  private takeSubscriptionsUntil: Subject<void> = new Subject();

  @ViewChild(PrintDirective, {static: true}) printHost!: PrintDirective;

  constructor (
    private componentFactoryResovler: ComponentFactoryResolver,
    private printStateService: PrintStateService
  ) { }

  public ngAfterViewInit(): void {
    this.printStateService.printNotifier.pipe(takeUntil(this.takeSubscriptionsUntil))
      .subscribe((notificationId: string | null) => {
        if (!!notificationId) {
          this.loadComponent();
        } else {
          this.printHost.viewContainerRef.clear();
        }
      })
  }

  public ngOnDestroy(): void {
    this.takeSubscriptionsUntil.next();
    this.takeSubscriptionsUntil.complete();
  }

  private loadComponent(): void {
    if (!this.printStateService.type( {
      return;
    }

    const viewContainerRef = this.printHost.viewContainerRef;
    viewContainerRef.clear();

    const factory = this.componentFactoryResolver.resolveComponentFactory(this.printStateService.type);
    const componentRef = viewContainerRef.createComponent(factory);
    componentRef.instance.isPrinted = true;

    // this is wrapped in a setTimeout because not doing that causes the data to not be loaded into the injected component
    // so basically wrapping it like this gives the lifecycle the necessary order to populate the injected component so there's
    // actually something to display in the print preview window
    setTimeout(() => window.print(), 1);
  }
}

A little bit of styling on the print component allows us to hide everything except the print component when printing is invoked.

@media screen {
  app-print {
    display: none !important;
  }
}

@media print {
  .modal, .modal-backdrop, .app-wrapper, .app-footer, button {
    display: none !important;
  }
}

Once I have all of that in place I can modify the app.component to use the print component I created in the previous step.

template: `<router-outlet></router-outlet><app-print></app-print><app-footer></app-footer>`

The only thing left now is to tell the print button on the receipt component to load the receipt component anew into the print component and trigger the browser's print functionality.

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

import { Subject } from 'rxjs'
import { takeUntil } from 'rxjs/operators';

import { OrderStatuses, OrderTypes, PaymentTypes } from '../../../data/enums';
import { LoadingStateService, PrintStateService, ReceiptStateService } from '../../../core';
import { PrintableComponent, Receipt } from '../../data/models';


  
@Component({
  selector: 'app-receipt',
  templateUrl: './receipt.component.html',
  styleUrls: ['./receipt-component.scss']
})
export class ReceiptComponent implements OnInit, OnDestroy, PrintableComponent {
  @Input() isModal: boolean = false;
  @Input() isPrinted: boolean = false;

  private takeSubscriptionsUntil: Subject<void> = new Subject();

  public receipt: Receipt | null | undefined;
  public isLoading = false;
  public OrderStatuses: typeof OrderStatuses = OrderStatuses;
  public OrderTypes: typeof OrderTypes = OrderTypes;
  public PaymentTypes: typeof PaymentTypes = PaymentTypes;

  constructor (
    private printStateService: PrintStateService,
    private receiptStateService: ReceiptStateService
  ) { }

  public ngOnInit(): void {
    this.receiptStateService.stateChanged.pipe(takeUntil(this.takeSubscriptionsUntil))
      .subscribe((state: StoreState) => state?.receipt);

  }

  public ngOnDestroy(): void {
    this.takeSubscriptionsUntil.next();
    this.takeSubscriptionsUntil.complete();
  }

  public print(): void {
    this.printStateService.set(ReceiptComponent);
  }
}

Oops, I forgot to show what the PrintableComponent interface looks like. Here it is.

export interface PrintableComponent {
  isPrinted: boolean;
}

Et voila! Clicking the print button on the receipt pushes a new instance of the receipt component into the print directive on the print component, then triggers the browser's print functionality through window.print. Pretty neat!

Thursday, December 9, 2021

Linq DistinctBy

I've had to search for this quite a few times so I figured it was probably time to write it up. I actually did find this answer somewhere else (here, if you're interested) and modified it only slightly to follow my standards.

As I often (somehow) forget, the default .Distinct() extension in System.Linq for an IEnumerable just checks to see if objects are the exact same as each other. You may recall in C# that being the exact same means the two objects are actually references to the same exact object. I can say with a good amount of confidence that in 16+ years of working in C# that's never actually been what I'm trying to do when I use .Distinct(). I'd say my most common usage is determining whether two objects have the same data on them - usually one specific field (like an ID field, for example). There are Stack Overflow posts and extension libraries out there that include this, but I'm pretty loathe to bring in a whole library for one simple extension method. Which brings us to today.

I'm using Dapper to get a list of objects from the database and then using the splitOn feature to get the children of those objects. Think of a teacher and students: I'm getting all the teachers in the school and then all of each teacher's students in a single query, then I want to break the students out according to their teachers by using the splitOn feature of Dapper. That's no problem and doesn't even require me to use .Distinct(). If I also include the clubs that each teacher oversees, I could easily end up with duplicate students in my results. The easiest way to get the distinct students in my results would be to use the .Distinct() extension method included in System.Linq, if only that worked the way it seems like it should. Instead, I'll have to write my own. So here we are.


   1: public static IEnumerable<T> DistinctBy<T>(this IEnumerable<T> list, Func<T, object> propertySelector) where T : class
   2: {
   3:   return list.GroupBy(propertySelector).Select(x => x.First());
   4: }

That's it, really. The somewhat obvious flaw is that we'll take the first match we find, but if you're looking for distinct objects, that really shouldn't be too big of a deal. Hopefully this helps you (even if "you" are really just future me).

Wednesday, December 1, 2021

Removing A Single Commit From Master

I was asked today how you would go about rolling back a single commit in git, but keeping the commits that came after that one. It turned out to be pretty easy* so I figured I'd write it up for future me to refer back to.

To set the stage, I created a new directory and ran git init on it, then added nine text files to it that were all blank. For simplicity I just named them First.txt, Second.txt, etc. All nine files were committed in the first commit after init. I then edited each file, adding some arbitrary text in, and committed after each file. I added "some text goes here" to First.txt, saved it, and committed that change, then repeated that process nine times. That gave me a total of 10 commits (the initial commit and then one for each change to a file).

I reviewed my commits by running git log --oneline (the --oneline parameter just shows a summary view instead of the full log) to find the commit I wanted to skip, deciding on skipping the changes to Third.txt, which is commit b8b2f8c. Since that's the one I want to skip, I'm actually going to need to get the previous commit ID to use in my rebase, which is 5b124e0

Now that I know which commit I'm going to skip I'm ready to use an interactive rebase to drop it from the history.
  • git rebase -i 5b124e0
  • <text editor launches displaying all commits beginning with the one I want to drop in ascending order>
  • change "pick" in the first line (pick b8b2f8c <commit message>) to "drop"
  • save
  • close text editor
That's it! Since none of the subsequent commits touched Third.txt and only Third.txt was affected by the commit I dropped, there were no conflicts. If I look at my commit log now I see it has all of the commits except b8b2f8c and I can check Third.txt to see that it is empty (as it was after the initial commit).

That's the easy case. What happens when you have subsequent commits that have touched the same files as the commit you're trying to drop? You're going to have to manually merge them in. Using the same setup as before, I made additional changes to Third.txt and created a new commit for it (941a0d1). Then I went through the same steps as above, but this time instead of getting a nice friendly message about how everything worked I get the following:
Auto-merging Third.txt
CONFLICT (content): Merge conflict in Third.txt
error: could not apply 941a0d1... <commit message>
hint: Resolve all conflicts manually, mark them as resolved with
hint: 'git add/rm <conflicted_files>', then run 'git rebase --continue'.
hint: You can instead skip this commit: run 'git rebase --skip'.
hint: To abort and get back to the state before 'git rebase', run 'git rebase --abort'.
Could not apply 941a0d1... <commit message>
When I open up Third.txt I can see that there's a merge conflict that I can clear up. I remove the unwanted changes and leave in the changes from my last commit, save and close. Now I run git add -rm "Resolve conflict in Third.txt" and then git rebase --continue and I'm done! I can see that the commit I wanted to drop is gone, but everything else is there. The only difference is that now the latest commit is my new commit message "Resolve conflict in Third.txt" instead of the original commit message I had in there and the SHA1 (commit ID) of that latest commit has changed (it is no longer 941a0d1).

* This is easy and straightforward if none of the files after the commit you're dropping are also affected by the commit you're dropping. If that's the case, things get a bit messier, but it's still doable.

Wednesday, November 3, 2021

Angular Forms: setValue, setValidators, and updateValueAndValidity

This one was a doozy. When working with Reactive Forms in Angular you may find yourself changing the values and/or validators of controls on the form based on some user input. Doing so is a pretty simple operation. Angular provides built-in functions called setValue and setValidators, whose names are hopefully self-explanatory.

In my case I have a payment form where the payment type can be cash, check, or credit card and each payment type requires different data. When the user changes from credit card to check we don't need to require the card number field any longer. Makes sense, right? Using the built-in functions, this should just be a simple matter of calling setValidators on the credit card and check fields and moving on.

this.checkoutForm.controls.address.setValidators(null);
this.checkoutForm.controls.cardNumber.setValidators(null);
this.checkoutForm.controls.city.setValidators(null);
this.checkoutForm.controls.cvvCode.setValidators(null);
this.checkoutForm.controls.expirationMonth.setValidators(null);
this.checkoutForm.controls.expirationYear.setValidators(null);
this.checkoutForm.controls.firstName.setValidators(null);
this.checkoutForm.controls.lastName.setValidators(null);
this.checkoutForm.controls.postalCode.setValidators(null);
this.checkoutForm.controls.state.setValidators(null);

this.checkoutForm.controls.checkName.setValidators(Validators.required);
this.checkoutForm.controls.checkNumber.setValidators(Validators.required);

After running this code, you'd expect address, cardNumber, city, etc. to no longer be required and checkName and checkNumber to be required. At least, that's what I'd expect. It turns out there's one more step. We have to tell Angular to update the value and validity of each of those controls whose validators were changed. This is another easy one and it uses a built-in function again, called updateValueAndValidity.

this.checkoutForm.controls.address.updateValueAndValidity();
this.checkoutForm.controls.cardNumber.updateValueAndValidity();
this.checkoutForm.controls.city.updateValueAndValidity();
this.checkoutForm.controls.cvvCode.updateValueAndValidity();
this.checkoutForm.controls.expirationMonth.updateValueAndValidity();
this.checkoutForm.controls.expirationYear.updateValueAndValidity();
this.checkoutForm.controls.firstName.updateValueAndValidity();
this.checkoutForm.controls.lastName.updateValueAndValidity();
this.checkoutForm.controls.postalCode.updateValueAndValidity();
this.checkoutForm.controls.state.updateValueAndValidity();

this.checkoutForm.controls.checkName.updateValueAndValidity();
this.checkoutForm.controls.checkNumber.updateValueAndValidity();

This makes Angular aware that the validators have changed and reevaluates the validity of each control (and the form itself). This is all fine so far. Next we have a requirement that when the user changes from credit card to check we want to clear the credit card information and vice versa. No problem. We'll just use the setValue function.

this.checkoutForm.controls.address.setValue(null);
this.checkoutForm.controls.cardNumber.setValue(null);
this.checkoutForm.controls.city.setValue(null);
this.checkoutForm.controls.cvvCode.setValue(null);
this.checkoutForm.controls.expirationMonth.setValue(null);
this.checkoutForm.controls.expirationYear.setValue(null);
this.checkoutForm.controls.firstName.setValue(null);
this.checkoutForm.controls.postalCode.setValue(null);
this.checkoutForm.controls.state.setValue(null);

Simple and straightforward again, right? I think so. To recap, we're updating the validators, updating the values, and letting Angular know that we did that. Cool. When I wrote some unit tests against this code, I found something very weird. I wrote a test to expect updateValueAndValidity to have been called one time for each control. But the test failed because updateValueAndValidity was being called twice for each control. But that doesn't make any sense at all. I'm only calling it once. I spent hours trying to figure this out, with all kinds of console.log statements in my code until I finally realized that updateValueAndValidity was being called immediately after I called setValue.

This was really confusing for me because the way I learned to do this was that you called setValidators, setValue, then updateValueAndValidity and went on your way. I had even previously written unit tests for this exact sequence of steps and they all passed. So what gives!? I wrote my tests slightly differently this time, which exposed the issue. This time when I spied on checkoutForm.controls.address.setValue, I specified .and.callThrough(), which means keep an eye on it, but let it happen the way it always would anyway. In the past I had always just spied on it, which prevents it from calling through the way it normally would, thus hiding that updateValueAndValidity was being called twice.

That's right, it turns out setValue actually calls updateValueAndValidity for us, but setValidators doesn't. When you really stop to think about it, that makes perfect sense. Just because you updated the validators doesn't mean you want to check the validity of the the controls immediately. You may want to wait until something else happens. My confusion had to do with the way I learned to use these three functions to modify form controls on the fly. Hopefully this helps you (or better yet, future me) at some point.


BONUS!!!

You don't actually have to setValue and updateValueAndValidity on every single control on the form. You can invoke patchValue and updateValueAndValidity directly on the form to make your code cleaner. Here's how my code ended up looking using those.

this.checkoutForm.patchValue({
  address: null,
  cardNumber: null,
  city: null,
  cvvCode: null,
  expirationMonth: null,
  expirationYear: null,
  firstName: null,
  lastName: null,
  postalCode: null,
  state: null
});

Angular source: https://github.com/angular/angular/blob/e49fc96ed33c26434a14b80487dd912d8c76cace/packages/forms/src/model.ts

Reference for patchValue vs. setValue: https://ultimatecourses.com/blog/angular-2-form-controls-patch-value-set-value

Friday, October 1, 2021

Checking Authentication Without The Authorize Attribute

I wrote a post a couple of years ago about securing an endpoint with an API key that is generated dynamically. I recently came across a scenario where I wanted to use the same endpoint for authenticated requests and non-authenticated requests. That is, regardless whether someone is logged into my app or they've used a valid application to create their request, I want the same endpoint to service that request.

That set me down the path of trying to figure out how to check whether the request is authenticated without using the [Authorize] attribute on the controller class or the action method. It turned out to be pretty easy and testable, but it took me a while to find it.

We can check that User (which is the ClaimsPrincipal of the HttpContext of the controller) is not null and then - if it isn't null - we can check whether the IsAuthenticated property of the Identity property of the User is true. That's it. Just a couple of simple checks to tell us whether the request is authenticated.

if (User == null || !User.Identity.IsAuthenticated) {/*Request is not authenticated*/}

Hopefully this helps you (or future me) down the road.