Skip to main content
Ben Nadel at cf.Objective() 2010 (Minneapolis, MN) with: Pat Santora
Ben Nadel at cf.Objective() 2010 (Minneapolis, MN) with: Pat Santora ( @patweb99 )

Routable View Components Don't Need Selectors In Angular 4.4.4 - But They May Be Helpful

By on

For the the most part, Angular Directives need to provide a "selector" in their @Component() or @Directive() meta-data definitions. This allows Angular to locate and associate HTML entities with the appropriate directives when parsing templates. With routable components, however - those that are injected into a template by the Router and the router-outlet directives - selectors aren't required. That said, they may be helpful in debugging nested view-rendering logic.

Run this demo in my JavaScript Demos project on GitHub.

Ultimately, when a routable component is injected into a parent view, the Angular Router has to inject it as a piece of markup - it has to be come part of the rendered DOM (Document Object Model). If the routable component has a selector, Angular will use the selector to create an Element node. If the routable component omits a selector, Angular will use "ng-component" as the injected Element tag name.

To see this in action, I've created a simple Angular application that contains a single router-outlet that can switch between two view components: AViewComponent and BViewComponent. Since the parent view is somewhat irrelevant for this exploration, I'm going to skip over the application module and the root component and proceed directly to our routable views. First, let's look at AViewComponent:

// Import the core angular services.
import { Component } from "@angular/core";

// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //

@Component({
	// NOTE: Since routable views aren't embedded in template logic, we don't need to
	// provide a SELECTOR - Angular will inject this component automatically as part
	// of the router-outlet logic (using the "ng-component" tag).
	styleUrls: [ "./a-view.component.css" ],
	template:
	`
		<h3>
			A-View Component
		</h3>

		<p>
			This is the a-view component, noice!
		</p>
	`
})
export class AViewComponent {
	// ...
}

As you can see, in this view's @Component() meta-data, there is no "selector" property. With routable components, this isn't required. But, it can be used. And, we are going to use it in the BViewComponent:

// Import the core angular services.
import { Component } from "@angular/core";

// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //

@Component({
	// For this routable component, we're going to include a SELECTOR even though it
	// isn't strictly necessary (so that we can see how the view output is affected).
	selector: "b-view",
	styleUrls: [ "./b-view.component.css" ],
	template:
	`
		<h3>
			B-View Component
		</h3>

		<p>
			This is the b-view component, sweet!
		</p>
	`
})
export class BViewComponent {
	// ...
}

As you can see, in this view's @Component() meta-data, we're using the selector value of "b-view".

Now, if we run this application in the browser and navigate to the AViewComponent, we get the following Document Object Model rendering:

Selectors aren't required in routable components in Angular 4.

As you can see, the AViewComponent is being injected into the DOM using the "ng-component" element tag name. And, if we navigate to the BViewComponent, we get the following Document Object Model rendering:

Selectors can be used in routable components, affecting the element tag name.

This time, the selector provided by the component - "b-view" - is being used when Angular injects the component into the DOM.

Internally to the routable view component, these two outcomes are essentially the same. In both cases, the view component will render; and, in both cases, the view component's CSS will successfully be applied - ":host" isn't coupled to the element tag name).

But, providing a selector is helpful for the human factor. It's helpful when debugging a routable application because a tree of "ng-content" elements isn't exactly intuitive when you're inspecting elements in the Chrome Developer Tools. And, providing a selector may also make it easier to override styles in the parent component. You could attach styles to the "ng-component" selector; however, given the fact that a parent component may contain multiple router-outlet elements (for named and unnamed views), providing a unique local handle would make CSS selectors a bit more intuitive.

Now, to be clear, I'm not advocating that all view components provide selectors. To be honest, I'm on the fence about it. Part of me loves the fact that I can omit data that isn't strictly necessary for the routable component tree; but, I do see value in having the selectors present, especially for debugging (and I've been doing a lot of debugging lately with the Router in Angular 4). I suppose the best take-away here is that we have the flexibility to do what adds the most value in the given context.

Want to use code from this post? Check out the license.

Reader Comments

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel