Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at Scotch On The Rock (SOTR) 2010 (London) with: RichardCooper
Ben Nadel at Scotch On The Rock (SOTR) 2010 (London) with: RichardCooper ( @seopo )

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

By Ben Nadel 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.




Reader Comments

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.