Component Queries Metadata Appears To Be Broken When The Ivy Renderer Is Enabled In Angular 9.0.0-rc.2
UPDATE - 2019-11-17: The @Component() .queries metadata appears to work if the --prod flag is enabled. As such, this works for a production build; but, it doesn't work in the development server. So, perhaps this is actually a bug in the dev server, not in Ivy?
The other day, I thought I was taking crazy pills! No matter what I did, I couldn't get my ViewChild Component template query to work in Angular 9.0.0-rc.2 if the query was defined in the @Component() decorator. However, if I used a @ViewChild() property decorator instead, it worked. Seeing as I rather dislike property decorators, this predicament was quite off-putting. After some trial-and-error, I finally figured out that the @Component() decorator queries only failed if the Ivy rendered was enabled.
To see this in action, consider this App component that uses both forms of ViewChild query annotations:
// Import the core angular services.
import { Component } from "@angular/core";
import { ElementRef } from "@angular/core";
import { ViewChild } from "@angular/core";
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
@Component({
selector: "app-root",
queries: {
divRefOne: new ViewChild( "divRef" )
},
styleUrls: [ "./app.component.less" ],
template:
`
<div #divRef>
As you wish.....
</div>
`
})
export class AppComponent {
// Injected via @Component.queries.
public divRefOne!: ElementRef;
@ViewChild( "divRef" )
public divRefTwo!: ElementRef;
// ---
// PUBLIC METHODS.
// ---
// I get called once after view bindings have been initialized.
public ngAfterViewInit() : void {
console.group( "DivRefOne : @Component( .queries )" );
console.log( this.divRefOne );
console.groupEnd();
console.group( "DivRefTwo : @ViewChild()" );
console.log( this.divRefTwo );
console.groupEnd();
}
}
As you can see, divRefOne defines the query in the @Component() .queries metadata while divRefTwo defines the query using the @ViewChild() property decorator.
Now, if we run this with Ivy enabled in Angular 9.0.0-rc.2, we get the following output:
As you can see, the ElementRef for the @Component() query is undefined, even after the View Template has been initialized.
Now, if we run this with Ivy disabled in Angular 9.0.0-rc.2, we get the following output:
As you can see, the ElementRef for the @Component() query is valid. And, the only change that we made to this version of the Angular application is that we disabled Ivy.
ASIDE: I know that there were recent changes to the way the
ViewChild()annotation can be defined. However, this same behavior is also exhibited withViewChildren(), which has not had any recent changes.
Unless someone can explain why this is happening, this appears to be a bug in the way the Ivy renderer works in Angular 9.0.0-rc.2. I can't find any open Issues relating to this in the Angular GitHub, so I will try to open one shortly.
Epilogue On @Component() metadata.
To me, the "metadata" for a Component is separate from the logic of the Component. As such, I find it confusing to have metadata and decorators scattered throughout the component definition. Instead, I love to see all of the metadata gathered neatly at the top (in the @Component() decorator) so that I don't have to search for it. This way, when I want to see "How the component works", I look at the logic; and, if I want to see "How Angular interacts with the component", I look at the top in the @Component() decorator. These are two different concerns and - in my opinion - should be somewhat isolated.
This is just my opinion. Your mileage may vary.
Want to use code from this post? Check out the license.
Reader Comments
@All,
To my surprise, my demo actually worked when I pushed it to GitHub pages. The only difference there is that I'm using the
--prodflag when doing the build (as opposed to using the dev-server locally). I've added a note to the top of the blog post. So, perhaps this is a bug in the way Ivy interacts with the dev-server? This is clearly at a much lower-level than I can understand. I don't really even know how the dev-server is working.@All,
For completeness, here's my
package.json(currently installed Angular 9.0.0-rc.2:And, here's my
tsconfig.json:@All,
I've created an isolated repository in case anyone else wants reproduce:
https://github.com/bennadel/Component-Queries-Ivy-Bug-Angular9
@All,
It looks like it was a problem with my
angular.jsonconfiguration. For reasons I don't fully understand (read: not at all), it seems that it was breaking because I didn't have theaot(ahead of time) compiling setting in the base build configuration. Jeffrey Bosch figured it out, and I was able to get my proof-of-concept repo working.@All,
So, I just installed a fresh app on Angular 9.0.1 stable, and the same problem still exists.
ViewChildrenjust seems to be broken until you move theaotoption to the main options, not thebuildoptions. I don't get it.I'm having the same problem with unit tests. It works when I use decorators but not when I use the
queriescomponent metadata property. I'm guessing this is due to the component mocks being created dynamically so they can't benefit from AoT compilation on the first pass.@StupidAwesome,
Sorry you're running into this also; but, at the same time, I'm glad I'm not the only one.