Brian F Love
Learn from a Google Developer Expert focused on Angular, Web Technologies, and Node.js from Portland, OR.
Ad·ultimatecourses.com
Learn Angular the right way with Ultimate Courses

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:

  • Angular v9 (or greater)
  • Ivy enabled project
  • Development mode

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:

  • First, I get access to the HTMLElement in the DOM via Chrome's $(selector) utility function and set the result to the variable el.
  • Then, we specify the DOM element as the first (and only) argument to the globally available window.ng.getComponent() function. This returns the component instance.
  • With the component instance we can now access the personForm property in the object!
  • Using the FormsModule provided by Angular we invoke the setValue() method on the FormControl instance to the value "Luke Skywalker".
  • Finally, we trigger change detection by invoking the window.ng.markDirty() function, specifying the component instance that we want to mark as dirty.

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

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

  • Access an injected service and invoke that service manually to examine the result.
  • Subscribe to an observable stream of data and log the value to the console.
  • Manually invoke a method in your component in order to determine the outcome or the return value.

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:

  • Query the DOM for an Angular component using it's selector swr-people-list.
  • Then, using the ng.getHostElement() function we obtain the HTMLElement instance.
  • Finally, we can use Chrome's inspect() utility function to inspect the element in the Elements panel.

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.

Brian F Love

Hi, I'm Brian. I am interested in TypeScript, Angular and Node.js. I'm married to my best friend Bonnie, I live in Portland and I ski (a lot).