Picture of Brian Love wearing black against a dark wall in Portland, OR.

Brian Love

Console Debugging with `window.ng`

Angular 9 provides improved debugging in the console using window.ng.

window.ng

The new window.ng global enables developers to query for and access directives, components, and view containers in your browser’s devtools console with Angular version 9 and the new Ivy compilation and rendering pipeline.

Just to be clear, the prerequisites to use this feature are:

Demo

ng.getComponent() and ng.markAsDirty() demo

ng.getComponent()

To get started, use ng serve to start your application using the webpack dev server included with the Angular CLI, and then open your browser’s developer tools. In this example I have am going to access this Angular component:

@Component({
  selector: 'swr-person-search-form',
  template: `
    <ng-form #personForm="ngForm">
      <mat-form-field>
        <mat-label>Search</mat-label>
        <input matInput [(ngModel)]="q" name="q" />
      </mat-form-field>
    </ng-form>
  `,
})
export class PersonSearchFormComponent implements OnDestroy, OnInit {
  @ViewChild('personForm', { static: true }) personForm: NgForm;

  // omitted for brevity
}

In the code above I am using the @ViewChild() to query for the template reference variable personForm, which is an instance of NgForm. Note, I am using template driven forms in this example (though I often much prefer reactive forms).

We can then query for the <swr-person-search-form> element in the DOM, and then use the window.ng.getComponent() method to get the Angular component instance all within our browser’s console (as part of your dev tools):

let el = $('swr-person-search-form');
let c = ng.getComponent(el);
c.personForm.controls.q.setValue('Luke Skywalker');

Let’s break this down:

The ng.getComponent() exposes a powerful method for debugging in the console.

Here are just a few scenarios where console debugging may help:

With that said, and while I do think this is going to be very helpful for Anguler developers, this does not replace breakpoint style debugging either in the browser’s devtools or via an editor like VS Code.

ng.markDirty()

The last function that I want to introduce to you is the window.ng.markDirty() function. This function is important when debugging using the console because it triggers change detection for the specified component.

In our previous example we invoked the setValue() method on the FormControl in the PersonSearchFormComponent. If you play around with this code, or something similar, you’ll notice that the value is indeed set for the FormControl, but nothing happens as result of this. In my example I am expecting the list of people to be filtered based on the value supplied.

Using the window.ng.markAsDirty() function we can specify a component instance that we want to trigger change detection on:

let el = $('swr-person-search-form');
let c = ng.getComponent(el);
c.personForm.controls.q.setValue('Luke Skywalker');
ng.markDirty(c);

ng.getDirective()

The window.ng.getDirectives() function returns an array of Angular directives that are applied to an element in the DOM.

Using this, we can not only determine what directives are being applied to the element, but we can also access a specific directive instance. Let’s look at an example.

I have a directive declared in my Angular project called ContentDirective:

@Directive({
  selector: '[swrContent]',
})
export class ContentDirective implements AfterContentInit {
  /** The directive native element. */
  private el: HTMLElement;

  constructor(
    private readonly elementRef: ElementRef,
    @Inject(PLATFORM_ID) private readonly platformId: Object,
    private readonly renderer: Renderer2,
  ) {}

  ngAfterContentInit() {
    if (!isPlatformBrowser(this.platformId)) {
      return;
    }

    this.el = this.elementRef.nativeElement as HTMLElement;

    // omitted for brevity
  }
}

This directive is responsible for appropriately displaying content within the application.

In this next example I’m specifically using Google Chrome, which has helpful utilities available in the console including a special variable $0, which is the currently selected element in the Elements panel. I can reference this DOM element in the console, and even provide the element to ng.getDirectives() function that is expecting a DOM element:

ng.getDirectives($0);

This returns all of the directives applied to the currently selected DOM element $0. In my example, this is the ContentDirective that I mentioned previously.

Here is a screenshot showing the result of the getDirectives() function:

Console debugging with getDirectives() function

I can now programmatically manipulate the directive instance running in the browser in my console.

ng.getListeners()

The window.ng.getListeners() function returns an array of DebugEventListener objects. Using the DebugEventListener object you can examine the name of the event that triggers each listener.

I didn’t find much use for this particular function, so if you are aware of a use case, let me know in the comments below.

ng.getHostElement()

The window.ng.getHostElement() function accepts a directive (remember a component is a directive with a template) and returns the native host element.

Let’s look at an example:

let c = ng.getComponent($('swr-people-list'));
let el = ng.getHostElement(c);
inspect(el);

In this example we:

ng.getViewComponent()

As the documentation indicates:

The ng.getViewComponent() function returns the component instance associated with the view that owns the DOM element.

In this next example I am using a lazy-louted route, which renders an IndexComponent. The component is rendered as the sibling to the <router-outlet> element that indicates the view in which to render the route. You might have also noticed that this outlet is represented in Angular using a special <ng-component> custom element.

Using the ng.getViewComponent() we can obtain the parent view of an element:

let el = $('#userBtn');
let c = ng.getViewComponent(el);
let host = ng.getHostElement(c);

Here we are using the ng.getViewComponent() function to get the component instance for the view associated with the #userBtn element. We can leverage the component instance as necessary, or we can also use the ng.getHostElement() function to get the native host element in the DOM for the view component.

Conclusion

Angular 9 with the new Ivy compilation and rendering pipeline provides several useful globally available functions that we can leverage for debugging our Angular applications that are executing in the browser.