Skip to main content
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Pierre-Olivier Chassay
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Pierre-Olivier Chassay ( @moonpo )

Directive Conflict When Classes Export The Same Name In Angular 2 Beta 6

By on

The other day, when I was exploring the mixed-depth behavior of provider and directive arrays, I ran into a rather strange problem. When I created a number of test directives in a loop, only the last one was actually being applied to the consuming context. Eventually, I narrowed it down to the fact that each directive was exporting a class with the same name (since they were being defied within a loop). Despite the fact that each class was a different physical reference, with a unique object identity, the fact that they happened to have the same name was causing some sort of malfunction.

Run this demo in my JavaScript Demos project on GitHub.

To demonstrate this, all we have to do is create two directives that select against different elements but export a class with the same name. In the following code, I am doing this with two directives that select against "p" and "section", respectively. Each directive logs its own class instantiation; so, we would expect to see each class logged successfully:

<!doctype html>
<html>
<head>
	<meta charset="utf-8" />

	<title>
		Directive Conflict When Classes Export The Same Name In Angular 2 Beta 6
	</title>

	<link rel="stylesheet" type="text/css" href="./demo.css"></link>
</head>
<body>

	<h1>
		Directive Conflict When Classes Export The Same Name In Angular 2 Beta 6
	</h1>

	<my-app>
		Loading...
	</my-app>

	<!-- Load demo scripts. -->
	<script type="text/javascript" src="../../vendor/angularjs-2-beta/6/es6-shim.min.js"></script>
	<script type="text/javascript" src="../../vendor/angularjs-2-beta/6/Rx.umd.min.js"></script>
	<script type="text/javascript" src="../../vendor/angularjs-2-beta/6/angular2-polyfills.min.js"></script>
	<script type="text/javascript" src="../../vendor/angularjs-2-beta/6/angular2-all.umd.js"></script>
	<!-- AlmondJS - minimal implementation of RequireJS. -->
	<script type="text/javascript" src="../../vendor/angularjs-2-beta/6/almond.js"></script>
	<script type="text/javascript">

		// Defer bootstrapping until all of the components have been declared.
		// --
		// NOTE: Not all components have to be required here since they will be
		// implicitly required by other components.
		requirejs(
			[ "AppComponent" ],
			function run( AppComponent ) {

				ng.platform.browser.bootstrap( AppComponent );

			}
		);


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


		// I provide the root App component.
		define(
			"AppComponent",
			function registerAppComponent() {

				var PTag = require( "PTag" );
				var SectionTag = require( "SectionTag" );

				// Confirm that these two directives do, in fact, have different and
				// unique object identities.
				console.log( "( PTag === SectionTag ):", ( PTag === SectionTag ) );

				// Configure the App component definition.
				ng.core
					.Component({
						selector: "my-app",
						directives: [ PTag, SectionTag ],
						template:
						`
							<p>
								This is a P-tag. Woot!
							</p>

							<section>
								<strong>Note:</strong> This works "as expected" in
								Angular 2 Beta 1, but not Beta 3 or Beta 6.
							</section>
						`
					})
					.Class({
						constructor: AppController
					})
				;

				return( AppController );


				// I control the App component.
				function AppController() {

					// Nothing to do here.

				}

			}
		);


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


		// I provide a directive that selects against P tags and logs its own creation.
		define(
			"PTag",
			function registerPTag() {

				// Configure the PTag directive definition.
				ng.core
					.Directive({
						selector: "p" // <----- UNIQUE SELECTOR.
					})
					.Class({
						constructor: Pay_Attention_To_The_Name_Of_This_Controller
					})
				;

				return( Pay_Attention_To_The_Name_Of_This_Controller );


				// I control the PTag directive.
				// --
				// CAUTION: This function just so happens to have the same NAME as
				// another function that may also be applied to the same view context.
				// If this happens, it looks like Angular 2 Beta 6 (and Beta 3) gets
				// confused about which directive to use. Keep in mind, this function
				// is a totally UNIQUE REFERENCE and has a UNIQUE OBJECT IDENTITY
				// despite coincidentally having the same name.
				function Pay_Attention_To_The_Name_Of_This_Controller() {

					console.log( "PTag constructed." );

				}

			}
		);


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


		// I provide a directive that selects against SECTION tags and logs its own creation.
		define(
			"SectionTag",
			function registerSectionTag() {

				// Configure the SectionTag directive definition.
				ng.core
					.Directive({
						selector: "section" // <----- UNIQUE SELECTOR.
					})
					.Class({
						constructor: Pay_Attention_To_The_Name_Of_This_Controller
					})
				;

				return( Pay_Attention_To_The_Name_Of_This_Controller );


				// I control the SectionTag directive.
				// --
				// CAUTION: This function just so happens to have the same NAME as
				// another function that may also be applied to the same view context.
				// If this happens, it looks like Angular 2 Beta 6 (and Beta 3) gets
				// confused about which directive to use. Keep in mind, this function
				// is a totally UNIQUE REFERENCE and has a UNIQUE OBJECT IDENTITY
				// despite coincidentally having the same name.
				function Pay_Attention_To_The_Name_Of_This_Controller() {

					console.log( "SectionTag constructed." );

				}

			}
		);

	</script>

</body>
</html>

As you can see, each class is a unique reference but just so happens to use the name:

Pay_Attention_To_The_Name_Of_This_Controller

And, when we run this code, we get the following page output:

Directives conflict when two directives with the same exported class name are used in the same view in Angular 2 Beta 6.

As you can see, only the last directive was used and applied to the view.

I should mention that this appears to work, as expected, in Beta 1. And, if you define an anonymous class constructor in the .Class() decorator, it also works as expected, even in Beta 6. As such, it doesn't appear that Angular 2 needs to be dependent upon the name of the class. All said and done, though, this is clearly a bug.

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