Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at RIA Unleashed (Nov. 2010) with: Bob Silverberg and Carol Loffelmann and Vicky Ryder and Simon Free
Ben Nadel at RIA Unleashed (Nov. 2010) with: Bob Silverberg@elegant_chaos ) , Carol Loffelmann ( @Mommy_md ) , Vicky Ryder@fuzie ) , and Simon Free@simonfree )

Querying For Multiple ViewChild Directives On A Single Element In Angular 2 Beta 17

By Ben Nadel on

Yesterday, I took a look at exporting component and directive references in Angular 2. But, as I was playing around with that code, I realized that it was more complicated than it needed to be. Instead of exporting and querying for references, I could instead query for multiple ViewChild types on a single element in the component view. This approach has less dependencies and I thought was worth a quick follow-up post.


 
 
 

 
 
 
 
 

Run this demo in my JavaScript Demos project on GitHub.

As we saw yesterday, when you query the View (shadow DOM) for references (ie, #var attributes), the directives that you're targeting have to export an explicit reference name (or rely on the implicit reference exposed only by Component directives). If, however, we query for "types," rather than references, the target directives don't have to do anything. And, what's more, we can query for multiple directive types on the same element within the view.

To demonstrate, I've refactored my demo from yesterday to use injected ViewChild queries that are type-driven, not reference-driven. In the following code, notice that the root component view only houses one element with no exposed references. And yet, I'm querying for three different types:

  • <!doctype html>
  • <html>
  • <head>
  • <meta charset="utf-8" />
  •  
  • <title>
  • Querying For Multiple ViewChild Directives On A Single Element In Angular 2 Beta 17
  • </title>
  •  
  • <link rel="stylesheet" type="text/css" href="./demo.css"></lin>
  • </head>
  • <body>
  •  
  • <h1>
  • Querying For Multiple ViewChild Directives On A Single Element In Angular 2 Beta 17
  • </h1>
  •  
  • <my-app>
  • Loading...
  • </my-app>
  •  
  • <!-- Load demo scripts. -->
  • <script type="text/javascript" src="../../vendor/angularjs-2-beta/17/es6-shim.min.js"></script>
  • <script type="text/javascript" src="../../vendor/angularjs-2-beta/17/Rx.umd.min.js"></script>
  • <script type="text/javascript" src="../../vendor/angularjs-2-beta/17/angular2-polyfills.min.js"></script>
  • <script type="text/javascript" src="../../vendor/angularjs-2-beta/17/angular2-all.umd.js"></script>
  • <!-- AlmondJS - minimal implementation of RequireJS. -->
  • <script type="text/javascript" src="../../vendor/angularjs-2-beta/17/almond.js"></script>
  • <script type="text/javascript">
  •  
  • // Defer bootstrapping until all of the components have been declared.
  • requirejs(
  • [ /* Using require() for better readability. */ ],
  • function run() {
  •  
  • ng.platform.browser.bootstrap( require( "App" ) );
  •  
  • }
  • );
  •  
  •  
  • // --------------------------------------------------------------------------- //
  • // --------------------------------------------------------------------------- //
  •  
  •  
  • // I provide the root application component.
  • define(
  • "App",
  • function registerApp() {
  •  
  • // Configure the App component definition.
  • ng.core
  • .Component({
  • selector: "my-app",
  • directives: [
  • require( "ComponentA" ),
  • require( "DirectiveB" ),
  • require( "DirectiveC" )
  • ],
  •  
  • // In this version of the demo, rather than querying for "refs",
  • // we're querying for instances of the given directive types.
  • // --
  • // NOTE: All three of these directives select on "my-component".
  • queries: {
  • qTestA: new ng.core.ViewChild( require( "ComponentA" ) ),
  • qTestB: new ng.core.ViewChild( require( "DirectiveB" ) ),
  • qTestC: new ng.core.ViewChild( require( "DirectiveC" ) )
  • },
  • template:
  • `
  • <my-component></my-component>
  • `
  • })
  • .Class({
  • constructor: AppController,
  •  
  • // Define the life-cycle methods on the prototype so that
  • // Angular will pick them up at runtime.
  • ngAfterViewInit: function noop() {}
  • })
  • ;
  •  
  • return( AppController );
  •  
  •  
  • // I control the App component.
  • function AppController() {
  •  
  • var vm = this;
  •  
  • // Expose the public methods.
  • vm.ngAfterViewInit = ngAfterViewInit;
  •  
  •  
  • // ---
  • // PUBLIC METHODS.
  • // ---
  •  
  •  
  • // I get called once after the view is initialized / checked for the
  • // first time. At this point, all of the DOM-queries have been linked.
  • function ngAfterViewInit() {
  •  
  • console.group( "Injected ViewChild Queries" );
  • console.log( "qTestA:", vm.qTestA );
  • console.log( "qTestB:", vm.qTestB );
  • console.log( "qTestC:", vm.qTestC );
  • console.groupEnd();
  •  
  • }
  •  
  • }
  •  
  • }
  • );
  •  
  •  
  • // --------------------------------------------------------------------------- //
  • // --------------------------------------------------------------------------- //
  •  
  •  
  • // I provide a component that selects on "my-component".
  • define(
  • "ComponentA",
  • function registerComponentA() {
  •  
  • // Configure the ComponentA component definition.
  • return ng.core
  • .Component({
  • selector: "my-component",
  • template:
  • `
  • <div>
  • Such component! Much Directive!
  • </div>
  • `
  • })
  • .Class({
  • constructor: function ComponentAController() {
  •  
  • this.name = "ComA";
  •  
  • }
  • })
  • ;
  •  
  • }
  • );
  •  
  •  
  • // --------------------------------------------------------------------------- //
  • // --------------------------------------------------------------------------- //
  •  
  •  
  • // I provide a directive that selects on "my-component".
  • define(
  • "DirectiveB",
  • function registerDirectiveB() {
  •  
  • // Configure the DirectiveB directive definition.
  • return ng.core
  • .Directive({
  • selector: "my-component"
  • })
  • .Class({
  • constructor: function DirectiveBController() {
  •  
  • this.name = "DirB";
  •  
  • }
  • })
  • ;
  •  
  • }
  • );
  •  
  •  
  • // --------------------------------------------------------------------------- //
  • // --------------------------------------------------------------------------- //
  •  
  •  
  • // I provide a directive that selects on "my-component".
  • define(
  • "DirectiveC",
  • function registerDirectiveC() {
  •  
  • // Configure the DirectiveC directive definition.
  • return ng.core
  • .Directive({
  • selector: "my-component"
  • })
  • .Class({
  • constructor: function DirectiveCController() {
  •  
  • this.name = "DirC";
  •  
  • }
  • })
  • ;
  •  
  • }
  • );
  •  
  • </script>
  •  
  • </body>
  • </html>

As you can see, the component and directives in this demo aren't exporting anything - they're just existing. And yet, when our root component queries for the ViewChild'ren based on type, we're able to locate and extract all three of our directive controller instances on the same "my-component" element:


 
 
 

 
 ViewChild query for multiple directive instances in Anuglar 2 Beta 17. 
 
 
 

The reference-based queries in Angular 2 Beta 17 makes a lot of sense if you actually need to reference the directive instances within the View. But, if all you need to do is inject the references into the controller, I think the type-based queries make the most sense. Plus, this approach requires less coordination between the directives as we're not dependent on the target directive to export reference names.




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.